Cogs.Core
RenderingStatsInspector.cpp
1#include <cinttypes>
2#include <array>
3
4#include "Inspectors.h"
5#include "InspectorGuiHelper.h"
6#include "Services/Variables.h"
7#include "Services/QualityService.h"
8
9#include "Rendering/IGraphicsDevice.h"
10#include "Rendering/IContext.h"
11#include "Rendering/Statistics.h"
12
13#include "Context.h"
14
15
16namespace {
17
18 std::string byteString(size_t bytes)
19 {
20 if (bytes < 16 * 1024) {
21 return std::to_string(bytes) + " B";
22 }
23 bytes = bytes / 1024;
24 if (bytes < 16 * 1024) {
25 return std::to_string(bytes) + " KiB";
26 }
27 bytes = bytes / 1024;
28 if (bytes < 16 * 1024) {
29 return std::to_string(bytes) + " MiB";
30 }
31 bytes = bytes / 1024;
32 return std::to_string(bytes) + " GiB";
33 }
34
35 template<typename T>
36 std::string numberString(T n)
37 {
38 if (n == ~T(0)) { return "n/a"; }
39
40 if (n < 10 * 1000) {
41 return std::to_string(n);
42 }
43 n = n / 1000;
44 if (n < 4 * 1000) {
45 return std::to_string(n) + " k";
46 }
47 n = n / 1000;
48 if (n < 10 * 1000) {
49 return std::to_string(n) + " M";
50 }
51 n = n / 1000;
52 return std::to_string(n) + " G";
53 }
54
55}
56
57
58void Cogs::Core::renderingStatsInspector(class Context* context, bool* show)
59{
60 IContext* deviceContext = context->device->getImmediateContext();
61 if (*show) {
62 QualityService* quality = context->qualityService.get();
63
64 deviceContext->frameStatisticsConfigure(true);
65
66 guiBegin("Performance statistics", show);
67 if (ImGui::CollapsingHeader("Draw calls", ImGuiTreeNodeFlags_::ImGuiTreeNodeFlags_DefaultOpen)) {
68 const Cogs::FrameStatistics& frameStats = context->device->getImmediateContext()->getLastFrameStatistics();
69 const uint32_t* h = frameStats.drawCallHistogram;
70
71 std::array<std::string,12> labelTexts{
72 "0", // 0
73 "4", // 4 = 2^2
74 "16", // 16 = 2^4
75 "64", // 64 = 2^6
76 "256", // 128 = 2^8
77 "1k", // 1024 = 2^10
78 "4k", // 4096 = 2^12
79 "16k", // 16384 = 2^14
80 "65k", // 65536 = 2^16
81 "262k", // 262144 = 2^18
82 "1M", // 1048576 = 2^20
83 "4G", // 4294967296 = 2^32
84 };
85
86 std::array<uint32_t, labelTexts.size()-1> values{
87 h[0] + h[1], // 0 ... 2^2
88 h[2] + h[3], // 2^2 ... 2^4
89 h[4] + h[5], // 2^4 ... 2^6
90 h[6] + h[7], // 2^6 ... 2^8
91 h[8] + h[9], // 2^8 ... 2^10
92 h[10] + h[11], // 2^10 ... 2^12
93 h[12] + h[13], // 2^12 ... 2^14
94 h[14] + h[15], // 2^14 ... 2^16
95 h[16] + h[17], // 2^16 ... 2^18
96 h[18] + h[19], // 2^18 ... 2^20
97 h[20] + h[21] + h[22] + h[23] + // 2^20 ... 2^32
98 h[24] + h[25] + h[26] + h[27] +
99 h[28] + h[29] + h[30] + h[31]
100 };
101
102 uint32_t drawCalls = 0;
103 uint32_t maxValue = 1;
104 ImVec2 maxTextSize(40, 0);
105 std::array<std::string, values.size()> valueTexts;
106 std::array<ImVec2, values.size()> valueSizes;
107 std::array<ImVec2, labelTexts.size()> labelSizes;
108
109 for (size_t i = 0; i < values.size(); i++) {
110 drawCalls += values[i];
111 maxValue = std::max(maxValue, values[i]);
112 valueTexts[i] = numberString(values[i]);
113 valueSizes[i] = ImGui::CalcTextSize(valueTexts[i].c_str());
114 maxTextSize.x = std::max(maxTextSize.x, valueSizes[i].x);
115 maxTextSize.y = std::max(maxTextSize.y, valueSizes[i].y);
116 }
117 for (size_t i = 0; i < labelTexts.size(); i++) {
118 labelSizes[i] = ImGui::CalcTextSize(labelTexts[i].c_str());
119 maxTextSize.x = std::max(maxTextSize.x, labelSizes[i].x);
120 maxTextSize.y = std::max(maxTextSize.y, labelSizes[i].y);
121 }
122
123 const float height = 100.f;
124 const float spacing = 2.f;
125 ImGuiStyle& style = ImGui::GetStyle();
126 const ImU32 textCol = ImColor(style.Colors[ImGuiCol_Text]);
127 const ImU32 graphCol = ImColor(style.Colors[ImGuiCol_PlotLines]);
128 const ImVec2 p = ImGui::GetCursorScreenPos();
129 ImDrawList* draw_list = ImGui::GetWindowDrawList();
130
131 // Histogram bars
132 for (size_t i = 0; i < values.size(); i++) {
133 float v = static_cast<float>(values[i]) / static_cast<float>(maxValue);
134 float x = p.x + i * maxTextSize.x + spacing;
135 float y = p.y;
136
137 draw_list->AddRect(ImVec2(x + spacing,
138 maxTextSize.y + y + (1.f - v) * height),
139 ImVec2(x + maxTextSize.x - spacing,
140 maxTextSize.y + y + height),
141 graphCol, 0.0f);
142
143 }
144
145 // Value text on top of histogram bars
146 for (size_t i = 0; i < valueTexts.size(); i++) {
147 float v = static_cast<float>(values[i]) / static_cast<float>(maxValue);
148 float x = p.x + spacing + i * maxTextSize.x + 0.5f * (maxTextSize.x - valueSizes[i].x);
149 float y = p.y + spacing + (1.f - v) * height;
150 draw_list->AddText(ImVec2(x, y), textCol, valueTexts[i].c_str());
151 }
152
153 // Axis values
154 for (size_t i = 0; i < labelTexts.size(); i++) {
155 float x = p.x + spacing + std::min(std::max((i - 0.5f) * maxTextSize.x + 0.5f * (maxTextSize.x - labelSizes[i].x),
156 spacing),
157 (labelTexts.size()-1) * maxTextSize.x - labelSizes[i].x - spacing);
158 float y = p.y + spacing + maxTextSize.y + height;
159 draw_list->AddText(ImVec2(x, y), textCol, labelTexts[i].c_str());
160 }
161
162 // Frame
163 ImVec2 q(values.size() * maxTextSize.x + 2.f * spacing, 2.f * spacing + height + 2.f * maxTextSize.y);
164 draw_list->AddRect(p, ImVec2(p.x + q.x, p.y + q.y), graphCol, 0.0f);
165
166 // Reserve space
167 ImGui::Dummy(q);
168
169 std::string t;
170 ImGui::Columns(3);
171 ImGui::Text("Draw calls submitted");
172 ImGui::NextColumn();
173 t = numberString(drawCalls);
174 ImGui::TextUnformatted(t.c_str());
175 ImGui::NextColumn();
176 ImGui::NextColumn();
177
178 ImGui::Text("Vertices submitted");
179 ImGui::NextColumn();
180 t = numberString(frameStats.vertices);
181 ImGui::TextUnformatted(t.c_str());
182 ImGui::NextColumn();
183 ImGui::NextColumn();
184
185 ImGui::Text("Percentage indexed");
186 ImGui::NextColumn();
187 ImGui::Text("%.1f%%", (100.f * frameStats.indices) / frameStats.vertices);
188 ImGui::NextColumn();
189 ImGui::NextColumn();
190
191 ImGui::Text("Average submission size");
192 ImGui::NextColumn();
193 t = numberString(drawCalls == 0 ? 0 : static_cast<size_t>(static_cast<float>(frameStats.vertices) / drawCalls));
194 ImGui::TextUnformatted(t.c_str());
195 ImGui::NextColumn();
196 ImGui::NextColumn();
197
198 ImGui::Text("Frame time:");
199 ImGui::NextColumn();
200 ImGui::Text("%.2fms (%.1f FPS)",
201 quality->avgFrameTime,
202 1000.f/ quality->avgFrameTime);
203 ImGui::NextColumn();
204 {
205 float frameTime = std::max(0.f, context->variables->get("quality.frameTimeTarget", 0.f));
206 if (ImGui::InputFloat("##frametimetarget", &frameTime, 1.f, 10.f, "%.1f ms")) {
207 context->variables->set("quality.frameTimeTarget", std::max(0.f, frameTime));
208 }
209 }
210 ImGui::Columns(1);
211 }
212
213 if (ImGui::CollapsingHeader("GPU resources", ImGuiTreeNodeFlags_::ImGuiTreeNodeFlags_DefaultOpen)) {
214 const Cogs::ResourceStatistics resourceStats = context->device->getResourceStatistics();
215
216 std::string t;
217
218 ImGui::Columns(3);
219 ImGui::Text("GPU memory consumption");
220 ImGui::NextColumn();
221 t = byteString(resourceStats.memoryConsumption());
222 ImGui::TextUnformatted(t.c_str());
223 ImGui::NextColumn();
224 {
225 ImGui::Text("Limit:");
226 float GPUMBLimit = std::max(0.f, context->variables->get("quality.GPUMemTargetMB", 0.f));
227 if (ImGui::InputFloat("##gpumemtarget", &GPUMBLimit, 100.f, 250.f, "%.0f MB")) {
228 context->variables->set("quality.GPUMemTargetMB", std::max(0, int(GPUMBLimit)));
229 }
230 }
231
232 static size_t memoryPeak = 0;
233 static size_t bufferMemoryPeak = 0;
234 static size_t textureMemoryPeak = 0;
235 static size_t memoryAverage = resourceStats.memoryConsumption();
236 static size_t bufferMemoryAverage = resourceStats.bufferMemoryConsumption;
237 static size_t textureMemoryAverage = resourceStats.textureMemoryConsumption;
238 static size_t statsReadIteration = 1;
239 if((statsReadIteration + 1) == 0)
240 {
241 statsReadIteration = 1;
242 memoryAverage = resourceStats.memoryConsumption();
243 bufferMemoryAverage = resourceStats.bufferMemoryConsumption;
244 textureMemoryAverage = resourceStats.textureMemoryConsumption;
245 }
246
247 double res = 1.0 / statsReadIteration;
248 double termpart = static_cast<double>(statsReadIteration-1) * res;
249 memoryAverage = static_cast<size_t>(memoryAverage * termpart + static_cast<double>(resourceStats.memoryConsumption()) * res);
250 bufferMemoryAverage = static_cast<size_t>(bufferMemoryAverage * termpart + static_cast<double>(resourceStats.bufferMemoryConsumption) * res);
251 textureMemoryAverage = static_cast<size_t>(textureMemoryAverage * termpart + static_cast<double>(resourceStats.textureMemoryConsumption) * res);
252
253 // Combined texture + buffer memory consumption
254 ImGui::NextColumn();
255 ImGui::Text("\t- Peak");
256 ImGui::NextColumn();
257 memoryPeak = std::max(memoryPeak, resourceStats.memoryConsumption());
258 ImGui::TextUnformatted(byteString(memoryPeak).c_str());
259 ImGui::NextColumn();
260
261 if(ImGui::Button("Reset Peaks##mem")) {
262 statsReadIteration = 1;
263 memoryPeak = resourceStats.memoryConsumption();
264 bufferMemoryPeak = resourceStats.bufferMemoryConsumption;
265 textureMemoryPeak = resourceStats.textureMemoryConsumption;
266 }
267
268 ImGui::NextColumn();
269 ImGui::Text("\t- Average");
270 ImGui::NextColumn();
271 ImGui::TextUnformatted(byteString(memoryAverage).c_str());
272 ImGui::NextColumn();
273 ImGui::NextColumn();
274
275 ImGui::Text("Buffers");
276 ImGui::NextColumn();
277 ImGui::Text("%u (%s)", resourceStats.bufferCount, byteString(resourceStats.bufferMemoryConsumption).c_str());
278 ImGui::NextColumn();
279 ImGui::NextColumn();
280 ImGui::Text("\t- Peak");
281 ImGui::NextColumn();
282 bufferMemoryPeak = std::max(bufferMemoryPeak, resourceStats.bufferMemoryConsumption);
283 ImGui::TextUnformatted(byteString(bufferMemoryPeak).c_str());
284 ImGui::NextColumn();
285 ImGui::NextColumn();
286 ImGui::Text("\t- Average");
287 ImGui::NextColumn();
288 ImGui::TextUnformatted(byteString(bufferMemoryAverage).c_str());
289 ImGui::NextColumn();
290 ImGui::NextColumn();
291
292 ImGui::Text("Textures");
293 ImGui::NextColumn();
294 ImGui::Text("%u (%s)", resourceStats.textureCount, byteString(resourceStats.textureMemoryConsumption).c_str());
295 ImGui::NextColumn();
296 ImGui::NextColumn();
297 ImGui::Text("\t- Peak");
298 ImGui::NextColumn();
299 textureMemoryPeak = std::max(textureMemoryPeak, resourceStats.textureMemoryConsumption);
300 ImGui::TextUnformatted(byteString(textureMemoryPeak).c_str());
301 ImGui::NextColumn();
302 ImGui::NextColumn();
303 ImGui::Text("\t- Average");
304 ImGui::NextColumn();
305 ImGui::TextUnformatted(byteString(textureMemoryAverage).c_str());
306 ImGui::NextColumn();
307 ImGui::NextColumn();
308
309 ImGui::Text("Vertex array objects");
310 ImGui::NextColumn();
311 t = numberString(resourceStats.vertexArrayObjectCount);
312 ImGui::TextUnformatted(t.c_str());
313 ImGui::NextColumn();
314 ImGui::NextColumn();
315
316 ImGui::Text("Input layouts");
317 ImGui::NextColumn();
318 t = numberString(resourceStats.inputLayoutCount);
319 ImGui::TextUnformatted(t.c_str());
320 ImGui::NextColumn();
321 ImGui::NextColumn();
322
323 ImGui::Text("Effects");
324 ImGui::NextColumn();
325 t = numberString(resourceStats.effectCount);
326 ImGui::TextUnformatted(t.c_str());
327 ImGui::NextColumn();
328 ImGui::NextColumn();
329
330 ImGui::Text("Rendertargets");
331 ImGui::NextColumn();
332 t = numberString(resourceStats.rendertargetsCount);
333 ImGui::TextUnformatted(t.c_str());
334 ImGui::NextColumn();
335 ImGui::NextColumn();
336
337 ImGui::Text("Framebuffers");
338 ImGui::NextColumn();
339 t = numberString(resourceStats.framebufferCount);
340 ImGui::TextUnformatted(t.c_str());
341 ImGui::NextColumn();
342 ImGui::NextColumn();
343
344 ImGui::Text("Sampler states");
345 ImGui::NextColumn();
346 t = numberString(resourceStats.samplerStateCount);
347 ImGui::TextUnformatted(t.c_str());
348 ImGui::NextColumn();
349 ImGui::NextColumn();
350
351 ImGui::Text("Blend states");
352 ImGui::NextColumn();
353 t = numberString(resourceStats.blendStateCount);
354 ImGui::TextUnformatted(t.c_str());
355 ImGui::NextColumn();
356 ImGui::NextColumn();
357
358 ImGui::Text("Rasterizer states");
359 ImGui::NextColumn();
360 t = numberString(resourceStats.rasterizerStateCount);
361 ImGui::TextUnformatted(t.c_str());
362 ImGui::NextColumn();
363 ImGui::NextColumn();
364
365 ImGui::Text("Depth-stencil states");
366 ImGui::NextColumn();
367 t = numberString(resourceStats.depthStencilStateCount);
368 ImGui::TextUnformatted(t.c_str());
369 ImGui::NextColumn();
370 ImGui::NextColumn();
371
372 ImGui::Columns(1);
373 }
374
375 guiEnd();
376 }
377 else {
378 deviceContext->frameStatisticsConfigure(false);
379 }
380}
381