mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-21 05:09:34 -06:00
Implement ImGui window for scissor rectangles
This is mainly for debugging, and is only exposed by manually editing the configuration.
This commit is contained in:
@ -3,18 +3,26 @@
|
||||
|
||||
#include "VideoCommon/Statistics.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "VideoCommon/BPFunctions.h"
|
||||
#include "VideoCommon/VideoCommon.h"
|
||||
#include "VideoCommon/VideoConfig.h"
|
||||
|
||||
Statistics g_stats;
|
||||
static bool clear_scissors;
|
||||
|
||||
void Statistics::ResetFrame()
|
||||
{
|
||||
this_frame = {};
|
||||
clear_scissors = true;
|
||||
if (scissors.size() > 1)
|
||||
{
|
||||
scissors.erase(scissors.begin(), scissors.end() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void Statistics::SwapDL()
|
||||
@ -121,3 +129,355 @@ void Statistics::DisplayProj() const
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void Statistics::AddScissorRect()
|
||||
{
|
||||
if (clear_scissors)
|
||||
{
|
||||
scissors.clear();
|
||||
clear_scissors = false;
|
||||
}
|
||||
|
||||
BPFunctions::ScissorResult scissor = BPFunctions::ComputeScissorRects();
|
||||
bool add;
|
||||
if (scissors.empty())
|
||||
{
|
||||
add = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (allow_duplicate_scissors)
|
||||
{
|
||||
// Only check the last entry
|
||||
add = !scissors.back().Matches(scissor, show_scissors, show_viewports);
|
||||
}
|
||||
else
|
||||
{
|
||||
add = std::find_if(scissors.begin(), scissors.end(), [&](auto& s) {
|
||||
return s.Matches(scissor, show_scissors, show_viewports);
|
||||
}) == scissors.end();
|
||||
}
|
||||
}
|
||||
if (add)
|
||||
scissors.push_back(std::move(scissor));
|
||||
}
|
||||
|
||||
void Statistics::DisplayScissor()
|
||||
{
|
||||
// TODO: This is the same position as the regular statistics text
|
||||
const float scale = ImGui::GetIO().DisplayFramebufferScale.x;
|
||||
ImGui::SetNextWindowPos(ImVec2(10.0f * scale, 10.0f * scale), ImGuiCond_FirstUseEver);
|
||||
|
||||
if (!ImGui::Begin("Scissor Rectangles", nullptr, ImGuiWindowFlags_AlwaysAutoResize))
|
||||
{
|
||||
ImGui::End();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::TreeNode("Options"))
|
||||
{
|
||||
ImGui::Checkbox("Allow Duplicates", &allow_duplicate_scissors);
|
||||
ImGui::Checkbox("Show Scissors", &show_scissors);
|
||||
ImGui::BeginDisabled(!show_scissors);
|
||||
ImGui::Checkbox("Show Raw Values", &show_raw_scissors);
|
||||
ImGui::EndDisabled();
|
||||
ImGui::Checkbox("Show Viewports", &show_viewports);
|
||||
ImGui::Checkbox("Show Text", &show_text);
|
||||
ImGui::DragInt("Scale", &scissor_scale, .2f, 1, 16);
|
||||
ImGui::DragInt("Expected Scissor Count", &scissor_expected_count, .2f, 0, 16);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
|
||||
ImGui::BeginDisabled(current_scissor == 0);
|
||||
if (ImGui::ArrowButton("##left", ImGuiDir_Left))
|
||||
{
|
||||
current_scissor--;
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginDisabled(current_scissor >= scissors.size());
|
||||
if (ImGui::ArrowButton("##right", ImGuiDir_Right))
|
||||
{
|
||||
current_scissor++;
|
||||
if (current_scissor > scissors.size())
|
||||
{
|
||||
current_scissor = scissors.size();
|
||||
}
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
if (current_scissor == 0)
|
||||
ImGui::Text("Displaying all %zu rectangle(s)", scissors.size());
|
||||
else if (current_scissor <= scissors.size())
|
||||
ImGui::Text("Displaying rectangle %zu / %zu", current_scissor, scissors.size());
|
||||
else
|
||||
ImGui::Text("Displaying rectangle %zu / %zu (OoB)", current_scissor, scissors.size());
|
||||
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 p = ImGui::GetCursorScreenPos();
|
||||
ImGui::Dummy(ImVec2(1024 * 3 / scissor_scale, 1024 * 3 / scissor_scale));
|
||||
|
||||
constexpr int DRAW_START = -1024;
|
||||
constexpr int DRAW_END = DRAW_START + 3 * 1024;
|
||||
|
||||
const auto vec = [&](int x, int y, int xoff = 0, int yoff = 0) {
|
||||
return ImVec2(p.x + int(float(x - DRAW_START) / scissor_scale) + xoff,
|
||||
p.y + int(float(y - DRAW_START) / scissor_scale) + yoff);
|
||||
};
|
||||
|
||||
const auto light_grey = ImGui::GetColorU32(ImVec4(.5f, .5f, .5f, 1.f));
|
||||
|
||||
// Draw gridlines
|
||||
for (int x = DRAW_START; x <= DRAW_END; x += 1024)
|
||||
draw_list->AddLine(vec(x, DRAW_START), vec(x, DRAW_END), light_grey);
|
||||
for (int y = DRAW_START; y <= DRAW_END; y += 1024)
|
||||
draw_list->AddLine(vec(DRAW_START, y), vec(DRAW_END, y), light_grey);
|
||||
|
||||
const auto draw_x = [&](int x, int y, int size, ImU32 col) {
|
||||
// Add an extra offset on the second parameter as otherwise ImGui seems to give results that are
|
||||
// too small on one side
|
||||
draw_list->AddLine(vec(x, y, -size, -size), vec(x, y, +size + 1, +size + 1), col);
|
||||
draw_list->AddLine(vec(x, y, -size, +size), vec(x, y, +size + 1, -size - 1), col);
|
||||
};
|
||||
const auto draw_rect = [&](int x0, int y0, int x1, int y1, ImU32 col, bool show_oob = true) {
|
||||
x0 = std::clamp(x0, DRAW_START, DRAW_END);
|
||||
y0 = std::clamp(y0, DRAW_START, DRAW_END);
|
||||
x1 = std::clamp(x1, DRAW_START, DRAW_END);
|
||||
y1 = std::clamp(y1, DRAW_START, DRAW_END);
|
||||
if (x0 < x1 && y0 < y1)
|
||||
{
|
||||
draw_list->AddRect(vec(x0, y0), vec(x1, y1), col);
|
||||
}
|
||||
else if (show_oob)
|
||||
{
|
||||
// Markers at the two corners, for when they don't form a valid rectangle.
|
||||
draw_list->AddLine(vec(x0, y0), vec(x0, y0, 8, 0), col);
|
||||
draw_list->AddLine(vec(x0, y0), vec(x0, y0, 0, 8), col);
|
||||
draw_list->AddLine(vec(x1, y1), vec(x1, y1, -8, 0), col);
|
||||
draw_list->AddLine(vec(x1, y1), vec(x1, y1, 0, -8), col);
|
||||
}
|
||||
};
|
||||
static std::array<ImVec4, 6> COLORS = {
|
||||
ImVec4(1, 0, 0, 1), ImVec4(1, 1, 0, 1), ImVec4(0, 1, 0, 1),
|
||||
ImVec4(0, 1, 1, 1), ImVec4(0, 0, 1, 1), ImVec4(1, 0, 1, 1),
|
||||
};
|
||||
const auto draw_scissor = [&](size_t index) {
|
||||
const auto& info = scissors[index];
|
||||
const ImU32 col = ImGui::GetColorU32(COLORS[index % COLORS.size()]);
|
||||
int x_off = info.scissor_off.x << 1;
|
||||
int y_off = info.scissor_off.y << 1;
|
||||
// Subtract 2048 instead of 1024, because when x_off is large enough we need to show two
|
||||
// rectangles in the upper sections
|
||||
for (int y = y_off - 2048; y < DRAW_END; y += 1024)
|
||||
{
|
||||
for (int x = x_off - 2048; x < DRAW_END; x += 1024)
|
||||
{
|
||||
draw_rect(x, y, x + EFB_WIDTH, y + EFB_HEIGHT, col, false);
|
||||
}
|
||||
}
|
||||
// Use the full offset here so that ones that have the extra bit set show up distinctly
|
||||
draw_x(info.scissor_off.x_full << 1, info.scissor_off.y_full << 1, 4, col);
|
||||
|
||||
if (show_scissors)
|
||||
{
|
||||
draw_rect(info.scissor_tl.x, info.scissor_tl.y, info.scissor_br.x + 1, info.scissor_br.y + 1,
|
||||
col);
|
||||
}
|
||||
if (show_viewports)
|
||||
{
|
||||
draw_rect(info.viewport_left, info.viewport_top, info.viewport_right, info.viewport_bottom,
|
||||
col);
|
||||
}
|
||||
for (size_t i = 0; i < info.m_result.size(); i++)
|
||||
{
|
||||
// The last entry in the sorted list of results is the one that is used by hardware backends
|
||||
const u8 new_alpha = (i == info.m_result.size() - 1) ? 0x40 : 0x80;
|
||||
const ImU32 new_col = (col & ~IM_COL32_A_MASK) | (new_alpha << IM_COL32_A_SHIFT);
|
||||
|
||||
const auto& r = info.m_result[i];
|
||||
draw_list->AddRectFilled(vec(r.rect.left + r.x_off, r.rect.top + r.y_off),
|
||||
vec(r.rect.right + r.x_off, r.rect.bottom + r.y_off), new_col);
|
||||
}
|
||||
};
|
||||
constexpr auto NUM_SCISSOR_COLUMNS = 8;
|
||||
const auto draw_scissor_table_header = [&]() {
|
||||
ImGui::TableSetupColumn("#");
|
||||
ImGui::TableSetupColumn("x0");
|
||||
ImGui::TableSetupColumn("y0");
|
||||
ImGui::TableSetupColumn("x1");
|
||||
ImGui::TableSetupColumn("y1");
|
||||
ImGui::TableSetupColumn("xOff");
|
||||
ImGui::TableSetupColumn("yOff");
|
||||
ImGui::TableSetupColumn("Affected");
|
||||
ImGui::TableHeadersRow();
|
||||
};
|
||||
const auto draw_scissor_table_row = [&](size_t index) {
|
||||
const auto& info = scissors[index];
|
||||
int x_off = (info.scissor_off.x << 1) - info.viewport_top;
|
||||
int y_off = (info.scissor_off.y << 1) - info.viewport_left;
|
||||
int x0 = info.scissor_tl.x - info.viewport_top;
|
||||
int x1 = info.scissor_br.x - info.viewport_left;
|
||||
int y0 = info.scissor_tl.y - info.viewport_top;
|
||||
int y1 = info.scissor_br.y - info.viewport_left;
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(COLORS[index % COLORS.size()], "%zu", index + 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", x0);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", y0);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", x1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", y1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", x_off);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", y_off);
|
||||
|
||||
// Visualization of where things are updated on screen with this specific scissor
|
||||
ImGui::TableNextColumn();
|
||||
float scale = ImGui::GetTextLineHeight() / EFB_HEIGHT;
|
||||
if (show_raw_scissors)
|
||||
scale += ImGui::GetTextLineHeightWithSpacing() / EFB_HEIGHT;
|
||||
ImVec2 p2 = ImGui::GetCursorScreenPos();
|
||||
// Use a height of 1 since we want this to span two table rows (if possible)
|
||||
ImGui::Dummy(ImVec2(EFB_WIDTH * scale, 1));
|
||||
for (size_t i = 0; i < info.m_result.size(); i++)
|
||||
{
|
||||
// The last entry in the sorted list of results is the one that is used by hardware backends
|
||||
const u8 new_alpha = (i == info.m_result.size() - 1) ? 0x80 : 0x40;
|
||||
const ImU32 col = ImGui::GetColorU32(COLORS[index % COLORS.size()]);
|
||||
const ImU32 new_col = (col & ~IM_COL32_A_MASK) | (new_alpha << IM_COL32_A_SHIFT);
|
||||
|
||||
const auto& r = info.m_result[i];
|
||||
draw_list->AddRectFilled(ImVec2(p2.x + r.rect.left * scale, p2.y + r.rect.top * scale),
|
||||
ImVec2(p2.x + r.rect.right * scale, p2.y + r.rect.bottom * scale),
|
||||
new_col);
|
||||
}
|
||||
draw_list->AddRect(p2, ImVec2(p2.x + EFB_WIDTH * scale, p2.y + EFB_HEIGHT * scale), light_grey);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%d", int(info.m_result.size()));
|
||||
|
||||
if (show_raw_scissors)
|
||||
{
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(COLORS[index % COLORS.size()], "Raw");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", info.scissor_tl.x_full.Value());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", info.scissor_tl.y_full.Value());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", info.scissor_br.x_full.Value());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", info.scissor_br.y_full.Value());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", info.scissor_off.x_full.Value());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", info.scissor_off.y_full.Value());
|
||||
ImGui::TableNextColumn();
|
||||
}
|
||||
};
|
||||
const auto scissor_table_skip_row = [&](size_t index) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(COLORS[index % COLORS.size()], "%zu", index + 1);
|
||||
if (show_raw_scissors)
|
||||
{
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(COLORS[index % COLORS.size()], "Raw");
|
||||
}
|
||||
};
|
||||
constexpr auto NUM_VIEWPORT_COLUMNS = 5;
|
||||
const auto draw_viewport_table_header = [&]() {
|
||||
ImGui::TableSetupColumn("#");
|
||||
ImGui::TableSetupColumn("vx0");
|
||||
ImGui::TableSetupColumn("vy0");
|
||||
ImGui::TableSetupColumn("vx1");
|
||||
ImGui::TableSetupColumn("vy1");
|
||||
ImGui::TableHeadersRow();
|
||||
};
|
||||
const auto draw_viewport_table_row = [&](size_t index) {
|
||||
const auto& info = scissors[index];
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(COLORS[index % COLORS.size()], "%zu", index + 1);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.1f", info.viewport_left);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.1f", info.viewport_top);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.1f", info.viewport_right);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%.1f", info.viewport_bottom);
|
||||
};
|
||||
const auto viewport_table_skip_row = [&](size_t index) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextColored(COLORS[index % COLORS.size()], "%zu", index + 1);
|
||||
};
|
||||
if (current_scissor == 0)
|
||||
{
|
||||
for (size_t i = 0; i < scissors.size(); i++)
|
||||
draw_scissor(i);
|
||||
if (show_text)
|
||||
{
|
||||
if (show_scissors)
|
||||
{
|
||||
if (ImGui::BeginTable("Scissors", NUM_SCISSOR_COLUMNS))
|
||||
{
|
||||
draw_scissor_table_header();
|
||||
for (size_t i = 0; i < scissors.size(); i++)
|
||||
draw_scissor_table_row(i);
|
||||
for (size_t i = scissors.size(); i < scissor_expected_count; i++)
|
||||
scissor_table_skip_row(i);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
if (show_viewports)
|
||||
{
|
||||
if (ImGui::BeginTable("Viewports", NUM_VIEWPORT_COLUMNS))
|
||||
{
|
||||
draw_viewport_table_header();
|
||||
for (size_t i = 0; i < scissors.size(); i++)
|
||||
draw_viewport_table_row(i);
|
||||
for (size_t i = scissors.size(); i < scissor_expected_count; i++)
|
||||
viewport_table_skip_row(i);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (current_scissor <= scissors.size())
|
||||
{
|
||||
// This bounds check is needed since we only clamp when changing the value; different frames may
|
||||
// have different numbers
|
||||
draw_scissor(current_scissor - 1);
|
||||
if (show_text)
|
||||
{
|
||||
if (show_scissors)
|
||||
{
|
||||
if (ImGui::BeginTable("Scissors", NUM_SCISSOR_COLUMNS))
|
||||
{
|
||||
draw_scissor_table_header();
|
||||
draw_scissor_table_row(current_scissor - 1);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
if (ImGui::BeginTable("Viewports", NUM_VIEWPORT_COLUMNS))
|
||||
{
|
||||
draw_viewport_table_header();
|
||||
draw_viewport_table_row(current_scissor - 1);
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (show_text)
|
||||
{
|
||||
if (show_scissors)
|
||||
ImGui::Text("Scissor %zu: Does not exist", current_scissor);
|
||||
if (show_viewports)
|
||||
ImGui::Text("Viewport %zu: Does not exist", current_scissor);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
Reference in New Issue
Block a user