diff --git a/.github/agents/ppsspp-core.agent.md b/.github/agents/ppsspp-core.agent.md index 09e3e73d52..1bc57b3195 100644 --- a/.github/agents/ppsspp-core.agent.md +++ b/.github/agents/ppsspp-core.agent.md @@ -1,35 +1 @@ ---- -name: ppsspp-core -description: "Use for PPSSPP emulator core C++ tasks: HLE, CPU, GPU, timing, regression analysis, and minimal-risk fixes." ---- - -# PPSSPP Core Agent - -You are a specialized agent for PPSSPP emulator-core engineering work. - -## Focus - -1. C++ changes in core emulator paths, including HLE, CPU, GPU, timing, and synchronization. -2. Bug-risk-first analysis for behavior regressions and compatibility issues. -3. Minimal, targeted diffs that preserve existing architecture. - -## Workflow - -1. Locate affected code paths and nearby call flow before editing. -2. Identify behavior and compatibility risks first, especially for savestates and timing. -3. Implement the smallest safe patch that addresses the issue. -4. Validate with targeted build or test commands relevant to touched code. -5. Report outcomes with explicit notes on what was and was not validated. - -## Guardrails - -1. Do not perform broad refactors unless required to fix correctness. -2. Avoid changing unrelated platform paths while fixing a focused issue. -3. Keep threading and shared-state handling aligned with existing patterns. -4. Preserve serialization assumptions unless migration handling is included. - -## Output Style - -1. Lead with findings and risks for review requests. -2. Include concrete file and symbol references. -3. Keep recommendations specific, testable, and scoped. \ No newline at end of file +See AGENTS.md in the root folder of this PPSSPP git repo. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 9d121858cf..8e8b3b1f5b 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,38 +1 @@ -# PPSSPP Copilot Instructions - -These rules apply to this repository by default. - -Ignore the folder ai_instructions, it's old stuff from contributors. - -## Priorities - -1. Preserve emulator behavior and compatibility first. -2. Prefer correctness over speed when they conflict. -3. Improve performance only when behavior is unchanged and measurable. -4. Keep style changes minimal unless requested. Follow existing code patterns and conventions. - -## Change Strategy - -1. Make the smallest safe change that solves the issue. -2. Avoid broad refactors unless they are required for correctness. -3. Preserve platform-specific code paths and build logic. -4. Keep cross-platform parity in mind when changing shared code. - -## Core Safety Checks - -1. For HLE, CPU, GPU, timing, threading, and memory changes, call out regression risks explicitly. -2. Consider savestate compatibility when changing serialized state. -3. Keep lock usage and shared-state access consistent with existing patterns. -4. Prefer existing architecture and helper paths over introducing new abstractions. - -## Build and Validation - -To verify that things build on Linux/Mac, use ./b.sh --debug. For Windows, use the Visual Studio solution in the Windows subdirectory. - -Do not run unit test (I will add instructions for how to run them later). - -## Review Expectations - -1. In reviews, list concrete risks and behavior changes before summaries. -2. Include file and symbol references for important findings. -3. Propose focused follow-up checks when full validation is not possible. \ No newline at end of file +See: #file:AGENTS.md at the root of the git repo for additional agent-specific guidance. diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..2e6016a6b0 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,45 @@ +# PPSSPP Agent instructions + +These rules apply to this repository by default. + +Ignore the folder ai_instructions in the root directory, it's old stuff from contributors. + +## General instructions + +1. Keep style changes minimal unless requested. Follow existing code patterns and conventions. +2. Keep cross-platform parity in mind when changing shared code. See below for more multiplatform tips + +## Core Safety Checks + +1. For HLE, CPU, GPU, timing, threading, and memory changes, call out regression risks explicitly. +2. Consider savestate compatibility when changing serialized state. + +## Build and Validation + +To verify that things build on Linux/Mac, use ./b.sh --debug. For Windows, use the Visual Studio solution in the Windows subdirectory. +Do not run unit test (I will add instructions for how to run them later). + +## Multiplatform considerations + +The emulator has multiple platform-specific entry points. Some of these will be merged or removed in the future, but are all +still there. To verify that a change works, technically we need to compile for all these systems, but in practice we'll +just compile locally and test the platform we are currently on, and let CI handle the cross platform considerations. + +System_-prefixed wrapper functions implement kind of a platform wrapper for some functionality, and are implemented in +the following list of files for each system. If we change one, we need to change them all. + +Windows/main.cpp +ios/main.cpp +SDL/SDLMain.cpp +UWP/PPSSPP_UWPMain.cpp +Qt/main.cpp +android/jni/app-android.cpp + +## Headless and unittest builds + +We have additional PPSSPPHeadless and unit test builds (/headless and /unittest), that have their own separate +main functions (and also stub out most of the System_ functions as needed). Take these into account +when making cross platform changes. + +New unit tests are added by listing them in availableTests in unittest.cpp. If they are large, put them in +separate files in the unittest subdirectory. Remember to update both CMakeLists.txt and the visual studio project. diff --git a/Common/System/Request.cpp b/Common/System/Request.cpp index 4d24ac1140..4dcbe98b9c 100644 --- a/Common/System/Request.cpp +++ b/Common/System/Request.cpp @@ -39,8 +39,6 @@ const char *RequestTypeAsString(SystemRequestType type) { case SystemRequestType::GRAPHICS_BACKEND_FAILED_ALERT: return "GRAPHICS_BACKEND_FAILED_ALERT"; case SystemRequestType::CREATE_GAME_SHORTCUT: return "CREATE_GAME_SHORTCUT"; case SystemRequestType::SHOW_FILE_IN_FOLDER: return "SHOW_FILE_IN_FOLDER"; - case SystemRequestType::SEND_DEBUG_OUTPUT: return "SEND_DEBUG_OUTPUT"; - case SystemRequestType::SEND_DEBUG_SCREENSHOT: return "SEND_DEBUG_SCREENSHOT"; case SystemRequestType::NOTIFY_UI_EVENT: return "NOTIFY_UI_EVENT"; case SystemRequestType::SET_KEEP_SCREEN_BRIGHT: return "SET_KEEP_SCREEN_BRIGHT"; case SystemRequestType::CAMERA_COMMAND: return "CAMERA_COMMAND"; diff --git a/Common/System/Request.h b/Common/System/Request.h index 8bfeba4efa..6bd40b1fd1 100644 --- a/Common/System/Request.h +++ b/Common/System/Request.h @@ -187,13 +187,6 @@ inline void System_SetWindowTitle(std::string_view param) { g_requestManager.MakeSystemRequest(SystemRequestType::SET_WINDOW_TITLE, NO_REQUESTER_TOKEN, nullptr, nullptr, param, "", 0); } -inline bool System_SendDebugOutput(std::string_view string) { - return g_requestManager.MakeSystemRequest(SystemRequestType::SEND_DEBUG_OUTPUT, NO_REQUESTER_TOKEN, nullptr, nullptr, string, "", 0); -} - -inline void System_SendDebugScreenshot(std::string_view data, int height) { - g_requestManager.MakeSystemRequest(SystemRequestType::SEND_DEBUG_SCREENSHOT, NO_REQUESTER_TOKEN, nullptr, nullptr, data, "", height); -} inline void System_IAPRestorePurchases(RequesterToken token, RequestCallback callback, RequestFailedCallback failedCallback = nullptr) { g_requestManager.MakeSystemRequest(SystemRequestType::IAP_RESTORE_PURCHASES, token, callback, failedCallback, "", "", 0); @@ -213,3 +206,5 @@ void System_RunCallbackInWndProc(void (*callback)(void *, void *), void *userdat // Non-inline to avoid including Path.h void System_CreateGameShortcut(const Path &path, std::string_view title); void System_ShowFileInFolder(const Path &path); +bool System_SendDebugOutput(std::string_view string); +void System_SendDebugScreenshot(const uint8_t *data, int width, int height); diff --git a/Common/System/System.h b/Common/System/System.h index dbb10375d4..0a5c2e75a1 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -87,11 +87,6 @@ enum class SystemRequestType { CREATE_GAME_SHORTCUT, SHOW_FILE_IN_FOLDER, - // Commonly ignored, used when automated tests generate output. - SEND_DEBUG_OUTPUT, - // Note: height specified as param3, width based on param1.size() / param3. - SEND_DEBUG_SCREENSHOT, - NOTIFY_UI_EVENT, // Used to manage events that are useful for popup virtual keyboards. SET_KEEP_SCREEN_BRIGHT, diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 32f19beb26..faf7a135ef 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -2060,7 +2060,7 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o __DisplayGetFramebuf(&topaddr, &linesize, nullptr, 0); // TODO: Convert based on pixel format / mode / something? - System_SendDebugScreenshot(std::string((const char *)&topaddr[0], linesize * 272), 272); + System_SendDebugScreenshot(&topaddr[0], linesize, 272); return hleLogDebug(Log::sceIo, 0); } case EMULATOR_DEVCTL__TOGGLE_FASTFORWARD: diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index b40dbb0070..77484383df 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1876,7 +1876,7 @@ int __KernelGPUReplay() { PSPPointer topaddr; u32 linesize = 512; __DisplayGetFramebuf(&topaddr, &linesize, nullptr, 0); - System_SendDebugScreenshot(std::string((const char *)&topaddr[0], linesize * 272), 272); + System_SendDebugScreenshot(&topaddr[0], linesize, 272); Core_Stop(); } diff --git a/Qt/QtMain.cpp b/Qt/QtMain.cpp index d99d3d345c..9434f75f81 100644 --- a/Qt/QtMain.cpp +++ b/Qt/QtMain.cpp @@ -408,6 +408,9 @@ bool MainUI::HandleCustomEvent(QEvent *e) { return true; } +bool System_SendDebugOutput(std::string_view data) { return false; } +void System_SendDebugScreenshot(const uint8_t *data, int width, int height) {} + bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) { switch (type) { case SystemRequestType::EXIT_APP: diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index b592ce7148..20cd42ce46 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -838,6 +838,9 @@ void System_Notify(SystemNotification notification) { } } +bool System_SendDebugOutput(std::string_view data) { return false; } +void System_SendDebugScreenshot(const uint8_t *data, int width, int height) {} + // returns -1 on failure static int parseInt(const char *str) { int val; diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index 02d2bdb4ba..0c48831c85 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -416,6 +416,9 @@ std::vector System_GetPropertyStringVec(SystemProperty prop) { } } +bool System_SendDebugOutput(std::string_view data) { return false; } +void System_SendDebugScreenshot(const uint8_t *data, int width, int height) {} + extern AudioBackend *g_audioBackend; int64_t System_GetPropertyInt(SystemProperty prop) { diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 3e9d803465..ef7fdda4bb 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -1163,6 +1163,7 @@ + @@ -1451,4 +1452,4 @@ - + \ No newline at end of file diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index bde68d098f..1a6242c406 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -842,6 +842,7 @@ assets + @@ -880,4 +881,4 @@ assets - + \ No newline at end of file diff --git a/Windows/main.cpp b/Windows/main.cpp index e92e4f5d51..3de86a2eac 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -885,6 +885,9 @@ static std::string GetDefaultLangRegion() { } } +bool System_SendDebugOutput(std::string_view data) { return false; } +void System_SendDebugScreenshot(const uint8_t *data, int width, int height) {} + static const int EXIT_CODE_VULKAN_WORKS = 42; #ifndef _DEBUG diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 5d763f65c9..cea0d300bb 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -379,6 +379,9 @@ void System_LaunchUrl(LaunchUrlType urlType, std::string_view url) { } } +bool System_SendDebugOutput(std::string_view data) { return false; } +void System_SendDebugScreenshot(const uint8_t *data, int width, int height) {} + std::string System_GetProperty(SystemProperty prop) { switch (prop) { case SYSPROP_NAME: diff --git a/headless/Headless.cpp b/headless/Headless.cpp index 295a1f81f8..76f7dc13aa 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -60,6 +60,13 @@ static HeadlessHost *g_headlessHost; +static Path g_comparisonScreenshot; +static Path g_screenshotSavePath; +static double g_maxScreenshotError = 0.0; +static std::string g_debugOutputBuffer; +static bool g_writeFailureScreenshot = true; +static bool g_writeDebugOutput = true; + #if PPSSPP_PLATFORM(ANDROID) JNIEnv *getEnv() { return nullptr; @@ -98,29 +105,13 @@ bool System_GetPropertyBool(SystemProperty prop) { void System_Notify(SystemNotification notification) {} void System_PostUIMessage(UIMessage message, std::string_view param) {} void System_RunOnMainThread(std::function) {} -bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) { - switch (type) { - case SystemRequestType::SEND_DEBUG_OUTPUT: - if (g_headlessHost) { - g_headlessHost->SendDebugOutput(param1); - return true; - } - return false; - case SystemRequestType::SEND_DEBUG_SCREENSHOT: - if (g_headlessHost) { - g_headlessHost->SendDebugScreenshot((const u8 *)param1.data(), (uint32_t)(param1.size() / param3), param3); - return true; - } - return false; - default: - return false; - } -} + void System_AskForPermission(SystemPermission permission) {} PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; } void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; } void System_AudioClear() {} void System_AudioPushSamples(const s32 *audio, int numSamples, float volume) {} +bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) { return false; } // TODO: To avoid having to define these here, these should probably be turned into system "requests". bool NativeSaveSecret(std::string_view nameOfSecret, std::string_view data) { return false; } @@ -128,8 +119,7 @@ std::string NativeLoadSecret(std::string_view nameOfSecret) { return ""; } -int printUsage(const char *progname, const char *reason) -{ +int printUsage(const char *progname, const char *reason) { if (reason != NULL) fprintf(stderr, "Error: %s\n\n", reason); fprintf(stderr, "PPSSPP Headless\n"); @@ -173,6 +163,98 @@ static HeadlessHost *getHost(GPUCore gpuCore) { } } +void FlushDebugOutput() { + if (!g_debugOutputBuffer.empty()) { + fwrite(g_debugOutputBuffer.data(), sizeof(char), g_debugOutputBuffer.length(), stdout); + g_debugOutputBuffer.clear(); + } +} + +void SetWriteDebugOutput(bool flag) { + g_writeDebugOutput = flag; +} + +void SetComparisonScreenshot(const Path &filename, double maxError) { + g_comparisonScreenshot = filename; + g_maxScreenshotError = maxError; +} +void SetScreenshotSavePath(const Path &filename) { + g_screenshotSavePath = filename; +} +void SetWriteFailureScreenshot(bool flag) { + g_writeFailureScreenshot = flag; +} + +void SendDebugOutput(std::string_view output) { + if (!g_writeDebugOutput) + return; +#ifdef _WIN32 + std::string str(output); + OutputDebugStringUTF8(str.c_str()); +#endif + if (output.find('\n') != output.npos) { + g_debugOutputBuffer += output; + FlushDebugOutput(); + } else { + g_debugOutputBuffer += output; + } +} + +bool System_SendDebugOutput(std::string_view data) { + SendDebugOutput(data); + return true; +} + +void SendAndCollectOutput(const std::string &output) { + SendDebugOutput(output); + if (PSP_CoreParameter().collectDebugOutput) { + *PSP_CoreParameter().collectDebugOutput += output; + } +} + +void System_SendDebugScreenshot(const uint8_t *data, int width, int height) { + const u8 *pixbuf = (const u8 *)data; + u32 w = width; + u32 h = height; + + // We ignore the current framebuffer parameters and just grab the full screen. + const static u32 FRAME_STRIDE = 512; + const static u32 FRAME_WIDTH = 480; + const static u32 FRAME_HEIGHT = 272; + + GPUDebugBuffer buffer; + gpu->GetCurrentFramebuffer(buffer, GPU_DBG_FRAMEBUF_DISPLAY); + const std::vector pixels = TranslateDebugBufferToCompare(&buffer, 512, 272); + + // If a screenshot save path is set, save unconditionally. + if (!g_screenshotSavePath.empty()) { + ScreenshotComparer saver(pixels, FRAME_STRIDE, FRAME_WIDTH, FRAME_HEIGHT); + if (saver.SaveActualBitmap(g_screenshotSavePath)) + SendAndCollectOutput("Screenshot saved to: " + g_screenshotSavePath.ToVisualString() + "\n"); + } + + // Only compare if we have a reference. + if (g_comparisonScreenshot.empty()) { + return; + } + + ScreenshotComparer comparer(pixels, FRAME_STRIDE, FRAME_WIDTH, FRAME_HEIGHT); + double errors = comparer.Compare(g_comparisonScreenshot); + if (errors < 0) { + SendAndCollectOutput(comparer.GetError() + "\n"); + } + + if (errors > g_maxScreenshotError) { + SendAndCollectOutput(StringFromFormat("Screenshot MSE: %f\n", errors)); + } + + if (errors > g_maxScreenshotError && g_writeFailureScreenshot) { + if (comparer.SaveActualBitmap(Path("__testfailure.bmp"))) + SendAndCollectOutput("Actual output written to: __testfailure.bmp\n"); + comparer.SaveVisualComparisonPNG(Path("__testcompare.png")); + } +} + struct AutoTestOptions { double timeout; double maxScreenshotError; @@ -200,7 +282,7 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const } if (opt.compare) { - headlessHost->SetComparisonScreenshot(ExpectedScreenshotFromFilename(coreParameter.fileToStart), opt.maxScreenshotError); + SetComparisonScreenshot(ExpectedScreenshotFromFilename(coreParameter.fileToStart), opt.maxScreenshotError); } std::string error_string; @@ -251,7 +333,7 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const if (!opt.bench) { printf("%s", output.c_str()); - System_SendDebugOutput("TIMEOUT\n"); + SendDebugOutput("TIMEOUT\n"); GitHubActionsPrint("error", "Test timeout for %s", currentTestName.c_str()); } @@ -279,7 +361,7 @@ bool RunAutoTest(HeadlessHost *headlessHost, CoreParameter &coreParameter, const PSP_Shutdown(true); if (!opt.bench) { - headlessHost->FlushDebugOutput(); + FlushDebugOutput(); } if (opt.compare && passed) { @@ -561,11 +643,11 @@ int main(int argc, const char* argv[]) { } if (cmdLineOptions.screenshotFilename.has_value()) - headlessHost->SetComparisonScreenshot(Path(std::string(cmdLineOptions.screenshotFilename.value())), testOptions.maxScreenshotError); + SetComparisonScreenshot(Path(std::string(cmdLineOptions.screenshotFilename.value())), testOptions.maxScreenshotError); if (cmdLineOptions.screenshotFilenameSave.has_value()) - headlessHost->SetScreenshotSavePath(Path(std::string(cmdLineOptions.screenshotFilenameSave.value()))); - headlessHost->SetWriteFailureScreenshot(!getenv("GITHUB_ACTIONS") && !testOptions.bench); - headlessHost->SetWriteDebugOutput(!testOptions.compare && !testOptions.bench); + SetScreenshotSavePath(Path(std::string(cmdLineOptions.screenshotFilenameSave.value()))); + SetWriteFailureScreenshot(!getenv("GITHUB_ACTIONS") && !testOptions.bench); + SetWriteDebugOutput(!testOptions.compare && !testOptions.bench); #if PPSSPP_PLATFORM(ANDROID) // For some reason the debugger installs it with this name? diff --git a/headless/HeadlessHost.cpp b/headless/HeadlessHost.cpp index 6429dba8af..ddd3e4008d 100644 --- a/headless/HeadlessHost.cpp +++ b/headless/HeadlessHost.cpp @@ -24,45 +24,3 @@ #include "headless/Compare.h" #include "headless/HeadlessHost.h" -void HeadlessHost::SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h) { - // We ignore the current framebuffer parameters and just grab the full screen. - const static u32 FRAME_STRIDE = 512; - const static u32 FRAME_WIDTH = 480; - const static u32 FRAME_HEIGHT = 272; - - GPUDebugBuffer buffer; - gpu->GetCurrentFramebuffer(buffer, GPU_DBG_FRAMEBUF_DISPLAY); - const std::vector pixels = TranslateDebugBufferToCompare(&buffer, 512, 272); - - // If a screenshot save path is set, save unconditionally. - if (!screenshotSavePath_.empty()) { - ScreenshotComparer saver(pixels, FRAME_STRIDE, FRAME_WIDTH, FRAME_HEIGHT); - if (saver.SaveActualBitmap(screenshotSavePath_)) - SendAndCollectOutput("Screenshot saved to: " + screenshotSavePath_.ToVisualString() + "\n"); - } - - // Only compare if we have a reference. - if (comparisonScreenshot_.empty()) { - return; - } - - ScreenshotComparer comparer(pixels, FRAME_STRIDE, FRAME_WIDTH, FRAME_HEIGHT); - double errors = comparer.Compare(comparisonScreenshot_); - if (errors < 0) - SendAndCollectOutput(comparer.GetError() + "\n"); - - if (errors > maxScreenshotError_) - SendAndCollectOutput(StringFromFormat("Screenshot MSE: %f\n", errors)); - - if (errors > maxScreenshotError_ && writeFailureScreenshot_) { - if (comparer.SaveActualBitmap(Path("__testfailure.bmp"))) - SendAndCollectOutput("Actual output written to: __testfailure.bmp\n"); - comparer.SaveVisualComparisonPNG(Path("__testcompare.png")); - } -} - -void HeadlessHost::SendAndCollectOutput(const std::string &output) { - SendDebugOutput(output); - if (PSP_CoreParameter().collectDebugOutput) - *PSP_CoreParameter().collectDebugOutput += output; -} diff --git a/headless/HeadlessHost.h b/headless/HeadlessHost.h index 4a0504abc9..fd17dfb829 100644 --- a/headless/HeadlessHost.h +++ b/headless/HeadlessHost.h @@ -28,51 +28,9 @@ public: virtual bool InitGraphics(std::string *error_message, GraphicsContext **ctx, GPUCore core) {return false;} virtual void ShutdownGraphics() {} - virtual void SendDebugOutput(const std::string &output) { - if (!writeDebugOutput_) - return; - if (output.find('\n') != output.npos) { - FlushDebugOutput(); - fwrite(output.data(), sizeof(char), output.length(), stdout); - } else { - debugOutputBuffer_ += output; - } - } - void FlushDebugOutput() { - if (!debugOutputBuffer_.empty()) { - fwrite(debugOutputBuffer_.data(), sizeof(char), debugOutputBuffer_.length(), stdout); - debugOutputBuffer_.clear(); - } - } - - void SetWriteDebugOutput(bool flag) { - writeDebugOutput_ = flag; - } - - void SetComparisonScreenshot(const Path &filename, double maxError) { - comparisonScreenshot_ = filename; - maxScreenshotError_ = maxError; - } - void SetScreenshotSavePath(const Path &filename) { - screenshotSavePath_ = filename; - } - void SetWriteFailureScreenshot(bool flag) { - writeFailureScreenshot_ = flag; - } - - void SendDebugScreenshot(const u8 *pixbuf, u32 w, u32 h); - virtual void SwapBuffers() {} protected: - void SendAndCollectOutput(const std::string &output); - - Path comparisonScreenshot_; - Path screenshotSavePath_; - double maxScreenshotError_ = 0.0; - std::string debugOutputBuffer_; GPUCore gpuCore_; GraphicsContext *gfx_ = nullptr; - bool writeFailureScreenshot_ = true; - bool writeDebugOutput_ = true; }; diff --git a/headless/SDLHeadlessHost.cpp b/headless/SDLHeadlessHost.cpp index ac00aa0d1f..5d0d46829e 100644 --- a/headless/SDLHeadlessHost.cpp +++ b/headless/SDLHeadlessHost.cpp @@ -50,8 +50,7 @@ SDL_Window *CreateHiddenWindow() { class GLDummyGraphicsContext : public GraphicsContext { public: - GLDummyGraphicsContext() { - } + GLDummyGraphicsContext() {} ~GLDummyGraphicsContext() { delete draw_; } bool InitFromRenderThread(std::string *errorMessage) override; @@ -165,8 +164,7 @@ bool SDLHeadlessHost::InitGraphics(std::string *error_message, GraphicsContext * gfx_ = graphicsContext; std::thread th([&]{ - // This is the "EmuThread". - SetCurrentThreadName("SDL-EmuThread"); + SetCurrentThreadName("SDL-RenderThread"); while (threadState_ == RenderThreadState::IDLE) sleep_ms(1, "sdl-idle-poll"); diff --git a/headless/WindowsHeadlessHost.cpp b/headless/WindowsHeadlessHost.cpp index 0afcc2c08d..697cbbb338 100644 --- a/headless/WindowsHeadlessHost.cpp +++ b/headless/WindowsHeadlessHost.cpp @@ -66,12 +66,6 @@ HWND CreateHiddenWindow() { return CreateWindowEx(0, L"PPSSPPHeadless", L"PPSSPPHeadless", style, CW_USEDEFAULT, CW_USEDEFAULT, WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, NULL, NULL); } -void WindowsHeadlessHost::SendDebugOutput(const std::string &output) { - if (writeDebugOutput_) - fwrite(output.data(), sizeof(char), output.length(), stdout); - OutputDebugStringUTF8(output.c_str()); -} - bool WindowsHeadlessHost::InitGraphics(std::string *error_message, GraphicsContext **ctx, GPUCore core) { hWnd = CreateHiddenWindow(); gpuCore_ = core; diff --git a/headless/WindowsHeadlessHost.h b/headless/WindowsHeadlessHost.h index 8282cea874..7b966506dc 100644 --- a/headless/WindowsHeadlessHost.h +++ b/headless/WindowsHeadlessHost.h @@ -32,8 +32,6 @@ public: void SwapBuffers() override; - void SendDebugOutput(const std::string &output) override; - protected: enum class RenderThreadState { IDLE, diff --git a/ios/main.mm b/ios/main.mm index 2a2aa85c7f..989e50f5d3 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -652,6 +652,9 @@ AudioBackend *System_CreateAudioBackend() { return nullptr; } +bool System_SendDebugOutput(std::string_view data) { return false; } +void System_SendDebugScreenshot(const uint8_t *data, int width, int height) {} + int main(int argc, char *argv[]) { version = [[[UIDevice currentDevice] systemVersion] UTF8String]; if (1 != sscanf(version.c_str(), "%d", &g_iosVersionMajor)) { diff --git a/unittest/JitHarness.cpp b/unittest/JitHarness.cpp index 2964964807..4e56ee10b3 100644 --- a/unittest/JitHarness.cpp +++ b/unittest/JitHarness.cpp @@ -38,15 +38,6 @@ #include "Core/Config.h" #include "Core/HLE/HLE.h" -// Temporary hacks around annoying linking errors. Copied from Headless. -void NativeFrame(GraphicsContext *graphicsContext) { } -void NativeResized() { } - -bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) { return false; } -void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function cb) { cb(false, ""); } -void System_AskForPermission(SystemPermission permission) {} -PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; } - void UnitTestTerminator() { // Bails out of jit so we can time things. coreState = CORE_POWERDOWN; diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 8daaeb0ed5..cc2da2563e 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -125,6 +125,17 @@ void System_RunOnMainThread(std::function) {} void System_AudioGetDebugStats(char *buf, size_t bufSize) { if (buf) buf[0] = '\0'; } void System_AudioClear() {} void System_AudioPushSamples(const s32 *audio, int numSamples, float volume) {} +bool System_SendDebugOutput(std::string_view data) { return false; } +void System_SendDebugScreenshot(const uint8_t *data, int width, int height) {} + +// Temporary hacks around annoying linking errors. Copied from Headless. +void NativeFrame(GraphicsContext *graphicsContext) {} +void NativeResized() {} + +bool System_MakeRequest(SystemRequestType type, int requestId, const std::string ¶m1, const std::string ¶m2, int64_t param3, int64_t param4) { return false; } +void System_InputBoxGetString(const std::string &title, const std::string &defaultValue, std::function cb) { cb(false, ""); } +void System_AskForPermission(SystemPermission permission) {} +PermissionStatus System_GetPermissionStatus(SystemPermission permission) { return PERMISSION_STATUS_GRANTED; } // TODO: To avoid having to define these here, these should probably be turned into system "requests". // To clear the secret entirely, just save an empty string.