Statistics.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. // Copyright 2008 Dolphin Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "VideoCommon/Statistics.h"
  4. #include <cstring>
  5. #include <utility>
  6. #include <imgui.h>
  7. #include "Core/DolphinAnalytics.h"
  8. #include "Core/HW/SystemTimers.h"
  9. #include "Core/System.h"
  10. #include "VideoCommon/BPFunctions.h"
  11. #include "VideoCommon/VideoCommon.h"
  12. #include "VideoCommon/VideoConfig.h"
  13. #include "VideoCommon/VideoEvents.h"
  14. Statistics g_stats;
  15. static Common::EventHook s_before_frame_event =
  16. BeforeFrameEvent::Register([] { g_stats.ResetFrame(); }, "Statistics::ResetFrame");
  17. static Common::EventHook s_after_frame_event = AfterFrameEvent::Register(
  18. [](const Core::System& system) {
  19. DolphinAnalytics::Instance().ReportPerformanceInfo({
  20. .speed_ratio = system.GetSystemTimers().GetEstimatedEmulationPerformance(),
  21. .num_prims = g_stats.this_frame.num_prims + g_stats.this_frame.num_dl_prims,
  22. .num_draw_calls = g_stats.this_frame.num_draw_calls,
  23. });
  24. },
  25. "Statistics::PerformanceSample");
  26. static bool clear_scissors;
  27. void Statistics::ResetFrame()
  28. {
  29. this_frame = {};
  30. clear_scissors = true;
  31. if (scissors.size() > 1)
  32. {
  33. scissors.erase(scissors.begin(), scissors.end() - 1);
  34. }
  35. }
  36. void Statistics::SwapDL()
  37. {
  38. std::swap(this_frame.num_dl_prims, this_frame.num_prims);
  39. std::swap(this_frame.num_xf_loads_in_dl, this_frame.num_xf_loads);
  40. std::swap(this_frame.num_cp_loads_in_dl, this_frame.num_cp_loads);
  41. std::swap(this_frame.num_bp_loads_in_dl, this_frame.num_bp_loads);
  42. }
  43. void Statistics::Display() const
  44. {
  45. const float scale = ImGui::GetIO().DisplayFramebufferScale.x;
  46. ImGui::SetNextWindowPos(ImVec2(10.0f * scale, 10.0f * scale), ImGuiCond_FirstUseEver);
  47. ImGui::SetNextWindowSizeConstraints(ImVec2(275.0f * scale, 400.0f * scale),
  48. ImGui::GetIO().DisplaySize);
  49. if (!ImGui::Begin("Statistics", nullptr, ImGuiWindowFlags_NoNavInputs))
  50. {
  51. ImGui::End();
  52. return;
  53. }
  54. ImGui::Columns(2, "Statistics", true);
  55. const auto draw_statistic = [](const char* name, const char* format, auto&&... args) {
  56. ImGui::TextUnformatted(name);
  57. ImGui::NextColumn();
  58. ImGui::Text(format, std::forward<decltype(args)>(args)...);
  59. ImGui::NextColumn();
  60. };
  61. if (g_ActiveConfig.backend_info.api_type == APIType::Nothing)
  62. {
  63. draw_statistic("Objects", "%d", this_frame.num_drawn_objects);
  64. draw_statistic("Vertices Loaded", "%d", this_frame.num_vertices_loaded);
  65. draw_statistic("Triangles Input", "%d", this_frame.num_triangles_in);
  66. draw_statistic("Triangles Rejected", "%d", this_frame.num_triangles_rejected);
  67. draw_statistic("Triangles Culled", "%d", this_frame.num_triangles_culled);
  68. draw_statistic("Triangles Clipped", "%d", this_frame.num_triangles_clipped);
  69. draw_statistic("Triangles Drawn", "%d", this_frame.num_triangles_drawn);
  70. draw_statistic("Rasterized Pix", "%d", this_frame.rasterized_pixels);
  71. draw_statistic("TEV Pix In", "%d", this_frame.tev_pixels_in);
  72. draw_statistic("TEV Pix Out", "%d", this_frame.tev_pixels_out);
  73. }
  74. draw_statistic("Textures created", "%d", num_textures_created);
  75. draw_statistic("Textures uploaded", "%d", num_textures_uploaded);
  76. draw_statistic("Textures alive", "%d", num_textures_alive);
  77. draw_statistic("pshaders created", "%d", num_pixel_shaders_created);
  78. draw_statistic("pshaders alive", "%d", num_pixel_shaders_alive);
  79. draw_statistic("vshaders created", "%d", num_vertex_shaders_created);
  80. draw_statistic("vshaders alive", "%d", num_vertex_shaders_alive);
  81. draw_statistic("shaders changes", "%d", this_frame.num_shader_changes);
  82. draw_statistic("dlists called", "%d", this_frame.num_dlists_called);
  83. draw_statistic("Primitive joins", "%d", this_frame.num_primitive_joins);
  84. draw_statistic("Draw calls", "%d", this_frame.num_draw_calls);
  85. draw_statistic("Primitives", "%d", this_frame.num_prims);
  86. draw_statistic("Primitives (DL)", "%d", this_frame.num_dl_prims);
  87. draw_statistic("XF loads", "%d", this_frame.num_xf_loads);
  88. draw_statistic("XF loads (DL)", "%d", this_frame.num_xf_loads_in_dl);
  89. draw_statistic("CP loads", "%d", this_frame.num_cp_loads);
  90. draw_statistic("CP loads (DL)", "%d", this_frame.num_cp_loads_in_dl);
  91. draw_statistic("BP loads", "%d", this_frame.num_bp_loads);
  92. draw_statistic("BP loads (DL)", "%d", this_frame.num_bp_loads_in_dl);
  93. draw_statistic("Vertex streamed", "%i kB", this_frame.bytes_vertex_streamed / 1024);
  94. draw_statistic("Index streamed", "%i kB", this_frame.bytes_index_streamed / 1024);
  95. draw_statistic("Uniform streamed", "%i kB", this_frame.bytes_uniform_streamed / 1024);
  96. draw_statistic("Vertex Loaders", "%d", num_vertex_loaders);
  97. draw_statistic("EFB peeks:", "%d", this_frame.num_efb_peeks);
  98. draw_statistic("EFB pokes:", "%d", this_frame.num_efb_pokes);
  99. draw_statistic("Draw dones:", "%d", this_frame.num_draw_done);
  100. draw_statistic("Tokens:", "%d/%d", this_frame.num_token, this_frame.num_token_int);
  101. ImGui::Columns(1);
  102. ImGui::End();
  103. }
  104. void Statistics::DisplayProj() const
  105. {
  106. if (!ImGui::Begin("Projection Statistics", nullptr, ImGuiWindowFlags_NoNavInputs))
  107. {
  108. ImGui::End();
  109. return;
  110. }
  111. ImGui::TextUnformatted("Projection #: X for Raw 6=0 (X for Raw 6!=0)");
  112. ImGui::NewLine();
  113. ImGui::Text("Projection 0: %f (%f) Raw 0: %f", gproj[0], g2proj[0], proj[0]);
  114. ImGui::Text("Projection 1: %f (%f)", gproj[1], g2proj[1]);
  115. ImGui::Text("Projection 2: %f (%f) Raw 1: %f", gproj[2], g2proj[2], proj[1]);
  116. ImGui::Text("Projection 3: %f (%f)", gproj[3], g2proj[3]);
  117. ImGui::Text("Projection 4: %f (%f)", gproj[4], g2proj[4]);
  118. ImGui::Text("Projection 5: %f (%f) Raw 2: %f", gproj[5], g2proj[5], proj[2]);
  119. ImGui::Text("Projection 6: %f (%f) Raw 3: %f", gproj[6], g2proj[6], proj[3]);
  120. ImGui::Text("Projection 7: %f (%f)", gproj[7], g2proj[7]);
  121. ImGui::Text("Projection 8: %f (%f)", gproj[8], g2proj[8]);
  122. ImGui::Text("Projection 9: %f (%f)", gproj[9], g2proj[9]);
  123. ImGui::Text("Projection 10: %f (%f) Raw 4: %f", gproj[10], g2proj[10], proj[4]);
  124. ImGui::Text("Projection 11: %f (%f) Raw 5: %f", gproj[11], g2proj[11], proj[5]);
  125. ImGui::Text("Projection 12: %f (%f)", gproj[12], g2proj[12]);
  126. ImGui::Text("Projection 13: %f (%f)", gproj[13], g2proj[13]);
  127. ImGui::Text("Projection 14: %f (%f)", gproj[14], g2proj[14]);
  128. ImGui::Text("Projection 15: %f (%f)", gproj[15], g2proj[15]);
  129. ImGui::NewLine();
  130. ImGui::Text("Avg Projection Viewport Ratio Persp(3D): %f", avg_persp_proj_viewport_ratio);
  131. ImGui::Text("Avg Projection Viewport Ratio Ortho(2D): %f", avg_ortho_proj_viewport_ratio);
  132. ImGui::End();
  133. }
  134. void Statistics::AddScissorRect()
  135. {
  136. if (clear_scissors)
  137. {
  138. scissors.clear();
  139. clear_scissors = false;
  140. }
  141. BPFunctions::ScissorResult scissor = BPFunctions::ComputeScissorRects();
  142. bool add;
  143. if (scissors.empty())
  144. {
  145. add = true;
  146. }
  147. else
  148. {
  149. if (allow_duplicate_scissors)
  150. {
  151. // Only check the last entry
  152. add = !scissors.back().Matches(scissor, show_scissors, show_viewports);
  153. }
  154. else
  155. {
  156. add = std::ranges::find_if(scissors, [&](auto& s) {
  157. return s.Matches(scissor, show_scissors, show_viewports);
  158. }) == scissors.end();
  159. }
  160. }
  161. if (add)
  162. scissors.push_back(std::move(scissor));
  163. }
  164. void Statistics::DisplayScissor()
  165. {
  166. // TODO: This is the same position as the regular statistics text
  167. const float scale = ImGui::GetIO().DisplayFramebufferScale.x;
  168. ImGui::SetNextWindowPos(ImVec2(10.0f * scale, 10.0f * scale), ImGuiCond_FirstUseEver);
  169. if (!ImGui::Begin("Scissor Rectangles", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
  170. {
  171. ImGui::End();
  172. return;
  173. }
  174. if (ImGui::TreeNode("Options"))
  175. {
  176. ImGui::Checkbox("Allow Duplicates", &allow_duplicate_scissors);
  177. ImGui::Checkbox("Show Scissors", &show_scissors);
  178. ImGui::BeginDisabled(!show_scissors);
  179. ImGui::Checkbox("Show Raw Values", &show_raw_scissors);
  180. ImGui::EndDisabled();
  181. ImGui::Checkbox("Show Viewports", &show_viewports);
  182. ImGui::Checkbox("Show Text", &show_text);
  183. ImGui::DragInt("Scale", &scissor_scale, .2f, 1, 16);
  184. ImGui::DragInt("Expected Scissor Count", &scissor_expected_count, .2f, 0, 16);
  185. ImGui::TreePop();
  186. }
  187. ImGui::BeginDisabled(current_scissor == 0);
  188. if (ImGui::ArrowButton("##left", ImGuiDir_Left))
  189. {
  190. current_scissor--;
  191. }
  192. ImGui::EndDisabled();
  193. ImGui::SameLine();
  194. ImGui::BeginDisabled(current_scissor >= scissors.size());
  195. if (ImGui::ArrowButton("##right", ImGuiDir_Right))
  196. {
  197. current_scissor++;
  198. if (current_scissor > scissors.size())
  199. {
  200. current_scissor = scissors.size();
  201. }
  202. }
  203. ImGui::EndDisabled();
  204. ImGui::SameLine();
  205. if (current_scissor == 0)
  206. ImGui::Text("Displaying all %zu rectangle(s)", scissors.size());
  207. else if (current_scissor <= scissors.size())
  208. ImGui::Text("Displaying rectangle %zu / %zu", current_scissor, scissors.size());
  209. else
  210. ImGui::Text("Displaying rectangle %zu / %zu (OoB)", current_scissor, scissors.size());
  211. ImDrawList* draw_list = ImGui::GetWindowDrawList();
  212. ImVec2 p = ImGui::GetCursorScreenPos();
  213. ImGui::Dummy(ImVec2(1024 * 3 / scissor_scale, 1024 * 3 / scissor_scale));
  214. constexpr int DRAW_START = -1024;
  215. constexpr int DRAW_END = DRAW_START + 3 * 1024;
  216. const auto vec = [&](int x, int y, int xoff = 0, int yoff = 0) {
  217. return ImVec2(p.x + int(float(x - DRAW_START) / scissor_scale) + xoff,
  218. p.y + int(float(y - DRAW_START) / scissor_scale) + yoff);
  219. };
  220. const auto light_grey = ImGui::GetColorU32(ImVec4(.5f, .5f, .5f, 1.f));
  221. // Draw gridlines
  222. for (int x = DRAW_START; x <= DRAW_END; x += 1024)
  223. draw_list->AddLine(vec(x, DRAW_START), vec(x, DRAW_END), light_grey);
  224. for (int y = DRAW_START; y <= DRAW_END; y += 1024)
  225. draw_list->AddLine(vec(DRAW_START, y), vec(DRAW_END, y), light_grey);
  226. const auto draw_x = [&](int x, int y, int size, ImU32 col) {
  227. // Add an extra offset on the second parameter as otherwise ImGui seems to give results that are
  228. // too small on one side
  229. draw_list->AddLine(vec(x, y, -size, -size), vec(x, y, +size + 1, +size + 1), col);
  230. draw_list->AddLine(vec(x, y, -size, +size), vec(x, y, +size + 1, -size - 1), col);
  231. };
  232. const auto draw_rect = [&](int x0, int y0, int x1, int y1, ImU32 col, bool show_oob = true) {
  233. x0 = std::clamp(x0, DRAW_START, DRAW_END);
  234. y0 = std::clamp(y0, DRAW_START, DRAW_END);
  235. x1 = std::clamp(x1, DRAW_START, DRAW_END);
  236. y1 = std::clamp(y1, DRAW_START, DRAW_END);
  237. if (x0 < x1 && y0 < y1)
  238. {
  239. draw_list->AddRect(vec(x0, y0), vec(x1, y1), col);
  240. }
  241. else if (show_oob)
  242. {
  243. // Markers at the two corners, for when they don't form a valid rectangle.
  244. draw_list->AddLine(vec(x0, y0), vec(x0, y0, 8, 0), col);
  245. draw_list->AddLine(vec(x0, y0), vec(x0, y0, 0, 8), col);
  246. draw_list->AddLine(vec(x1, y1), vec(x1, y1, -8, 0), col);
  247. draw_list->AddLine(vec(x1, y1), vec(x1, y1, 0, -8), col);
  248. }
  249. };
  250. static std::array<ImVec4, 6> COLORS = {
  251. ImVec4(1, 0, 0, 1), ImVec4(1, 1, 0, 1), ImVec4(0, 1, 0, 1),
  252. ImVec4(0, 1, 1, 1), ImVec4(0, 0, 1, 1), ImVec4(1, 0, 1, 1),
  253. };
  254. const auto draw_scissor = [&](size_t index) {
  255. const auto& info = scissors[index];
  256. const ImU32 col = ImGui::GetColorU32(COLORS[index % COLORS.size()]);
  257. int x_off = info.scissor_off.x << 1;
  258. int y_off = info.scissor_off.y << 1;
  259. // Subtract 2048 instead of 1024, because when x_off is large enough we need to show two
  260. // rectangles in the upper sections
  261. for (int y = y_off - 2048; y < DRAW_END; y += 1024)
  262. {
  263. for (int x = x_off - 2048; x < DRAW_END; x += 1024)
  264. {
  265. draw_rect(x, y, x + EFB_WIDTH, y + EFB_HEIGHT, col, false);
  266. }
  267. }
  268. // Use the full offset here so that ones that have the extra bit set show up distinctly
  269. draw_x(info.scissor_off.x_full << 1, info.scissor_off.y_full << 1, 4, col);
  270. if (show_scissors)
  271. {
  272. draw_rect(info.scissor_tl.x, info.scissor_tl.y, info.scissor_br.x + 1, info.scissor_br.y + 1,
  273. col);
  274. }
  275. if (show_viewports)
  276. {
  277. draw_rect(info.viewport_left, info.viewport_top, info.viewport_right, info.viewport_bottom,
  278. col);
  279. }
  280. for (size_t i = 0; i < info.m_result.size(); i++)
  281. {
  282. // The last entry in the sorted list of results is the one that is used by hardware backends
  283. const u8 new_alpha = (i == info.m_result.size() - 1) ? 0x40 : 0x80;
  284. const ImU32 new_col = (col & ~IM_COL32_A_MASK) | (new_alpha << IM_COL32_A_SHIFT);
  285. const auto& r = info.m_result[i];
  286. draw_list->AddRectFilled(vec(r.rect.left + r.x_off, r.rect.top + r.y_off),
  287. vec(r.rect.right + r.x_off, r.rect.bottom + r.y_off), new_col);
  288. }
  289. };
  290. constexpr auto NUM_SCISSOR_COLUMNS = 8;
  291. const auto draw_scissor_table_header = [&]() {
  292. ImGui::TableSetupColumn("#");
  293. ImGui::TableSetupColumn("x0");
  294. ImGui::TableSetupColumn("y0");
  295. ImGui::TableSetupColumn("x1");
  296. ImGui::TableSetupColumn("y1");
  297. ImGui::TableSetupColumn("xOff");
  298. ImGui::TableSetupColumn("yOff");
  299. ImGui::TableSetupColumn("Affected");
  300. ImGui::TableHeadersRow();
  301. };
  302. const auto draw_scissor_table_row = [&](size_t index) {
  303. const auto& info = scissors[index];
  304. int x_off = (info.scissor_off.x << 1) - info.viewport_top;
  305. int y_off = (info.scissor_off.y << 1) - info.viewport_left;
  306. int x0 = info.scissor_tl.x - info.viewport_top;
  307. int x1 = info.scissor_br.x - info.viewport_left;
  308. int y0 = info.scissor_tl.y - info.viewport_top;
  309. int y1 = info.scissor_br.y - info.viewport_left;
  310. ImGui::TableNextColumn();
  311. ImGui::TextColored(COLORS[index % COLORS.size()], "%zu", index + 1);
  312. ImGui::TableNextColumn();
  313. ImGui::Text("%d", x0);
  314. ImGui::TableNextColumn();
  315. ImGui::Text("%d", y0);
  316. ImGui::TableNextColumn();
  317. ImGui::Text("%d", x1);
  318. ImGui::TableNextColumn();
  319. ImGui::Text("%d", y1);
  320. ImGui::TableNextColumn();
  321. ImGui::Text("%d", x_off);
  322. ImGui::TableNextColumn();
  323. ImGui::Text("%d", y_off);
  324. // Visualization of where things are updated on screen with this specific scissor
  325. ImGui::TableNextColumn();
  326. float scale_height = ImGui::GetTextLineHeight() / EFB_HEIGHT;
  327. if (show_raw_scissors)
  328. scale_height += ImGui::GetTextLineHeightWithSpacing() / EFB_HEIGHT;
  329. ImVec2 p2 = ImGui::GetCursorScreenPos();
  330. // Use a height of 1 since we want this to span two table rows (if possible)
  331. ImGui::Dummy(ImVec2(EFB_WIDTH * scale_height, 1));
  332. for (size_t i = 0; i < info.m_result.size(); i++)
  333. {
  334. // The last entry in the sorted list of results is the one that is used by hardware backends
  335. const u8 new_alpha = (i == info.m_result.size() - 1) ? 0x80 : 0x40;
  336. const ImU32 col = ImGui::GetColorU32(COLORS[index % COLORS.size()]);
  337. const ImU32 new_col = (col & ~IM_COL32_A_MASK) | (new_alpha << IM_COL32_A_SHIFT);
  338. const auto& r = info.m_result[i];
  339. draw_list->AddRectFilled(
  340. ImVec2(p2.x + r.rect.left * scale_height, p2.y + r.rect.top * scale_height),
  341. ImVec2(p2.x + r.rect.right * scale_height, p2.y + r.rect.bottom * scale_height), new_col);
  342. }
  343. draw_list->AddRect(
  344. p2, ImVec2(p2.x + EFB_WIDTH * scale_height, p2.y + EFB_HEIGHT * scale_height), light_grey);
  345. ImGui::SameLine();
  346. ImGui::Text("%d", int(info.m_result.size()));
  347. if (show_raw_scissors)
  348. {
  349. ImGui::TableNextColumn();
  350. ImGui::TextColored(COLORS[index % COLORS.size()], "Raw");
  351. ImGui::TableNextColumn();
  352. ImGui::Text("%d", info.scissor_tl.x_full.Value());
  353. ImGui::TableNextColumn();
  354. ImGui::Text("%d", info.scissor_tl.y_full.Value());
  355. ImGui::TableNextColumn();
  356. ImGui::Text("%d", info.scissor_br.x_full.Value());
  357. ImGui::TableNextColumn();
  358. ImGui::Text("%d", info.scissor_br.y_full.Value());
  359. ImGui::TableNextColumn();
  360. ImGui::Text("%d", info.scissor_off.x_full.Value());
  361. ImGui::TableNextColumn();
  362. ImGui::Text("%d", info.scissor_off.y_full.Value());
  363. ImGui::TableNextColumn();
  364. }
  365. };
  366. const auto scissor_table_skip_row = [&](size_t index) {
  367. ImGui::TableNextRow();
  368. ImGui::TableNextColumn();
  369. ImGui::TextColored(COLORS[index % COLORS.size()], "%zu", index + 1);
  370. if (show_raw_scissors)
  371. {
  372. ImGui::TableNextRow();
  373. ImGui::TableNextColumn();
  374. ImGui::TextColored(COLORS[index % COLORS.size()], "Raw");
  375. }
  376. };
  377. constexpr auto NUM_VIEWPORT_COLUMNS = 5;
  378. const auto draw_viewport_table_header = [&]() {
  379. ImGui::TableSetupColumn("#");
  380. ImGui::TableSetupColumn("vx0");
  381. ImGui::TableSetupColumn("vy0");
  382. ImGui::TableSetupColumn("vx1");
  383. ImGui::TableSetupColumn("vy1");
  384. ImGui::TableHeadersRow();
  385. };
  386. const auto draw_viewport_table_row = [&](size_t index) {
  387. const auto& info = scissors[index];
  388. ImGui::TableNextColumn();
  389. ImGui::TextColored(COLORS[index % COLORS.size()], "%zu", index + 1);
  390. ImGui::TableNextColumn();
  391. ImGui::Text("%.1f", info.viewport_left);
  392. ImGui::TableNextColumn();
  393. ImGui::Text("%.1f", info.viewport_top);
  394. ImGui::TableNextColumn();
  395. ImGui::Text("%.1f", info.viewport_right);
  396. ImGui::TableNextColumn();
  397. ImGui::Text("%.1f", info.viewport_bottom);
  398. };
  399. const auto viewport_table_skip_row = [&](size_t index) {
  400. ImGui::TableNextRow();
  401. ImGui::TableNextColumn();
  402. ImGui::TextColored(COLORS[index % COLORS.size()], "%zu", index + 1);
  403. };
  404. if (current_scissor == 0)
  405. {
  406. for (size_t i = 0; i < scissors.size(); i++)
  407. draw_scissor(i);
  408. if (show_text)
  409. {
  410. if (show_scissors)
  411. {
  412. if (ImGui::BeginTable("Scissors", NUM_SCISSOR_COLUMNS))
  413. {
  414. draw_scissor_table_header();
  415. for (size_t i = 0; i < scissors.size(); i++)
  416. draw_scissor_table_row(i);
  417. for (size_t i = scissors.size(); i < static_cast<size_t>(scissor_expected_count); i++)
  418. scissor_table_skip_row(i);
  419. ImGui::EndTable();
  420. }
  421. }
  422. if (show_viewports)
  423. {
  424. if (ImGui::BeginTable("Viewports", NUM_VIEWPORT_COLUMNS))
  425. {
  426. draw_viewport_table_header();
  427. for (size_t i = 0; i < scissors.size(); i++)
  428. draw_viewport_table_row(i);
  429. for (size_t i = scissors.size(); i < static_cast<size_t>(scissor_expected_count); i++)
  430. viewport_table_skip_row(i);
  431. ImGui::EndTable();
  432. }
  433. }
  434. }
  435. }
  436. else if (current_scissor <= scissors.size())
  437. {
  438. // This bounds check is needed since we only clamp when changing the value; different frames may
  439. // have different numbers
  440. draw_scissor(current_scissor - 1);
  441. if (show_text)
  442. {
  443. if (show_scissors)
  444. {
  445. if (ImGui::BeginTable("Scissors", NUM_SCISSOR_COLUMNS))
  446. {
  447. draw_scissor_table_header();
  448. draw_scissor_table_row(current_scissor - 1);
  449. ImGui::EndTable();
  450. }
  451. if (ImGui::BeginTable("Viewports", NUM_VIEWPORT_COLUMNS))
  452. {
  453. draw_viewport_table_header();
  454. draw_viewport_table_row(current_scissor - 1);
  455. ImGui::EndTable();
  456. }
  457. }
  458. }
  459. }
  460. else if (show_text)
  461. {
  462. if (show_scissors)
  463. ImGui::Text("Scissor %zu: Does not exist", current_scissor);
  464. if (show_viewports)
  465. ImGui::Text("Viewport %zu: Does not exist", current_scissor);
  466. }
  467. ImGui::End();
  468. }