Add rudimentary block transfer visualization (in text form)

This commit is contained in:
Henrik Rydgård 2024-12-17 11:35:04 +01:00
parent db13c09c41
commit b5ed3051e5
2 changed files with 130 additions and 110 deletions

View file

@ -193,6 +193,7 @@ void IndexGenerator::AddStrip(int numVerts, int indexOffset, bool clockwise) {
#endif
}
// God of War uses this for text. Otherwise rare, not much reason to optimize.
void IndexGenerator::AddFan(int numVerts, int indexOffset, bool clockwise) {
const int numTris = numVerts - 2;
u16 *outInds = inds_;

View file

@ -896,20 +896,30 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
u32 op = 0;
DisplayList list;
bool isOnBlockTransfer = false;
if (gpuDebug->GetCurrentDisplayList(list)) {
op = Memory::Read_U32(list.pc);
// TODO: Also add support for block transfer previews!
bool isOnPrim = (op >> 24) == GE_CMD_PRIM;
if (isOnPrim) {
bool isOnPrim = false;
switch (op >> 24) {
case GE_CMD_PRIM:
isOnPrim = true;
if (reloadPreview_) {
GetPrimPreview(op, previewPrim_, previewVertices_, previewIndices_, previewCount_);
reloadPreview_ = false;
}
} else {
break;
case GE_CMD_TRANSFERSTART:
isOnBlockTransfer = true;
break;
default:
// Disable the current preview.
previewCount_ = 0;
break;
}
}
ImGui::BeginChild("texture/fb view"); // Leave room for 1 line below us
@ -917,125 +927,134 @@ void ImGeDebuggerWindow::Draw(ImConfig &cfg, ImControl &control, GPUDebugInterfa
ImDrawList *drawList = ImGui::GetWindowDrawList();
if (coreState == CORE_STEPPING_GE) {
VirtualFramebuffer *vfb = rbViewer_.vfb;
if (vfb) {
if (vfb->fbo) {
ImGui::Text("Framebuffer: %s", vfb->fbo->Tag());
} else {
ImGui::Text("Framebuffer");
if (isOnBlockTransfer) {
ImGui::Text("Block transfer! Proper preview coming in the future.\n");
ImGui::Text("%08x -> %08x, %d bpp (strides: %d, %d)", gstate.getTransferSrcAddress(), gstate.getTransferDstAddress(), gstate.getTransferBpp(), gstate.getTransferSrcStride(), gstate.getTransferDstStride());
ImGui::Text("%dx%d pixels", gstate.getTransferWidth(), gstate.getTransferHeight());
ImGui::Text("Src pos: %d, %d", gstate.getTransferSrcX(), gstate.getTransferSrcY());
ImGui::Text("Dst pos: %d, %d", gstate.getTransferDstX(), gstate.getTransferDstY());
ImGui::Text("Total bytes to transfer: %d", gstate.getTransferWidth() * gstate.getTransferHeight() * gstate.getTransferBpp());
} else {
// Visualize prim by default (even if we're not directly on a prim instruction).
VirtualFramebuffer *vfb = rbViewer_.vfb;
if (vfb) {
if (vfb->fbo) {
ImGui::Text("Framebuffer: %s", vfb->fbo->Tag());
} else {
ImGui::Text("Framebuffer");
}
}
}
// Use selectable instead of tab bar so we can get events (haven't figured that out).
static const Draw::Aspect aspects[3] = { Draw::Aspect::COLOR_BIT, Draw::Aspect::DEPTH_BIT, Draw::Aspect::STENCIL_BIT, };
static const char *const aspectNames[3] = { "Color", "Depth", "Stencil" };
for (int i = 0; i < ARRAY_SIZE(aspects); i++) {
if (i != 0)
// Use selectable instead of tab bar so we can get events (haven't figured that out).
static const Draw::Aspect aspects[3] = { Draw::Aspect::COLOR_BIT, Draw::Aspect::DEPTH_BIT, Draw::Aspect::STENCIL_BIT, };
static const char *const aspectNames[3] = { "Color", "Depth", "Stencil" };
for (int i = 0; i < ARRAY_SIZE(aspects); i++) {
if (i != 0)
ImGui::SameLine();
if (ImGui::Selectable(aspectNames[i], aspects[i] == selectedAspect_, 0, ImVec2(120.0f, 0.0f))) {
selectedAspect_ = aspects[i];
NotifyStep();
}
}
if (selectedAspect_ == Draw::Aspect::DEPTH_BIT) {
float minimum = 0.5f;
float maximum = 256.0f;
ImGui::SameLine();
if (ImGui::Selectable(aspectNames[i], aspects[i] == selectedAspect_, 0, ImVec2(120.0f, 0.0f))) {
selectedAspect_ = aspects[i];
NotifyStep();
ImGui::SetNextItemWidth(200.0f);
if (ImGui::DragFloat("Z scale", &rbViewer_.scale, 1.0f, 0.5f, 256.0f, "%0.2f", ImGuiSliderFlags_Logarithmic)) {
rbViewer_.Snapshot();
swViewer_.Snapshot();
}
}
}
if (selectedAspect_ == Draw::Aspect::DEPTH_BIT) {
float minimum = 0.5f;
float maximum = 256.0f;
ImGui::SameLine();
ImGui::SetNextItemWidth(200.0f);
if (ImGui::DragFloat("Z scale", &rbViewer_.scale, 1.0f, 0.5f, 256.0f, "%0.2f", ImGuiSliderFlags_Logarithmic)) {
rbViewer_.Snapshot();
swViewer_.Snapshot();
}
}
const ImVec2 p0 = ImGui::GetCursorScreenPos();
ImVec2 p1;
float scale = 1.0f;
if (vfb && vfb->fbo) {
scale = vfb->renderScaleFactor;
p1 = ImVec2(p0.x + vfb->fbo->Width(), p0.y + vfb->fbo->Height());
} else {
// Guess
p1 = ImVec2(p0.x + swViewer_.width, p0.y + swViewer_.height);
}
// Draw border and background color
drawList->PushClipRect(p0, p1, true);
PixelLookup *lookup = nullptr;
if (vfb) {
rbViewer_.Draw(gpuDebug, draw);
lookup = &rbViewer_;
// ImTextureID texId = ImGui_ImplThin3d_AddFBAsTextureTemp(vfb->fbo, Draw::Aspect::COLOR_BIT, ImGuiPipeline::TexturedOpaque);
// ImGui::Image(texId, ImVec2(vfb->width, vfb->height));
} else {
swViewer_.Draw(gpuDebug, draw);
lookup = &swViewer_;
}
// Draw vertex preview on top!
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, false, scale, scale);
drawList->PopClipRect();
if (ImGui::IsItemHovered()) {
int x = (int)(ImGui::GetMousePos().x - p0.x);
int y = (int)(ImGui::GetMousePos().y - p0.y);
char temp[128];
if (lookup->FormatValueAt(temp, sizeof(temp), x, y)) {
ImGui::Text("(%d, %d): %s", x, y, temp);
const ImVec2 p0 = ImGui::GetCursorScreenPos();
ImVec2 p1;
float scale = 1.0f;
if (vfb && vfb->fbo) {
scale = vfb->renderScaleFactor;
p1 = ImVec2(p0.x + vfb->fbo->Width(), p0.y + vfb->fbo->Height());
} else {
ImGui::Text("%d, %d: N/A");
// Guess
p1 = ImVec2(p0.x + swViewer_.width, p0.y + swViewer_.height);
}
} else {
ImGui::TextUnformatted("(no pixel hovered)");
}
if (vfb && vfb->fbo) {
ImGui::Text("VFB %dx%d (emulated: %dx%d)", vfb->width, vfb->height, vfb->fbo->Width(), vfb->fbo->Height());
} else {
// Use the swViewer_!
ImGui::Text("Raw FB: %08x (%s)", gstate.getFrameBufRawAddress(), GeBufferFormatToString(gstate.FrameBufFormat()));
}
// Draw border and background color
drawList->PushClipRect(p0, p1, true);
if (gstate.isModeClear()) {
ImGui::Text("(clear mode - texturing not used)");
} else if (!gstate.isTextureMapEnabled()) {
ImGui::Text("(texturing not enabled");
} else {
TextureCacheCommon *texcache = gpuDebug->GetTextureCacheCommon();
TexCacheEntry *tex = texcache ? texcache->SetTexture() : nullptr;
if (tex) {
ImGui::Text("Texture: ");
texcache->ApplyTexture();
void *nativeView = texcache->GetNativeTextureView(tex, true);
ImTextureID texId = ImGui_ImplThin3d_AddNativeTextureTemp(nativeView);
float texW = dimWidth(tex->dim);
float texH = dimHeight(tex->dim);
const ImVec2 p0 = ImGui::GetCursorScreenPos();
const ImVec2 sz = ImGui::GetContentRegionAvail();
const ImVec2 p1 = ImVec2(p0.x + texW, p0.y + texH);
// Draw border and background color
drawList->PushClipRect(p0, p1, true);
ImGui::Image(texId, ImVec2(texW, texH));
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, true, texW, texH);
drawList->PopClipRect();
PixelLookup *lookup = nullptr;
if (vfb) {
rbViewer_.Draw(gpuDebug, draw);
lookup = &rbViewer_;
// ImTextureID texId = ImGui_ImplThin3d_AddFBAsTextureTemp(vfb->fbo, Draw::Aspect::COLOR_BIT, ImGuiPipeline::TexturedOpaque);
// ImGui::Image(texId, ImVec2(vfb->width, vfb->height));
} else {
ImGui::Text("(no valid texture bound)");
// In software mode, we should just decode the texture here.
// TODO: List some of the texture params here.
swViewer_.Draw(gpuDebug, draw);
lookup = &swViewer_;
}
// Draw vertex preview on top!
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, false, scale, scale);
drawList->PopClipRect();
if (ImGui::IsItemHovered()) {
int x = (int)(ImGui::GetMousePos().x - p0.x);
int y = (int)(ImGui::GetMousePos().y - p0.y);
char temp[128];
if (lookup->FormatValueAt(temp, sizeof(temp), x, y)) {
ImGui::Text("(%d, %d): %s", x, y, temp);
} else {
ImGui::Text("%d, %d: N/A");
}
} else {
ImGui::TextUnformatted("(no pixel hovered)");
}
if (vfb && vfb->fbo) {
ImGui::Text("VFB %dx%d (emulated: %dx%d)", vfb->width, vfb->height, vfb->fbo->Width(), vfb->fbo->Height());
} else {
// Use the swViewer_!
ImGui::Text("Raw FB: %08x (%s)", gstate.getFrameBufRawAddress(), GeBufferFormatToString(gstate.FrameBufFormat()));
}
if (gstate.isModeClear()) {
ImGui::Text("(clear mode - texturing not used)");
} else if (!gstate.isTextureMapEnabled()) {
ImGui::Text("(texturing not enabled");
} else {
TextureCacheCommon *texcache = gpuDebug->GetTextureCacheCommon();
TexCacheEntry *tex = texcache ? texcache->SetTexture() : nullptr;
if (tex) {
ImGui::Text("Texture: ");
texcache->ApplyTexture();
void *nativeView = texcache->GetNativeTextureView(tex, true);
ImTextureID texId = ImGui_ImplThin3d_AddNativeTextureTemp(nativeView);
float texW = dimWidth(tex->dim);
float texH = dimHeight(tex->dim);
const ImVec2 p0 = ImGui::GetCursorScreenPos();
const ImVec2 sz = ImGui::GetContentRegionAvail();
const ImVec2 p1 = ImVec2(p0.x + texW, p0.y + texH);
// Draw border and background color
drawList->PushClipRect(p0, p1, true);
ImGui::Image(texId, ImVec2(texW, texH));
DrawPreviewPrimitive(drawList, p0, previewPrim_, previewIndices_, previewVertices_, previewCount_, true, texW, texH);
drawList->PopClipRect();
} else {
ImGui::Text("(no valid texture bound)");
// In software mode, we should just decode the texture here.
// TODO: List some of the texture params here.
}
}
// Let's display the current CLUT.
}
// Let's display the current CLUT.
} else {
ImGui::Text("Click the buttons above (Tex, etc) to stop");
}