diff --git a/GPU/Debugger/Breakpoints.cpp b/GPU/Debugger/Breakpoints.cpp index c020b5585f82..8c3e9c5e28ba 100644 --- a/GPU/Debugger/Breakpoints.cpp +++ b/GPU/Debugger/Breakpoints.cpp @@ -27,14 +27,17 @@ static recursive_mutex breaksLock; static std::vector breakCmds; static std::set breakPCs; static std::set breakTextures; +static std::set breakRenderTargets; // Small optimization to avoid a lock/lookup for the common case. static size_t breakPCsCount = 0; static size_t breakTexturesCount = 0; +static size_t breakRenderTargetsCount = 0; // If these are set, the above are also, but they should be temporary. static std::vector breakCmdsTemp; static std::set breakPCsTemp; static std::set breakTexturesTemp; +static std::set breakRenderTargetsTemp; static bool textureChangeTemp = false; static u32 lastTexture = 0xFFFFFFFF; @@ -96,6 +99,17 @@ u32 GetAdjustedTextureAddress(u32 op) { return addr; } +u32 GetAdjustedRenderTargetAddress(u32 op) { + const u8 cmd = op >> 24; + switch (cmd) { + case GE_CMD_FRAMEBUFPTR: + case GE_CMD_ZBUFPTR: + return op & 0x003FFFF0; + } + + return (u32)-1; +} + bool IsTextureChangeBreakpoint(u32 op, u32 addr) { if (!textureChangeTemp) { return false; @@ -131,6 +145,14 @@ bool IsTextureCmdBreakpoint(u32 op) { } } +bool IsRenderTargetCmdBreakpoint(u32 op) { + const u32 addr = GetAdjustedRenderTargetAddress(op); + if (addr != (u32)-1) { + return IsRenderTargetBreakpoint(addr); + } + return false; +} + bool IsBreakpoint(u32 pc, u32 op) { if (IsAddressBreakpoint(pc) || IsOpBreakpoint(op)) { return true; @@ -140,6 +162,9 @@ bool IsBreakpoint(u32 pc, u32 op) { // Break on the next non-texture. AddNonTextureTempBreakpoints(); } + if (breakRenderTargetsCount != 0 && IsRenderTargetCmdBreakpoint(op)) { + return true; + } return false; } @@ -184,6 +209,38 @@ bool IsTextureBreakpoint(u32 addr) { return breakTextures.find(addr) != breakTextures.end(); } +bool IsRenderTargetBreakpoint(u32 addr, bool &temp) { + if (breakRenderTargetsCount == 0) { + temp = false; + return false; + } + + addr &= 0x003FFFF0; + + lock_guard guard(breaksLock); + temp = breakRenderTargetsTemp.find(addr) != breakRenderTargetsTemp.end(); + return breakRenderTargets.find(addr) != breakRenderTargets.end(); +} + +bool IsRenderTargetBreakpoint(u32 addr) { + if (breakRenderTargetsCount == 0) { + return false; + } + + addr &= 0x003FFFF0; + + lock_guard guard(breaksLock); + return breakRenderTargets.find(addr) != breakRenderTargets.end(); +} + +bool IsOpBreakpoint(u32 op, bool &temp) { + return IsCmdBreakpoint(op >> 24, temp); +} + +bool IsOpBreakpoint(u32 op) { + return IsCmdBreakpoint(op >> 24); +} + bool IsCmdBreakpoint(u8 cmd, bool &temp) { temp = breakCmdsTemp[cmd]; return breakCmds[cmd]; @@ -241,6 +298,24 @@ void AddTextureBreakpoint(u32 addr, bool temp) { breakTexturesCount = breakTextures.size(); } +void AddRenderTargetBreakpoint(u32 addr, bool temp) { + lock_guard guard(breaksLock); + + addr &= 0x003FFFF0; + + if (temp) { + if (breakRenderTargets.find(addr) == breakRenderTargets.end()) { + breakRenderTargetsTemp.insert(addr); + breakRenderTargets.insert(addr); + } + } else { + breakRenderTargetsTemp.erase(addr); + breakRenderTargets.insert(addr); + } + + breakRenderTargetsCount = breakRenderTargets.size(); +} + void AddTextureChangeTempBreakpoint() { textureChangeTemp = true; } @@ -268,6 +343,17 @@ void RemoveTextureBreakpoint(u32 addr) { breakTexturesCount = breakTextures.size(); } +void RemoveRenderTargetBreakpoint(u32 addr) { + lock_guard guard(breaksLock); + + addr &= 0x003FFFF0; + + breakRenderTargetsTemp.erase(addr); + breakRenderTargets.erase(addr); + + breakRenderTargetsCount = breakRenderTargets.size(); +} + void RemoveTextureChangeTempBreakpoint() { textureChangeTemp = false; } @@ -283,14 +369,17 @@ void ClearAllBreakpoints() { breakCmds.resize(256, false); breakPCs.clear(); breakTextures.clear(); + breakRenderTargets.clear(); breakCmdsTemp.clear(); breakCmdsTemp.resize(256, false); breakPCsTemp.clear(); breakTexturesTemp.clear(); + breakRenderTargetsTemp.clear(); breakPCsCount = breakPCs.size(); breakTexturesCount = breakTextures.size(); + breakRenderTargetsCount = breakRenderTargets.size(); textureChangeTemp = false; } @@ -316,7 +405,13 @@ void ClearTempBreakpoints() { breakTextures.erase(*it); } breakTexturesTemp.clear(); - breakPCsCount = breakTextures.size(); + breakTexturesCount = breakTextures.size(); + + for (auto it = breakRenderTargetsTemp.begin(), end = breakRenderTargetsTemp.end(); it != end; ++it) { + breakRenderTargets.erase(*it); + } + breakRenderTargetsTemp.clear(); + breakRenderTargetsCount = breakRenderTargets.size(); textureChangeTemp = false; } diff --git a/GPU/Debugger/Breakpoints.h b/GPU/Debugger/Breakpoints.h index 3381d6f463f7..282b325dd211 100644 --- a/GPU/Debugger/Breakpoints.h +++ b/GPU/Debugger/Breakpoints.h @@ -30,27 +30,27 @@ namespace GPUBreakpoints { bool IsCmdBreakpoint(u8 cmd); bool IsTextureBreakpoint(u32 addr, bool &temp); bool IsTextureBreakpoint(u32 addr); + bool IsRenderTargetBreakpoint(u32 addr, bool &temp); + bool IsRenderTargetBreakpoint(u32 addr); void AddAddressBreakpoint(u32 addr, bool temp = false); void AddCmdBreakpoint(u8 cmd, bool temp = false); void AddTextureBreakpoint(u32 addr, bool temp = false); void AddTextureChangeTempBreakpoint(); + void AddRenderTargetBreakpoint(u32 addr, bool temp = false); void RemoveAddressBreakpoint(u32 addr); void RemoveCmdBreakpoint(u8 cmd); void RemoveTextureBreakpoint(u32 addr); void RemoveTextureChangeTempBreakpoint(); + void RemoveRenderTargetBreakpoint(u32 addr); void UpdateLastTexture(u32 addr); void ClearAllBreakpoints(); void ClearTempBreakpoints(); - static inline bool IsOpBreakpoint(u32 op, bool &temp) { - return IsCmdBreakpoint(op >> 24, temp); - } + bool IsOpBreakpoint(u32 op, bool &temp); - static inline bool IsOpBreakpoint(u32 op) { - return IsCmdBreakpoint(op >> 24); - } + bool IsOpBreakpoint(u32 op); }; diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index b9ae191b556a..6b3d707cf8ff 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -113,6 +113,9 @@ CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent) lists = new TabDisplayLists(_hInstance, m_hDlg); tabs->AddTabDialog(lists, L"Lists"); + watch = new TabStateWatch(_hInstance, m_hDlg); + tabs->AddTabDialog(watch, L"Watch"); + tabs->ShowTab(0, true); // set window position @@ -135,6 +138,7 @@ CGEDebugger::~CGEDebugger() { delete vertices; delete matrices; delete lists; + delete watch; delete tabs; delete fbTabs; } @@ -225,6 +229,7 @@ void CGEDebugger::UpdatePreviews() { vertices->Update(); matrices->Update(); lists->Update(); + watch->Update(); } u32 CGEDebugger::TexturePreviewFlags(const GPUgstate &state) { @@ -658,6 +663,25 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { } break; + case IDC_GEDBG_BREAKTARGET: + { + attached = true; + if (!gpuDebug) { + break; + } + const auto state = gpuDebug->GetGState(); + u32 fbAddr = state.getFrameBufRawAddress(); + // TODO: Better interface that allows add/remove or something. + if (InputBox_GetHex(GetModuleHandle(NULL), m_hDlg, L"Framebuffer Address", fbAddr, fbAddr)) { + if (IsRenderTargetBreakpoint(fbAddr)) { + RemoveRenderTargetBreakpoint(fbAddr); + } else { + AddRenderTargetBreakpoint(fbAddr); + } + } + } + break; + case IDC_GEDBG_TEXLEVELDOWN: UpdateTextureLevel(textureLevel_ - 1); if (attached && gpuDebug != nullptr) { @@ -747,6 +771,12 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { GPU_SetCmdValue((u32)wParam); } break; + + case WM_GEDBG_UPDATE_WATCH: + // Just a notification to update. + if (watch) + watch->Update(); + break; } return FALSE; diff --git a/Windows/GEDebugger/GEDebugger.h b/Windows/GEDebugger/GEDebugger.h index 3d0ee1ef202c..ae71c929f7f8 100644 --- a/Windows/GEDebugger/GEDebugger.h +++ b/Windows/GEDebugger/GEDebugger.h @@ -32,6 +32,7 @@ enum { WM_GEDBG_TOGGLEPCBREAKPOINT, WM_GEDBG_RUNTOWPARAM, WM_GEDBG_SETCMDWPARAM, + WM_GEDBG_UPDATE_WATCH, }; enum BreakNextType { @@ -52,6 +53,7 @@ class TabStateTexture; class TabStateSettings; class TabVertices; class TabMatrices; +class TabStateWatch; struct GPUgstate; class CGEDebugger : public Dialog { @@ -94,6 +96,7 @@ class CGEDebugger : public Dialog { TabMatrices *matrices; SimpleGLWindow *primaryWindow; SimpleGLWindow *secondWindow; + TabStateWatch *watch; TabControl *tabs; TabControl *fbTabs; int textureLevel_; diff --git a/Windows/GEDebugger/TabState.cpp b/Windows/GEDebugger/TabState.cpp index 8388c1a62e0b..0207ffcbdadd 100644 --- a/Windows/GEDebugger/TabState.cpp +++ b/Windows/GEDebugger/TabState.cpp @@ -17,12 +17,18 @@ #include "base/basictypes.h" #include "Windows/resource.h" +#include "Windows/main.h" #include "Windows/InputBox.h" #include "Windows/GEDebugger/GEDebugger.h" #include "Windows/GEDebugger/TabState.h" #include "GPU/GPUState.h" #include "GPU/GeDisasm.h" #include "GPU/Common/GPUDebugInterface.h" +#include "GPU/Debugger/Breakpoints.h" + +using namespace GPUBreakpoints; + +const int POPUP_SUBMENU_ID_GEDBG_STATE = 9; // TODO: Show an icon or something for breakpoints, toggle. static const GenericListViewColumn stateValuesCols[] = { @@ -262,6 +268,19 @@ static const TabStateRow stateSettingsRows[] = { // GE_CMD_TRANSFERSTART, // GE_CMD_UNKNOWN_* +static std::vector watchList; + +static void ToggleWatchList(const TabStateRow &info) { + for (size_t i = 0; i < watchList.size(); ++i) { + if (watchList[i].cmd == info.cmd) { + watchList.erase(watchList.begin() + i); + return; + } + } + + watchList.push_back(info); +} + CtrlStateValues::CtrlStateValues(const TabStateRow *rows, int rowCount, HWND hwnd) : GenericListControl(hwnd, stateValuesListDef), rows_(rows), rowCount_(rowCount) { @@ -844,17 +863,82 @@ void CtrlStateValues::OnDoubleClick(int row, int column) { } } -void CtrlStateValues::OnRightClick(int row, int column, const POINT& point) { - if (gpuDebug == NULL) { +void CtrlStateValues::OnRightClick(int row, int column, const POINT &point) { + if (gpuDebug == nullptr) { return; } - // TODO: Copy, break, watch... enable? + const auto info = rows_[row]; + const auto state = gpuDebug->GetGState(); + + POINT screenPt(point); + ClientToScreen(GetHandle(), &screenPt); + + HMENU subMenu = GetSubMenu(g_hPopupMenus, POPUP_SUBMENU_ID_GEDBG_STATE); + SetMenuDefaultItem(subMenu, ID_REGLIST_CHANGE, FALSE); + + // Ehh, kinda ugly. + if (rows_ == &watchList[0]) { + ModifyMenu(subMenu, ID_GEDBG_WATCH, MF_BYCOMMAND | MF_STRING, ID_GEDBG_WATCH, L"Remove Watch"); + } else { + ModifyMenu(subMenu, ID_GEDBG_WATCH, MF_BYCOMMAND | MF_STRING, ID_GEDBG_WATCH, L"Add Watch"); + } + + switch (TrackPopupMenuEx(subMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, screenPt.x, screenPt.y, GetHandle(), 0)) + { + case ID_DISASM_TOGGLEBREAKPOINT: + if (IsCmdBreakpoint(info.cmd)) { + RemoveCmdBreakpoint(info.cmd); + RemoveCmdBreakpoint(info.otherCmd); + RemoveCmdBreakpoint(info.otherCmd2); + } else { + AddCmdBreakpoint(info.cmd); + if (info.otherCmd) { + AddCmdBreakpoint(info.otherCmd); + } + if (info.otherCmd2) { + AddCmdBreakpoint(info.otherCmd2); + } + } + break; + + case ID_DISASM_COPYINSTRUCTIONHEX: { + char temp[16]; + snprintf(temp, sizeof(temp), "%08x", gstate.cmdmem[info.cmd] & 0x00FFFFFF); + W32Util::CopyTextToClipboard(GetHandle(), temp); + break; + } + + case ID_DISASM_COPYINSTRUCTIONDISASM: { + const bool enabled = info.enableCmd == 0 || (state.cmdmem[info.enableCmd] & 1) == 1; + const u32 value = state.cmdmem[info.cmd] & 0xFFFFFF; + const u32 otherValue = state.cmdmem[info.otherCmd] & 0xFFFFFF; + const u32 otherValue2 = state.cmdmem[info.otherCmd2] & 0xFFFFFF; + + wchar_t dest[512]; + FormatStateRow(dest, info, value, enabled, otherValue, otherValue2); + W32Util::CopyTextToClipboard(GetHandle(), dest); + break; + } + + case ID_GEDBG_COPYALL: + CopyRows(0, GetRowCount()); + break; + + case ID_REGLIST_CHANGE: + OnDoubleClick(row, column); + break; + + case ID_GEDBG_WATCH: + ToggleWatchList(info); + SendMessage(GetParent(GetParent(GetHandle())), WM_GEDBG_UPDATE_WATCH, 0, 0); + break; + } } void CtrlStateValues::SetCmdValue(u32 op) { SendMessage(GetParent(GetParent(GetHandle())), WM_GEDBG_SETCMDWPARAM, op, NULL); - Update(); + Update(); } TabStateValues::TabStateValues(const TabStateRow *rows, int rowCount, LPCSTR dialogID, HINSTANCE _hInstance, HWND _hParent) @@ -921,3 +1005,12 @@ TabStateSettings::TabStateSettings(HINSTANCE _hInstance, HWND _hParent) TabStateTexture::TabStateTexture(HINSTANCE _hInstance, HWND _hParent) : TabStateValues(stateTextureRows, ARRAY_SIZE(stateTextureRows), (LPCSTR)IDD_GEDBG_TAB_VALUES, _hInstance, _hParent) { } + +TabStateWatch::TabStateWatch(HINSTANCE _hInstance, HWND _hParent) + : TabStateValues(&watchList[0], 0, (LPCSTR)IDD_GEDBG_TAB_VALUES, _hInstance, _hParent) { +} + +void TabStateWatch::Update() { + values->UpdateRows(&watchList[0], (int)watchList.size()); + TabStateValues::Update(); +} diff --git a/Windows/GEDebugger/TabState.h b/Windows/GEDebugger/TabState.h index c31ef8bfe39e..03ecb4e59493 100644 --- a/Windows/GEDebugger/TabState.h +++ b/Windows/GEDebugger/TabState.h @@ -26,12 +26,20 @@ class CtrlStateValues: public GenericListControl { public: CtrlStateValues(const TabStateRow *rows, int rowCount, HWND hwnd); + // Used by watch. + void UpdateRows(const TabStateRow *rows, int rowCount) { + rows_ = rows; + rowCount_ = rowCount; + } + protected: - virtual bool WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT& returnValue) { return false; }; - virtual void GetColumnText(wchar_t* dest, int row, int col); - virtual int GetRowCount() { return rowCount_; } - virtual void OnDoubleClick(int row, int column); - virtual void OnRightClick(int row, int column, const POINT& point); + bool WindowMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT& returnValue) override { + return false; + } + void GetColumnText(wchar_t* dest, int row, int col) override; + int GetRowCount() override { return rowCount_; } + void OnDoubleClick(int row, int column) override; + void OnRightClick(int row, int column, const POINT& point) override; private: void SetCmdValue(u32 op); @@ -52,10 +60,10 @@ class TabStateValues : public Dialog { protected: BOOL DlgProc(UINT message, WPARAM wParam, LPARAM lParam); + CtrlStateValues *values; + private: void UpdateSize(WORD width, WORD height); - - CtrlStateValues *values; }; class TabStateFlags : public TabStateValues { @@ -77,3 +85,10 @@ class TabStateTexture : public TabStateValues { public: TabStateTexture(HINSTANCE _hInstance, HWND _hParent); }; + +class TabStateWatch : public TabStateValues { +public: + TabStateWatch(HINSTANCE _hInstance, HWND _hParent); + + void Update() override; +}; diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index 01baf572a8d2..766f9dff641e 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -187,6 +187,7 @@ BEGIN PUSHBUTTON "Level -",IDC_GEDBG_TEXLEVELDOWN,24,192,40,14 PUSHBUTTON "Level +",IDC_GEDBG_TEXLEVELUP,84,192,40,14 CONTROL "",IDC_GEDBG_FBTABS,"SysTabControl32",TCS_BUTTONS | TCS_FOCUSNEVER,384,192,110,12 + PUSHBUTTON "Break on Target...",IDC_GEDBG_BREAKTARGET,394,211,100,14 END @@ -625,6 +626,17 @@ BEGIN MENUITEM "Go to Address...", ID_GEDBG_GOTOADDR MENUITEM "Go to in Memory View", ID_DISASM_GOTOINMEMORYVIEW END + POPUP "stateoptions" + BEGIN + MENUITEM "Change...", ID_REGLIST_CHANGE + MENUITEM SEPARATOR + MENUITEM "Copy Value (Hex)", ID_DISASM_COPYINSTRUCTIONHEX + MENUITEM "Copy Value (Formatted)", ID_DISASM_COPYINSTRUCTIONDISASM + MENUITEM "Copy Entire Tab (Formatted)",ID_GEDBG_COPYALL + MENUITEM SEPARATOR + MENUITEM "Toggle Breakpoint", ID_DISASM_TOGGLEBREAKPOINT + MENUITEM "Add Watch", ID_GEDBG_WATCH + END END #endif // English (United States) resources diff --git a/Windows/resource.h b/Windows/resource.h index bdb6137da246..5c8af70437fd 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -324,6 +324,9 @@ #define ID_EMULATION_ROTATION_V_R 40159 #define ID_OPTIONS_DISPLAY_LAYOUT 40160 #define ID_OPTIONS_VULKAN 40161 +#define IDC_GEDBG_BREAKTARGET 40162 +#define ID_GEDBG_COPYALL 40163 +#define ID_GEDBG_WATCH 40164 // Dummy option to let the buffered rendering hotkey cycle through all the options. #define ID_OPTIONS_BUFFEREDRENDERINGDUMMY 40500 @@ -336,7 +339,7 @@ #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 256 -#define _APS_NEXT_COMMAND_VALUE 40161 +#define _APS_NEXT_COMMAND_VALUE 40165 #define _APS_NEXT_CONTROL_VALUE 1199 #define _APS_NEXT_SYMED_VALUE 101 #endif