From b654ee9d440654b98632c8bec3091dd02ad8c53c Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 13 Feb 2022 10:22:38 -0800 Subject: [PATCH 1/4] Debugger: Allow custom draw and generic lists. --- Windows/Debugger/Debugger_Disasm.cpp | 16 +++++++------- Windows/GEDebugger/TabDisplayLists.cpp | 8 +++---- Windows/GEDebugger/TabState.cpp | 4 ++-- Windows/GEDebugger/TabVertices.cpp | 8 +++---- Windows/W32Util/Misc.cpp | 29 ++++++++++++++++++++------ Windows/W32Util/Misc.h | 6 +++++- 6 files changed, 46 insertions(+), 25 deletions(-) diff --git a/Windows/Debugger/Debugger_Disasm.cpp b/Windows/Debugger/Debugger_Disasm.cpp index a42fe32c3259..84ea2f991108 100644 --- a/Windows/Debugger/Debugger_Disasm.cpp +++ b/Windows/Debugger/Debugger_Disasm.cpp @@ -361,17 +361,17 @@ BOOL CDisasm::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) leftTabs->HandleNotify(lParam); break; case IDC_BREAKPOINTLIST: - breakpointList->HandleNotify(lParam); - break; + SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, breakpointList->HandleNotify(lParam)); + return TRUE; case IDC_THREADLIST: - threadList->HandleNotify(lParam); - break; + SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, threadList->HandleNotify(lParam)); + return TRUE; case IDC_STACKFRAMES: - stackTraceView->HandleNotify(lParam); - break; + SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, stackTraceView->HandleNotify(lParam)); + return TRUE; case IDC_MODULELIST: - moduleList->HandleNotify(lParam); - break; + SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, moduleList->HandleNotify(lParam)); + return TRUE; case IDC_DEBUG_BOTTOMTABS: bottomTabs->HandleNotify(lParam); break; diff --git a/Windows/GEDebugger/TabDisplayLists.cpp b/Windows/GEDebugger/TabDisplayLists.cpp index fd4e615ad2b2..3a622a044274 100644 --- a/Windows/GEDebugger/TabDisplayLists.cpp +++ b/Windows/GEDebugger/TabDisplayLists.cpp @@ -258,11 +258,11 @@ BOOL TabDisplayLists::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (wParam) { case IDC_GEDBG_LISTS_STACK: - stack->HandleNotify(lParam); - break; + SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, stack->HandleNotify(lParam)); + return TRUE; case IDC_GEDBG_LISTS_ALLLISTS: - allLists->HandleNotify(lParam); - break; + SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, allLists->HandleNotify(lParam)); + return TRUE; } break; diff --git a/Windows/GEDebugger/TabState.cpp b/Windows/GEDebugger/TabState.cpp index 1a67b43b6417..0e30def5cbae 100644 --- a/Windows/GEDebugger/TabState.cpp +++ b/Windows/GEDebugger/TabState.cpp @@ -1054,8 +1054,8 @@ BOOL TabStateValues::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (wParam) { case IDC_GEDBG_VALUES: - values->HandleNotify(lParam); - break; + SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, values->HandleNotify(lParam)); + return TRUE; } break; } diff --git a/Windows/GEDebugger/TabVertices.cpp b/Windows/GEDebugger/TabVertices.cpp index dbd96c246448..24215e23077d 100644 --- a/Windows/GEDebugger/TabVertices.cpp +++ b/Windows/GEDebugger/TabVertices.cpp @@ -355,8 +355,8 @@ BOOL TabVertices::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (wParam) { case IDC_GEDBG_VERTICES: - values->HandleNotify(lParam); - break; + SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, values->HandleNotify(lParam)); + return TRUE; } break; } @@ -652,8 +652,8 @@ BOOL TabMatrices::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { switch (wParam) { case IDC_GEDBG_MATRICES: - values->HandleNotify(lParam); - break; + SetWindowLongPtr(m_hDlg, DWLP_MSGRESULT, values->HandleNotify(lParam)); + return TRUE; } break; } diff --git a/Windows/W32Util/Misc.cpp b/Windows/W32Util/Misc.cpp index c2b100279542..02a234eb50c5 100644 --- a/Windows/W32Util/Misc.cpp +++ b/Windows/W32Util/Misc.cpp @@ -215,8 +215,7 @@ void GenericListControl::SetIconList(int w, int h, const std::vector &ico ListView_SetImageList(handle, (HIMAGELIST)images_, LVSIL_STATE); } -void GenericListControl::HandleNotify(LPARAM lParam) -{ +int GenericListControl::HandleNotify(LPARAM lParam) { LPNMHDR mhdr = (LPNMHDR) lParam; if (mhdr->code == NM_DBLCLK) @@ -224,7 +223,7 @@ void GenericListControl::HandleNotify(LPARAM lParam) LPNMITEMACTIVATE item = (LPNMITEMACTIVATE) lParam; if ((item->iItem != -1 && item->iItem < GetRowCount()) || sendInvalidRows) OnDoubleClick(item->iItem,item->iSubItem); - return; + return 0; } if (mhdr->code == NM_RCLICK) @@ -232,7 +231,23 @@ void GenericListControl::HandleNotify(LPARAM lParam) const LPNMITEMACTIVATE item = (LPNMITEMACTIVATE)lParam; if ((item->iItem != -1 && item->iItem < GetRowCount()) || sendInvalidRows) OnRightClick(item->iItem,item->iSubItem,item->ptAction); - return; + return 0; + } + + if (mhdr->code == NM_CUSTOMDRAW && ListenRowPrePaint()) { + LPNMLVCUSTOMDRAW msg = (LPNMLVCUSTOMDRAW)lParam; + switch (msg->nmcd.dwDrawStage) { + case CDDS_PREPAINT: + return CDRF_NOTIFYITEMDRAW; + + case CDDS_ITEMPREPAINT: + if (OnRowPrePaint((int)msg->nmcd.dwItemSpec, msg)) { + return CDRF_NEWFONT; + } + return CDRF_DODEFAULT; + } + + return CDRF_DODEFAULT; } if (mhdr->code == LVN_GETDISPINFO) @@ -247,7 +262,7 @@ void GenericListControl::HandleNotify(LPARAM lParam) dispInfo->item.pszText = stringBuffer; dispInfo->item.mask |= LVIF_TEXT; - return; + return 0; } // handle checkboxes @@ -263,8 +278,10 @@ void GenericListControl::HandleNotify(LPARAM lParam) OnToggle(item->iItem,newImage == 2); } - return; + return 0; } + + return 0; } void GenericListControl::Update() { diff --git a/Windows/W32Util/Misc.h b/Windows/W32Util/Misc.h index c0ce2c33aab7..e3c8630fc040 100644 --- a/Windows/W32Util/Misc.h +++ b/Windows/W32Util/Misc.h @@ -33,6 +33,7 @@ struct GenericListViewDef #define GLVC_CENTERED 1 +typedef struct tagNMLVCUSTOMDRAW *LPNMLVCUSTOMDRAW; // the most significant bit states whether the key is currently down. // simply checking if it's != 0 is not enough, as bit0 is set if @@ -44,7 +45,7 @@ class GenericListControl public: GenericListControl(HWND hwnd, const GenericListViewDef& def); virtual ~GenericListControl(); - void HandleNotify(LPARAM lParam); + int HandleNotify(LPARAM lParam); void Update(); int GetSelectedIndex(); HWND GetHandle() { return handle; }; @@ -62,6 +63,9 @@ class GenericListControl virtual void CopyRows(int start, int size); virtual void OnToggle(int item, bool newValue) { }; + virtual bool ListenRowPrePaint() { return false; } + virtual bool OnRowPrePaint(int row, LPNMLVCUSTOMDRAW msg) { return false; } + private: static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); void ProcessUpdate(); From 71e855a822b5fbd08fbdd02a78cdb0ea85398ea4 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 13 Feb 2022 10:22:59 -0800 Subject: [PATCH 2/4] GE Debugger: Track last gstate on stepping. --- GPU/Debugger/Stepping.cpp | 36 ++++++++++++++++++++++++++---------- GPU/Debugger/Stepping.h | 3 +++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp index ea3ce03715f4..2b750235a71a 100644 --- a/GPU/Debugger/Stepping.cpp +++ b/GPU/Debugger/Stepping.cpp @@ -65,6 +65,8 @@ static GPUDebugBuffer bufferClut; static int bufferLevel; static u32 pauseSetCmdValue; +static GPUgstate lastGState; + static void SetPauseAction(PauseAction act, bool waitComplete = true) { pauseLock.lock(); std::unique_lock guard(actionLock); @@ -133,6 +135,22 @@ static void RunPauseAction() { pauseAction = PAUSE_BREAK; } +static void StartStepping() { + if (lastGState.cmdmem[1] == 0) { + lastGState = gstate; + // Play it safe so we don't keep resetting. + lastGState.cmdmem[1] |= 0x01000000; + } + gpuDebug->NotifySteppingEnter(); + isStepping = true; +} + +static void StopStepping() { + gpuDebug->NotifySteppingExit(); + lastGState = gstate; + isStepping = false; +} + bool SingleStep() { std::unique_lock guard(pauseLock); if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME && coreState != CORE_STEPPING) { @@ -147,13 +165,9 @@ bool SingleStep() { return false; } - gpuDebug->NotifySteppingEnter(); - isStepping = true; - + StartStepping(); RunPauseAction(); - - gpuDebug->NotifySteppingExit(); - isStepping = false; + StopStepping(); return true; } @@ -171,13 +185,12 @@ bool EnterStepping() { return false; } - gpuDebug->NotifySteppingEnter(); + StartStepping(); // Just to be sure. if (pauseAction == PAUSE_CONTINUE) { pauseAction = PAUSE_BREAK; } - isStepping = true; stepCounter++; do { @@ -185,8 +198,7 @@ bool EnterStepping() { pauseWait.wait(guard); } while (pauseAction != PAUSE_CONTINUE); - gpuDebug->NotifySteppingExit(); - isStepping = false; + StopStepping(); return true; } @@ -263,4 +275,8 @@ void ForceUnpause() { actionWait.notify_all(); } +GPUgstate LastState() { + return lastGState; +} + } // namespace diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h index af93743eb3c0..35409c03b95b 100644 --- a/GPU/Debugger/Stepping.h +++ b/GPU/Debugger/Stepping.h @@ -22,6 +22,7 @@ #include "Common/CommonTypes.h" #include "Core/Core.h" #include "GPU/Common/GPUDebugInterface.h" +#include "GPU/GPUState.h" namespace GPUStepping { // Should be called from the emu thread. @@ -42,4 +43,6 @@ namespace GPUStepping { void ResumeFromStepping(); void ForceUnpause(); + + GPUgstate LastState(); }; From 8ffef9dd1eb4265b288d88a5bb4a5110af537d1d Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 13 Feb 2022 10:33:19 -0800 Subject: [PATCH 3/4] GE Debugger: Highlight changed state values. This way it's easy to tell while stepping what is different. Especially useful in the watch tab. --- Windows/GEDebugger/TabState.cpp | 28 ++++++++++++++++++++++++++++ Windows/GEDebugger/TabState.h | 4 ++++ 2 files changed, 32 insertions(+) diff --git a/Windows/GEDebugger/TabState.cpp b/Windows/GEDebugger/TabState.cpp index 0e30def5cbae..486e6030b4ff 100644 --- a/Windows/GEDebugger/TabState.cpp +++ b/Windows/GEDebugger/TabState.cpp @@ -15,8 +15,10 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include "Common/CommonFuncs.h" #include "Common/CommonTypes.h" +#include "Common/Log.h" #include "Common/Data/Text/Parsers.h" #include "Common/StringUtils.h" #include "Windows/resource.h" @@ -28,6 +30,7 @@ #include "GPU/GeDisasm.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Debugger/Breakpoints.h" +#include "GPU/Debugger/Stepping.h" using namespace GPUBreakpoints; @@ -1009,11 +1012,36 @@ void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) { } } +bool CtrlStateValues::OnRowPrePaint(int row, LPNMLVCUSTOMDRAW msg) { + if (RowValuesChanged(row)) { + msg->clrText = RGB(255, 0, 0); + return true; + } + return false; +} + void CtrlStateValues::SetCmdValue(u32 op) { SendMessage(GetParent(GetParent(GetHandle())), WM_GEDBG_SETCMDWPARAM, op, NULL); Update(); } +bool CtrlStateValues::RowValuesChanged(int row) { + _assert_(gpuDebug != nullptr && row >= 0 && row < rowCount_); + + const auto info = rows_[row]; + const auto state = gpuDebug->GetGState(); + const auto lastState = GPUStepping::LastState(); + + if (state.cmdmem[info.cmd] != lastState.cmdmem[info.cmd]) + return true; + if (info.otherCmd && state.cmdmem[info.otherCmd] != lastState.cmdmem[info.otherCmd]) + return true; + if (info.otherCmd2 && state.cmdmem[info.otherCmd2] != lastState.cmdmem[info.otherCmd2]) + return true; + + return false; +} + TabStateValues::TabStateValues(const TabStateRow *rows, int rowCount, LPCSTR dialogID, HINSTANCE _hInstance, HWND _hParent) : Dialog(dialogID, _hInstance, _hParent) { values = new CtrlStateValues(rows, rowCount, GetDlgItem(m_hDlg, IDC_GEDBG_VALUES)); diff --git a/Windows/GEDebugger/TabState.h b/Windows/GEDebugger/TabState.h index 03ecb4e59493..c60c8490288e 100644 --- a/Windows/GEDebugger/TabState.h +++ b/Windows/GEDebugger/TabState.h @@ -41,7 +41,11 @@ class CtrlStateValues: public GenericListControl { void OnDoubleClick(int row, int column) override; void OnRightClick(int row, int column, const POINT& point) override; + bool ListenRowPrePaint() override { return true; } + bool OnRowPrePaint(int row, LPNMLVCUSTOMDRAW msg) override; + private: + bool RowValuesChanged(int row); void SetCmdValue(u32 op); const TabStateRow *rows_; From 957e15f23a9f5671c4fc9ec867969105b9ee4ccc Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 13 Feb 2022 10:53:01 -0800 Subject: [PATCH 4/4] GE Debugger: Highlight changed matrix values. --- Windows/GEDebugger/TabVertices.cpp | 55 +++++++++++++++++++++++++++--- Windows/GEDebugger/TabVertices.h | 6 +++- Windows/W32Util/Misc.cpp | 8 ++++- Windows/W32Util/Misc.h | 2 ++ 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/Windows/GEDebugger/TabVertices.cpp b/Windows/GEDebugger/TabVertices.cpp index 24215e23077d..0576b96032a1 100644 --- a/Windows/GEDebugger/TabVertices.cpp +++ b/Windows/GEDebugger/TabVertices.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include "Common/CommonTypes.h" #include "Common/StringUtils.h" #include "Core/System.h" @@ -28,6 +29,7 @@ #include "GPU/GeDisasm.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Debugger/Breakpoints.h" +#include "GPU/Debugger/Stepping.h" static const GenericListViewColumn vertexListCols[] = { { L"X", 0.1f }, @@ -370,14 +372,57 @@ CtrlMatrixList::CtrlMatrixList(HWND hwnd) Update(); } -bool CtrlMatrixList::GetValue(int row, int col, float &val) { +bool CtrlMatrixList::OnColPrePaint(int row, int col, LPNMLVCUSTOMDRAW msg) { + const auto state = gpuDebug->GetGState(); + const auto lastState = GPUStepping::LastState(); + + bool changed = false; + if (col < MATRIXLIST_COL_0) { + for (int c = MATRIXLIST_COL_0; c <= MATRIXLIST_COL_3; ++c) { + changed = changed || ColChanged(lastState, state, row, c); + } + } else { + changed = ColChanged(lastState, state, row, col); + } + + // At the column level, we have to reset the color back. + static int lastRow = -1; + static COLORREF rowDefaultText; + if (lastRow != row) { + rowDefaultText = msg->clrText; + lastRow = row; + } + + if (changed) { + msg->clrText = RGB(255, 0, 0); + return true; + } else if (msg->clrText != rowDefaultText) { + msg->clrText = rowDefaultText; + return true; + } + + return false; +} + +bool CtrlMatrixList::ColChanged(const GPUgstate &lastState, const GPUgstate &state, int row, int col) { + union { + float f; + uint32_t u; + } newVal, oldVal; + if (!GetValue(state, row, col, newVal.f) || !GetValue(lastState, row, col, oldVal.f)) + return false; + + // If there's any difference in bits, highlight. + return newVal.u != oldVal.u; +} + +bool CtrlMatrixList::GetValue(const GPUgstate &state, int row, int col, float &val) { if (!gpuDebug || row < 0 || row >= MATRIXLIST_ROW_COUNT || col < 0 || col >= MATRIXLIST_COL_COUNT) return false; if (col < MATRIXLIST_COL_0) col = MATRIXLIST_COL_0; - 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; @@ -419,7 +464,7 @@ void CtrlMatrixList::GetColumnText(wchar_t *dest, int row, int col) { } float val; - if (!GetValue(row, col, val)) { + if (!GetValue(gpuDebug->GetGState(), row, col, val)) { wcscpy(dest, L"Invalid"); return; } @@ -534,7 +579,7 @@ void CtrlMatrixList::OnDoubleClick(int row, int column) { } float val; - if (!GetValue(row, column, val)) + if (!GetValue(gpuDebug->GetGState(), row, column, val)) return; std::string strvalue = StringFromFormat("%f", val); @@ -594,7 +639,7 @@ void CtrlMatrixList::OnRightClick(int row, int column, const POINT &point) { case ID_DISASM_COPYINSTRUCTIONDISASM: { float val; - if (GetValue(row, column, val)) { + if (GetValue(gpuDebug->GetGState(), row, column, val)) { wchar_t dest[512]; swprintf(dest, 511, L"%f", val); W32Util::CopyTextToClipboard(GetHandle(), dest); diff --git a/Windows/GEDebugger/TabVertices.h b/Windows/GEDebugger/TabVertices.h index 1b02d734f804..116f60f06ce2 100644 --- a/Windows/GEDebugger/TabVertices.h +++ b/Windows/GEDebugger/TabVertices.h @@ -83,8 +83,12 @@ class CtrlMatrixList: public GenericListControl { void OnDoubleClick(int row, int column) override; void OnRightClick(int row, int column, const POINT &point) override; + bool ListenColPrePaint() override { return true; } + bool OnColPrePaint(int row, int col, LPNMLVCUSTOMDRAW msg) override; + private: - bool GetValue(int row, int col, float &val); + bool GetValue(const GPUgstate &state, int row, int col, float &val); + bool ColChanged(const GPUgstate &lastState, const GPUgstate &state, int row, int col); void ToggleBreakpoint(int row); }; diff --git a/Windows/W32Util/Misc.cpp b/Windows/W32Util/Misc.cpp index 02a234eb50c5..37bdfd475e86 100644 --- a/Windows/W32Util/Misc.cpp +++ b/Windows/W32Util/Misc.cpp @@ -234,7 +234,7 @@ int GenericListControl::HandleNotify(LPARAM lParam) { return 0; } - if (mhdr->code == NM_CUSTOMDRAW && ListenRowPrePaint()) { + if (mhdr->code == NM_CUSTOMDRAW && (ListenRowPrePaint() || ListenColPrePaint())) { LPNMLVCUSTOMDRAW msg = (LPNMLVCUSTOMDRAW)lParam; switch (msg->nmcd.dwDrawStage) { case CDDS_PREPAINT: @@ -244,6 +244,12 @@ int GenericListControl::HandleNotify(LPARAM lParam) { if (OnRowPrePaint((int)msg->nmcd.dwItemSpec, msg)) { return CDRF_NEWFONT; } + return ListenColPrePaint() ? CDRF_NOTIFYSUBITEMDRAW : CDRF_DODEFAULT; + + case CDDS_SUBITEM | CDDS_ITEMPREPAINT: + if (OnColPrePaint((int)msg->nmcd.dwItemSpec, msg->iSubItem, msg)) { + return CDRF_NEWFONT; + } return CDRF_DODEFAULT; } diff --git a/Windows/W32Util/Misc.h b/Windows/W32Util/Misc.h index e3c8630fc040..8a43c55b593e 100644 --- a/Windows/W32Util/Misc.h +++ b/Windows/W32Util/Misc.h @@ -64,7 +64,9 @@ class GenericListControl virtual void OnToggle(int item, bool newValue) { }; virtual bool ListenRowPrePaint() { return false; } + virtual bool ListenColPrePaint() { return false; } virtual bool OnRowPrePaint(int row, LPNMLVCUSTOMDRAW msg) { return false; } + virtual bool OnColPrePaint(int row, int col, LPNMLVCUSTOMDRAW msg) { return false; } private: static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);