// Copyright (c) 2012- PPSSPP Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "base/basictypes.h" #include "Core/System.h" #include "Windows/resource.h" #include "Windows/GEDebugger/GEDebugger.h" #include "Windows/GEDebugger/TabVertices.h" #include "GPU/Common/VertexDecoderCommon.h" #include "GPU/GPUState.h" #include "GPU/GeDisasm.h" #include "GPU/Common/GPUDebugInterface.h" static const GenericListViewColumn vertexListCols[] = { { L"X", 0.1f }, { L"Y", 0.1f }, { L"Z", 0.1f }, { L"U", 0.1f }, { L"V", 0.1f }, { L"Color", 0.1f }, { L"NX", 0.1f }, { L"NY", 0.1f }, { L"NZ", 0.1f }, // TODO: weight, morph? }; GenericListViewDef vertexListDef = { vertexListCols, ARRAY_SIZE(vertexListCols), NULL, false }; enum VertexListCols { VERTEXLIST_COL_X, VERTEXLIST_COL_Y, VERTEXLIST_COL_Z, VERTEXLIST_COL_U, VERTEXLIST_COL_V, VERTEXLIST_COL_COLOR, VERTEXLIST_COL_NX, VERTEXLIST_COL_NY, VERTEXLIST_COL_NZ, }; static const GenericListViewColumn matrixListCols[] = { { L"Name", 0.24f }, { L"0", 0.19f }, { L"1", 0.19f }, { L"2", 0.19f }, { L"3", 0.19f }, }; GenericListViewDef matrixListDef = { matrixListCols, ARRAY_SIZE(matrixListCols), NULL, false }; enum MatrixListCols { MATRIXLIST_COL_NAME, MATRIXLIST_COL_0, MATRIXLIST_COL_1, MATRIXLIST_COL_2, MATRIXLIST_COL_3, MATRIXLIST_COL_COUNT, }; enum MatrixListRows { MATRIXLIST_ROW_WORLD_0, MATRIXLIST_ROW_WORLD_1, MATRIXLIST_ROW_WORLD_2, MATRIXLIST_ROW_VIEW_0, MATRIXLIST_ROW_VIEW_1, MATRIXLIST_ROW_VIEW_2, MATRIXLIST_ROW_PROJ_0, MATRIXLIST_ROW_PROJ_1, MATRIXLIST_ROW_PROJ_2, MATRIXLIST_ROW_PROJ_3, MATRIXLIST_ROW_TGEN_0, MATRIXLIST_ROW_TGEN_1, MATRIXLIST_ROW_TGEN_2, MATRIXLIST_ROW_BONE_0_0, MATRIXLIST_ROW_BONE_0_1, MATRIXLIST_ROW_BONE_0_2, MATRIXLIST_ROW_BONE_1_0, MATRIXLIST_ROW_BONE_1_1, MATRIXLIST_ROW_BONE_1_2, MATRIXLIST_ROW_BONE_2_0, MATRIXLIST_ROW_BONE_2_1, MATRIXLIST_ROW_BONE_2_2, MATRIXLIST_ROW_BONE_3_0, MATRIXLIST_ROW_BONE_3_1, MATRIXLIST_ROW_BONE_3_2, MATRIXLIST_ROW_BONE_4_0, MATRIXLIST_ROW_BONE_4_1, MATRIXLIST_ROW_BONE_4_2, MATRIXLIST_ROW_BONE_5_0, MATRIXLIST_ROW_BONE_5_1, MATRIXLIST_ROW_BONE_5_2, MATRIXLIST_ROW_BONE_6_0, MATRIXLIST_ROW_BONE_6_1, MATRIXLIST_ROW_BONE_6_2, MATRIXLIST_ROW_BONE_7_0, MATRIXLIST_ROW_BONE_7_1, MATRIXLIST_ROW_BONE_7_2, MATRIXLIST_ROW_COUNT, }; CtrlVertexList::CtrlVertexList(HWND hwnd) : GenericListControl(hwnd, vertexListDef), raw_(false) { decoder = new VertexDecoder(); Update(); } CtrlVertexList::~CtrlVertexList() { delete decoder; } void CtrlVertexList::GetColumnText(wchar_t *dest, int row, int col) { if (row < 0 || row >= rowCount_ ) { wcscpy(dest, L"Invalid"); return; } if (!indices.empty()) { if (row >= (int)indices.size()) { swprintf(dest, L"Invalid indice %d", row); return; } row = indices[row]; } if (raw_) { FormatVertColRaw(dest, row, col); } else { if (row >= (int)vertices.size()) { swprintf(dest, L"Invalid vertex %d", row); return; } FormatVertCol(dest, vertices[row], col); } } void CtrlVertexList::FormatVertCol(wchar_t *dest, const GPUDebugVertex &vert, int col) { switch (col) { case VERTEXLIST_COL_X: swprintf(dest, L"%f", vert.x); break; case VERTEXLIST_COL_Y: swprintf(dest, L"%f", vert.y); break; case VERTEXLIST_COL_Z: swprintf(dest, L"%f", vert.z); break; case VERTEXLIST_COL_U: swprintf(dest, L"%f", vert.u); break; case VERTEXLIST_COL_V: swprintf(dest, L"%f", vert.v); break; case VERTEXLIST_COL_COLOR: swprintf(dest, L"%02x%02x%02x%02x", vert.c[0], vert.c[1], vert.c[2], vert.c[3]); break; case VERTEXLIST_COL_NX: swprintf(dest, L"%f", vert.nx); break; case VERTEXLIST_COL_NY: swprintf(dest, L"%f", vert.ny); break; case VERTEXLIST_COL_NZ: swprintf(dest, L"%f", vert.nz); break; default: wcscpy(dest, L"Invalid"); break; } } void CtrlVertexList::FormatVertColRaw(wchar_t *dest, int row, int col) { auto memLock = Memory::Lock(); if (!PSP_IsInited()) { wcscpy(dest, L"Invalid"); return; } // We could use the vertex decoder and reader, but those already do some minor adjustments. // There's only a few values - let's just go after them directly. const u8 *vert = Memory::GetPointer(gpuDebug->GetVertexAddress()) + row * decoder->size; const u8 *pos = vert + decoder->posoff; const u8 *tc = vert + decoder->tcoff; const u8 *color = vert + decoder->coloff; const u8 *norm = vert + decoder->nrmoff; switch (col) { case VERTEXLIST_COL_X: FormatVertColRawType(dest, pos, decoder->pos, 0); break; case VERTEXLIST_COL_Y: FormatVertColRawType(dest, pos, decoder->pos, 1); break; case VERTEXLIST_COL_Z: FormatVertColRawType(dest, pos, decoder->pos, 2); break; case VERTEXLIST_COL_U: FormatVertColRawType(dest, tc, decoder->tc, 0); break; case VERTEXLIST_COL_V: FormatVertColRawType(dest, tc, decoder->tc, 1); break; case VERTEXLIST_COL_COLOR: FormatVertColRawColor(dest, color, decoder->col); break; case VERTEXLIST_COL_NX: FormatVertColRawType(dest, norm, decoder->nrm, 0); break; case VERTEXLIST_COL_NY: FormatVertColRawType(dest, norm, decoder->nrm, 1); break; case VERTEXLIST_COL_NZ: FormatVertColRawType(dest, norm, decoder->nrm, 2); break; default: wcscpy(dest, L"Invalid"); break; } } void CtrlVertexList::FormatVertColRawType(wchar_t *dest, const void *data, int type, int offset) { switch (type) { case 0: wcscpy(dest, L"-"); break; case 1: // 8-bit swprintf(dest, L"%02x", ((const u8 *)data)[offset]); break; case 2: // 16-bit swprintf(dest, L"%04x", ((const u16_le *)data)[offset]); break; case 3: // float swprintf(dest, L"%f", ((const float *)data)[offset]); break; default: wcscpy(dest, L"Invalid"); break; } } void CtrlVertexList::FormatVertColRawColor(wchar_t *dest, const void *data, int type) { switch (type) { case GE_VTYPE_COL_NONE >> GE_VTYPE_COL_SHIFT: wcscpy(dest, L"-"); break; case GE_VTYPE_COL_565 >> GE_VTYPE_COL_SHIFT: case GE_VTYPE_COL_5551 >> GE_VTYPE_COL_SHIFT: case GE_VTYPE_COL_4444 >> GE_VTYPE_COL_SHIFT: swprintf(dest, L"%04x", *(const u16_le *)data); break; case GE_VTYPE_COL_8888 >> GE_VTYPE_COL_SHIFT: swprintf(dest, L"%08x", *(const u32_le *)data); break; default: wcscpy(dest, L"Invalid"); break; } } int CtrlVertexList::GetRowCount() { auto memLock = Memory::Lock(); if (!PSP_IsInited()) { return 0; } if (!gpuDebug || !Memory::IsValidAddress(gpuDebug->GetVertexAddress())) { rowCount_ = 0; return rowCount_; } // TODO: Maybe there are smarter ways? Also, is this the best place to recalc? auto state = gpuDebug->GetGState(); rowCount_ = state.prim & 0xFFFF; // Override if we're on a prim command. DisplayList list; if (gpuDebug->GetCurrentDisplayList(list)) { u32 cmd = Memory::Read_U32(list.pc); if ((cmd >> 24) == GE_CMD_PRIM) { rowCount_ = cmd & 0xFFFF; } else if ((cmd >> 24) == GE_CMD_BEZIER || (cmd >> 24) == GE_CMD_SPLINE) { u32 u = (cmd & 0x00FF) >> 0; u32 v = (cmd & 0xFF00) >> 8; rowCount_ = u * v; } } if (!gpuDebug->GetCurrentSimpleVertices(rowCount_, vertices, indices)) { rowCount_ = 0; } VertexDecoderOptions options{}; decoder->SetVertexType(state.vertType, options); return rowCount_; } TabVertices::TabVertices(HINSTANCE _hInstance, HWND _hParent) : Dialog((LPCSTR)IDD_GEDBG_TAB_VERTICES, _hInstance, _hParent) { values = new CtrlVertexList(GetDlgItem(m_hDlg, IDC_GEDBG_VERTICES)); } TabVertices::~TabVertices() { delete values; } void TabVertices::UpdateSize(WORD width, WORD height) { struct Position { int x,y; int w,h; }; Position position; static const int borderMargin = 5; static const int checkboxSpace = 22; position.x = borderMargin; position.y = borderMargin + checkboxSpace; position.w = width - 2 * borderMargin; position.h = height - 2 * borderMargin - checkboxSpace; HWND handle = GetDlgItem(m_hDlg, IDC_GEDBG_VERTICES); MoveWindow(handle, position.x, position.y, position.w, position.h, TRUE); } BOOL TabVertices::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_SIZE: UpdateSize(LOWORD(lParam), HIWORD(lParam)); return TRUE; case WM_COMMAND: if (LOWORD(wParam) == IDC_GEDBG_RAWVERTS) { values->SetRaw(IsDlgButtonChecked(m_hDlg, IDC_GEDBG_RAWVERTS) == BST_CHECKED); values->Update(); } return TRUE; case WM_NOTIFY: switch (wParam) { case IDC_GEDBG_VERTICES: values->HandleNotify(lParam); break; } break; } return FALSE; } CtrlMatrixList::CtrlMatrixList(HWND hwnd) : GenericListControl(hwnd, matrixListDef) { Update(); } void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { if (!gpuDebug || row < 0 || row >= MATRIXLIST_ROW_COUNT || col < 0 || col >= MATRIXLIST_COL_COUNT) { wcscpy(dest, L"Invalid"); return; } auto state = gpuDebug->GetGState(); if (row >= MATRIXLIST_ROW_BONE_0_0) { int b = (row - MATRIXLIST_ROW_BONE_0_0) / 3; int r = (row - MATRIXLIST_ROW_BONE_0_0) % 3; int offset = b * 12 + r + (col - 1) * 3; switch (col) { case MATRIXLIST_COL_NAME: swprintf(dest, L"Bone #%d row %d", b, r); break; default: swprintf(dest, L"%f", state.boneMatrix[offset]); break; } } else if (row >= MATRIXLIST_ROW_TGEN_0) { int r = row - MATRIXLIST_ROW_TGEN_0; int offset = r + (col - 1) * 4; switch (col) { case MATRIXLIST_COL_NAME: swprintf(dest, L"Texgen %d", r); break; default: swprintf(dest, L"%f", state.tgenMatrix[offset]); break; } } else if (row >= MATRIXLIST_ROW_PROJ_0) { int r = row - MATRIXLIST_ROW_PROJ_0; int offset = r + (col - 1) * 4; switch (col) { case MATRIXLIST_COL_NAME: swprintf(dest, L"Proj %d", r); break; default: swprintf(dest, L"%f", state.projMatrix[offset]); break; } } else if (row >= MATRIXLIST_ROW_VIEW_0) { int r = row - MATRIXLIST_ROW_VIEW_0; int offset = r + (col - 1) * 3; switch (col) { case MATRIXLIST_COL_NAME: swprintf(dest, L"View %d", r); break; default: swprintf(dest, L"%f", state.viewMatrix[offset]); break; } } else { int r = row - MATRIXLIST_ROW_WORLD_0; int offset = r + (col - 1) * 3; switch (col) { case MATRIXLIST_COL_NAME: swprintf(dest, L"World %d", r); break; default: swprintf(dest, L"%f", state.worldMatrix[offset]); break; } } } int CtrlMatrixList::GetRowCount() { if (!gpuDebug) { return 0; } return MATRIXLIST_ROW_COUNT; } TabMatrices::TabMatrices(HINSTANCE _hInstance, HWND _hParent) : Dialog((LPCSTR)IDD_GEDBG_TAB_MATRICES, _hInstance, _hParent) { values = new CtrlMatrixList(GetDlgItem(m_hDlg, IDC_GEDBG_MATRICES)); } TabMatrices::~TabMatrices() { delete values; } void TabMatrices::UpdateSize(WORD width, WORD height) { struct Position { int x,y; int w,h; }; Position position; static const int borderMargin = 5; position.x = borderMargin; position.y = borderMargin; position.w = width - 2 * borderMargin; position.h = height - 2 * borderMargin; HWND handle = GetDlgItem(m_hDlg, IDC_GEDBG_MATRICES); MoveWindow(handle, position.x, position.y, position.w, position.h, TRUE); } BOOL TabMatrices::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_INITDIALOG: return TRUE; case WM_SIZE: UpdateSize(LOWORD(lParam), HIWORD(lParam)); return TRUE; case WM_NOTIFY: switch (wParam) { case IDC_GEDBG_MATRICES: values->HandleNotify(lParam); break; } break; } return FALSE; }