diff --git a/CMakeLists.txt b/CMakeLists.txt index b8ed965ac8..1ce247e912 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1006,6 +1006,8 @@ add_library(GPU OBJECT GPU/Common/SplineCommon.h GPU/Debugger/Breakpoints.cpp GPU/Debugger/Breakpoints.h + GPU/Debugger/Stepping.cpp + GPU/Debugger/Stepping.h GPU/GLES/GLES_GPU.cpp GPU/GLES/GLES_GPU.h GPU/GLES/FragmentShaderGenerator.cpp diff --git a/GPU/Debugger/Stepping.cpp b/GPU/Debugger/Stepping.cpp new file mode 100644 index 0000000000..f260a1da5b --- /dev/null +++ b/GPU/Debugger/Stepping.cpp @@ -0,0 +1,184 @@ +// Copyright (c) 2013- 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/mutex.h" +#include "GPU/Common/GPUDebugInterface.h" +#include "GPU/Debugger/Stepping.h" +#include "Core/Core.h" + +namespace GPUStepping { + +enum PauseAction { + PAUSE_CONTINUE, + PAUSE_BREAK, + PAUSE_GETFRAMEBUF, + PAUSE_GETDEPTHBUF, + PAUSE_GETSTENCILBUF, + PAUSE_GETTEX, + PAUSE_SETCMDVALUE, +}; + +static bool isStepping; + +static recursive_mutex pauseLock; +static condition_variable pauseWait; +static PauseAction pauseAction = PAUSE_CONTINUE; +static recursive_mutex actionLock; +static condition_variable actionWait; +// In case of accidental wakeup. +static bool actionComplete; + +// Many things need to run on the GPU thread. For example, reading the framebuffer. +// A message system is used to achieve this (temporarily "unpausing" the thread.) +// Below are values used to perform actions that return results. + +static bool bufferResult; +static GPUDebugBuffer bufferFrame; +static GPUDebugBuffer bufferDepth; +static GPUDebugBuffer bufferStencil; +static GPUDebugBuffer bufferTex; +static u32 pauseSetCmdValue; + +static void SetPauseAction(PauseAction act, bool waitComplete = true) { + { + lock_guard guard(pauseLock); + actionLock.lock(); + pauseAction = act; + } + + actionComplete = false; + pauseWait.notify_one(); + while (waitComplete && !actionComplete) { + actionWait.wait(actionLock); + } + actionLock.unlock(); +} + +static void RunPauseAction() { + lock_guard guard(actionLock); + + switch (pauseAction) { + case PAUSE_CONTINUE: + // Don't notify, just go back, woke up by accident. + return; + + case PAUSE_BREAK: + break; + + case PAUSE_GETFRAMEBUF: + bufferResult = gpuDebug->GetCurrentFramebuffer(bufferFrame); + break; + + case PAUSE_GETDEPTHBUF: + bufferResult = gpuDebug->GetCurrentDepthbuffer(bufferDepth); + break; + + case PAUSE_GETSTENCILBUF: + bufferResult = gpuDebug->GetCurrentStencilbuffer(bufferStencil); + break; + + case PAUSE_GETTEX: + bufferResult = gpuDebug->GetCurrentTexture(bufferTex); + break; + + case PAUSE_SETCMDVALUE: + gpuDebug->SetCmdValue(pauseSetCmdValue); + break; + + default: + ERROR_LOG(HLE, "Unsupported pause action, forgot to add it to the switch."); + } + + actionComplete = true; + actionWait.notify_one(); + pauseAction = PAUSE_BREAK; +} + +bool EnterStepping(std::function callback) { + lock_guard guard(pauseLock); + if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME) { + // Shutting down, don't try to step. + return false; + } + if (!gpuDebug) { + return false; + } + + // Just to be sure. + if (pauseAction == PAUSE_CONTINUE) { + pauseAction = PAUSE_BREAK; + } + isStepping = true; + + callback(); + + do { + RunPauseAction(); + pauseWait.wait(pauseLock); + } while (pauseAction != PAUSE_CONTINUE); + + isStepping = false; + return true; +} + +static bool GetBuffer(const GPUDebugBuffer *&buffer, PauseAction type, const GPUDebugBuffer &resultBuffer) { + if (!isStepping) { + return false; + } + + SetPauseAction(type); + buffer = &resultBuffer; + return bufferResult; +} + +bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer) { + return GetBuffer(buffer, PAUSE_GETFRAMEBUF, bufferFrame); +} + +bool GPU_GetCurrentDepthbuffer(const GPUDebugBuffer *&buffer) { + return GetBuffer(buffer, PAUSE_GETDEPTHBUF, bufferDepth); +} + +bool GPU_GetCurrentStencilbuffer(const GPUDebugBuffer *&buffer) { + return GetBuffer(buffer, PAUSE_GETSTENCILBUF, bufferStencil); +} + +bool GPU_GetCurrentTexture(const GPUDebugBuffer *&buffer) { + return GetBuffer(buffer, PAUSE_GETTEX, bufferTex); +} + +bool GPU_SetCmdValue(u32 op) { + if (!isStepping) { + return false; + } + + pauseSetCmdValue = op; + SetPauseAction(PAUSE_SETCMDVALUE); + return true; +} + +void ResumeFromStepping() { + SetPauseAction(PAUSE_CONTINUE, false); +} + +void ForceUnpause() { + SetPauseAction(PAUSE_CONTINUE, false); + actionComplete = true; + actionWait.notify_one(); +} + +}; \ No newline at end of file diff --git a/GPU/Debugger/Stepping.h b/GPU/Debugger/Stepping.h new file mode 100644 index 0000000000..4a8b954fbd --- /dev/null +++ b/GPU/Debugger/Stepping.h @@ -0,0 +1,38 @@ +// Copyright (c) 2013- 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/. + +#pragma once + +#include "base/functional.h" +#include "Common/CommonTypes.h" +#include "GPU/Common/GPUDebugInterface.h" + +namespace GPUStepping { + // Should be called from the GPU thread. + // Begins stepping and calls callback while inside a lock preparing stepping. + // This would be a good place to deliver a message to code that stepping is ready. + bool EnterStepping(std::function callback); + + bool GPU_GetCurrentFramebuffer(const GPUDebugBuffer *&buffer); + bool GPU_GetCurrentDepthbuffer(const GPUDebugBuffer *&buffer); + bool GPU_GetCurrentStencilbuffer(const GPUDebugBuffer *&buffer); + bool GPU_GetCurrentTexture(const GPUDebugBuffer *&buffer); + bool GPU_SetCmdValue(u32 op); + + void ResumeFromStepping(); + void ForceUnpause(); +}; \ No newline at end of file diff --git a/GPU/GPU.vcxproj b/GPU/GPU.vcxproj index f54ba562bd..c01a29f51b 100644 --- a/GPU/GPU.vcxproj +++ b/GPU/GPU.vcxproj @@ -161,6 +161,7 @@ + @@ -205,6 +206,7 @@ + diff --git a/GPU/GPU.vcxproj.filters b/GPU/GPU.vcxproj.filters index cd20eed520..fc2e0040d1 100644 --- a/GPU/GPU.vcxproj.filters +++ b/GPU/GPU.vcxproj.filters @@ -154,6 +154,9 @@ Debugger + + Debugger + @@ -277,6 +280,9 @@ Debugger + + Debugger + diff --git a/Windows/GEDebugger/GEDebugger.cpp b/Windows/GEDebugger/GEDebugger.cpp index 571eeddad2..998a761195 100644 --- a/Windows/GEDebugger/GEDebugger.cpp +++ b/Windows/GEDebugger/GEDebugger.cpp @@ -19,7 +19,8 @@ #include #include -#include "native/base/mutex.h" +#include "base/functional.h" +#include "base/mutex.h" #include "Windows/GEDebugger/GEDebugger.h" #include "Windows/GEDebugger/SimpleGLWindow.h" #include "Windows/GEDebugger/CtrlDisplayListView.h" @@ -33,115 +34,31 @@ #include "GPU/Common/GPUDebugInterface.h" #include "GPU/GPUState.h" #include "GPU/Debugger/Breakpoints.h" +#include "GPU/Debugger/Stepping.h" #include "Core/Config.h" #include #include using namespace GPUBreakpoints; - -enum PauseAction { - PAUSE_CONTINUE, - PAUSE_BREAK, - PAUSE_GETFRAMEBUF, - PAUSE_GETDEPTHBUF, - PAUSE_GETSTENCILBUF, - PAUSE_GETTEX, - PAUSE_SETCMDVALUE, -}; +using namespace GPUStepping; static bool attached = false; // TODO static bool textureCaching = true; -static recursive_mutex pauseLock; -static condition_variable pauseWait; -static PauseAction pauseAction = PAUSE_CONTINUE; -static recursive_mutex actionLock; -static condition_variable actionWait; -static bool actionComplete; static BreakNextType breakNext = BREAK_NONE; -static bool bufferResult; -static GPUDebugBuffer bufferFrame; -static GPUDebugBuffer bufferDepth; -static GPUDebugBuffer bufferStencil; -static GPUDebugBuffer bufferTex; -static u32 pauseSetCmdValue; - enum PrimaryDisplayType { PRIMARY_FRAMEBUF, PRIMARY_DEPTHBUF, PRIMARY_STENCILBUF, }; -// TODO: Simplify and move out of windows stuff, just block in a common way for everyone. - void CGEDebugger::Init() { SimpleGLWindow::RegisterClass(); CtrlDisplayListView::registerClass(); } -static void SetPauseAction(PauseAction act, bool waitComplete = true) { - { - lock_guard guard(pauseLock); - actionLock.lock(); - pauseAction = act; - } - - actionComplete = false; - pauseWait.notify_one(); - while (waitComplete && !actionComplete) { - actionWait.wait(actionLock); - } - actionLock.unlock(); -} - -static void RunPauseAction() { - lock_guard guard(actionLock); - - switch (pauseAction) { - case PAUSE_CONTINUE: - // Don't notify, just go back, woke up by accident. - return; - - case PAUSE_BREAK: - break; - - case PAUSE_GETFRAMEBUF: - bufferResult = gpuDebug->GetCurrentFramebuffer(bufferFrame); - break; - - case PAUSE_GETDEPTHBUF: - bufferResult = gpuDebug->GetCurrentDepthbuffer(bufferDepth); - break; - - case PAUSE_GETSTENCILBUF: - bufferResult = gpuDebug->GetCurrentStencilbuffer(bufferStencil); - break; - - case PAUSE_GETTEX: - bufferResult = gpuDebug->GetCurrentTexture(bufferTex); - break; - - case PAUSE_SETCMDVALUE: - gpuDebug->SetCmdValue(pauseSetCmdValue); - break; - - default: - ERROR_LOG(HLE, "Unsupported pause action, forgot to add it to the switch."); - } - - actionComplete = true; - actionWait.notify_one(); - pauseAction = PAUSE_BREAK; -} - -static void ForceUnpause() { - SetPauseAction(PAUSE_CONTINUE, false); - actionComplete = true; - actionWait.notify_one(); -} - CGEDebugger::CGEDebugger(HINSTANCE _hInstance, HWND _hParent) : Dialog((LPCSTR)IDD_GEDEBUGGER, _hInstance, _hParent), frameWindow(NULL), texWindow(NULL) { GPUBreakpoints::Init(); @@ -227,9 +144,9 @@ void CGEDebugger::UpdatePreviews() { // TODO: Do something different if not paused? wchar_t desc[256]; - GPUDebugBuffer *primaryBuffer = NULL; + const GPUDebugBuffer *primaryBuffer = NULL; + bool bufferResult = false; GPUgstate state; - bufferResult = false; if (gpuDebug != NULL) { state = gpuDebug->GetGState(); @@ -237,21 +154,24 @@ void CGEDebugger::UpdatePreviews() { switch (PrimaryDisplayType(fbTabs->CurrentTabIndex())) { case PRIMARY_FRAMEBUF: - SetPauseAction(PAUSE_GETFRAMEBUF); - primaryBuffer = &bufferFrame; - _snwprintf(desc, ARRAY_SIZE(desc), L"Color: 0x%08x (%dx%d)", state.getFrameBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight()); + bufferResult = GPU_GetCurrentFramebuffer(primaryBuffer); + if (bufferResult) { + _snwprintf(desc, ARRAY_SIZE(desc), L"Color: 0x%08x (%dx%d)", state.getFrameBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight()); + } break; case PRIMARY_DEPTHBUF: - SetPauseAction(PAUSE_GETDEPTHBUF); - primaryBuffer = &bufferDepth; - _snwprintf(desc, ARRAY_SIZE(desc), L"Depth: 0x%08x (%dx%d)", state.getDepthBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight()); + bufferResult = GPU_GetCurrentDepthbuffer(primaryBuffer); + if (bufferResult) { + _snwprintf(desc, ARRAY_SIZE(desc), L"Depth: 0x%08x (%dx%d)", state.getDepthBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight()); + } break; case PRIMARY_STENCILBUF: - SetPauseAction(PAUSE_GETSTENCILBUF); - primaryBuffer = &bufferStencil; - _snwprintf(desc, ARRAY_SIZE(desc), L"Stencil: 0x%08x (%dx%d)", state.getFrameBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight()); + bufferResult = GPU_GetCurrentStencilbuffer(primaryBuffer); + if (bufferResult) { + _snwprintf(desc, ARRAY_SIZE(desc), L"Stencil: 0x%08x (%dx%d)", state.getFrameBufRawAddress(), primaryBuffer->GetStride(), primaryBuffer->GetHeight()); + } break; } @@ -264,12 +184,12 @@ void CGEDebugger::UpdatePreviews() { SetDlgItemText(m_hDlg, IDC_GEDBG_FRAMEBUFADDR, L"Failed"); } - bufferResult = false; - SetPauseAction(PAUSE_GETTEX); + const GPUDebugBuffer *bufferTex = NULL; + bufferResult = GPU_GetCurrentTexture(bufferTex); if (bufferResult) { - auto fmt = SimpleGLWindow::Format(bufferTex.GetFormat()); - texWindow->Draw(bufferTex.GetData(), bufferTex.GetStride(), bufferTex.GetHeight(), bufferTex.GetFlipped(), fmt); + auto fmt = SimpleGLWindow::Format(bufferTex->GetFormat()); + texWindow->Draw(bufferTex->GetData(), bufferTex->GetStride(), bufferTex->GetHeight(), bufferTex->GetFlipped(), fmt); if (gpuDebug != NULL) { if (state.isTextureAlphaUsed()) { @@ -336,7 +256,7 @@ void CGEDebugger::SetBreakNext(BreakNextType type) { attached = true; SetupPreviews(); - SetPauseAction(PAUSE_CONTINUE, false); + ResumeFromStepping(); breakNext = type; } @@ -436,7 +356,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { SetDlgItemText(m_hDlg, IDC_GEDBG_TEXADDR, L""); // TODO: detach? Should probably have separate UI, or just on activate? - SetPauseAction(PAUSE_CONTINUE, false); + ResumeFromStepping(); breakNext = BREAK_NONE; break; } @@ -486,8 +406,7 @@ BOOL CGEDebugger::DlgProc(UINT message, WPARAM wParam, LPARAM lParam) { case WM_GEDBG_SETCMDWPARAM: { - pauseSetCmdValue = (u32)wParam; - SetPauseAction(PAUSE_SETCMDVALUE); + GPU_SetCmdValue((u32)wParam); } break; } @@ -501,23 +420,12 @@ bool WindowsHost::GPUDebuggingActive() { return attached; } -static void PauseWithMessage(UINT msg, WPARAM wParam = NULL, LPARAM lParam = NULL) { - lock_guard guard(pauseLock); - if (coreState != CORE_RUNNING && coreState != CORE_NEXTFRAME) { - return; - } - +static void DeliverMessage(UINT msg, WPARAM wParam, LPARAM lParam) { PostMessage(geDebuggerWindow->GetDlgHandle(), msg, wParam, lParam); +} - // Just to be sure. - if (pauseAction == PAUSE_CONTINUE) { - pauseAction = PAUSE_BREAK; - } - - do { - RunPauseAction(); - pauseWait.wait(pauseLock); - } while (pauseAction != PAUSE_CONTINUE); +static void PauseWithMessage(UINT msg, WPARAM wParam = NULL, LPARAM lParam = NULL) { + EnterStepping(std::bind(&DeliverMessage, msg, wParam, lParam)); } void WindowsHost::GPUNotifyCommand(u32 pc) { diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 72c9cdabb9..6919a86bc1 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -198,6 +198,7 @@ LOCAL_SRC_FILES := \ $(SRC)/GPU/Common/TextureDecoder.cpp \ $(SRC)/GPU/Common/PostShader.cpp \ $(SRC)/GPU/Debugger/Breakpoints.cpp \ + $(SRC)/GPU/Debugger/Stepping.cpp \ $(SRC)/GPU/GLES/Framebuffer.cpp \ $(SRC)/GPU/GLES/GLES_GPU.cpp.arm \ $(SRC)/GPU/GLES/TextureCache.cpp.arm \