diff --git a/Common/System/Request.h b/Common/System/Request.h index 2db3ddcf5a..9ba9bb5b61 100644 --- a/Common/System/Request.h +++ b/Common/System/Request.h @@ -207,5 +207,3 @@ 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/Core/Core.cpp b/Core/Core.cpp index fbe0b3d8a9..eeff849f37 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -155,6 +155,28 @@ static MIPSExceptionInfo g_exceptionInfo; // This is called on EmuThread before RunLoop. static bool Core_ProcessStepping(MIPSDebugInterface *cpu); +static std::function g_debugOutputListener; +static std::function g_debugScreenshotListener; + +void Core_RegisterDebugOutputListeners(std::function listener, std::function screenshotListener) { + g_debugOutputListener = std::move(listener); + g_debugScreenshotListener = std::move(screenshotListener); +} + +void Core_SendDebugOutput(LogLevel level, std::string_view string) { + if (g_debugOutputListener) { + g_debugOutputListener(string); + } else { + GENERIC_LOG(Log::sceIo, level, "%.*s", STR_VIEW(string)); + } +} + +void Core_SendDebugScreenshot(const DebugScreenshotDesc &desc) { + if (g_debugScreenshotListener) { + g_debugScreenshotListener(desc); + } +} + BreakReason Core_BreakReason() { return g_breakReason; } diff --git a/Core/Core.h b/Core/Core.h index c43689aed5..632c9f11c9 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -215,6 +215,21 @@ void Core_BreakException(u32 pc); // Call when loading save states, etc. void Core_ResetException(); +// Used by headless/pspautotest to collect data for the diffs. Crash reports are also sent here. +// Log level is only used if the listener is not registered. +enum class LogLevel : int; + +enum GEBufferFormat : uint8_t; +struct DebugScreenshotDesc { + const uint8_t *data; + u32 stride; + u32 height; + GEBufferFormat format; +}; +void Core_SendDebugOutput(LogLevel level, std::string_view string); +void Core_SendDebugScreenshot(const DebugScreenshotDesc &desc); +void Core_RegisterDebugOutputListeners(std::function listener, std::function screenshotListener); + class MIPSState; // Shortcut, just calls Core_MemoryException with automatically determined parameters (function name, etc). void Core_MemoryExceptionHLE(MIPSState *mips, u32 address, u32 accessSize, MemoryExceptionType type); diff --git a/Core/Debugger/MemBlockInfo.cpp b/Core/Debugger/MemBlockInfo.cpp index 31cbed4c80..9a9379f473 100644 --- a/Core/Debugger/MemBlockInfo.cpp +++ b/Core/Debugger/MemBlockInfo.cpp @@ -569,7 +569,7 @@ void NotifyMemInfoCopy(uint32_t destPtr, uint32_t srcPtr, uint32_t size, const c info.pc = currentMIPS->pc; // Store the prefix for now. The correct tag will be calculated on flush. - info.tagLen = std::min(sizeof(info.tag), prefixLen); + info.tagLen = (uint8_t)std::min(sizeof(info.tag), prefixLen); memcpy(info.tag, prefix, info.tagLen); std::lock_guard guard(pendingWriteMutex); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index c9c75946f7..b81a848fb8 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -940,7 +940,7 @@ int sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync) } } -bool __DisplayGetFramebuf(PSPPointer *topaddr, u32 *linesize, u32 *pixelFormat, int latchedMode) { +bool __DisplayGetFramebuf(PSPPointer *topaddr, u32 *linesize, GEBufferFormat *pixelFormat, int latchedMode) { const FrameBufferState &fbState = latchedMode == PSP_DISPLAY_SETBUF_NEXTFRAME ? latchedFramebuf : framebuf; if (topaddr != nullptr) (*topaddr).ptr = fbState.topaddr; diff --git a/Core/HLE/sceDisplay.h b/Core/HLE/sceDisplay.h index 2f8d05aa28..eef5d2c9da 100644 --- a/Core/HLE/sceDisplay.h +++ b/Core/HLE/sceDisplay.h @@ -19,6 +19,8 @@ #include "Core/MemMap.h" +enum GEBufferFormat : uint8_t; + void __DisplayInit(); void __DisplayDoState(PointerWrap &p); void __DisplayShutdown(); @@ -26,7 +28,7 @@ void __DisplayShutdown(); void Register_sceDisplay(); // Get information about the current framebuffer. -bool __DisplayGetFramebuf(PSPPointer *topaddr, u32 *linesize, u32 *pixelFormat, int mode); +bool __DisplayGetFramebuf(PSPPointer *topaddr, u32 *linesize, GEBufferFormat *pixelFormat, int mode); void __DisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync); // Call this when resuming to avoid a small speedup burst diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 3641881906..e54165c6fe 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -2037,8 +2037,7 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o case EMULATOR_DEVCTL__SEND_OUTPUT: if (Memory::IsValidRange(argAddr, argLen)) { std::string data(Memory::GetCharPointerUnchecked(argAddr), argLen); - if (!System_SendDebugOutput(data)) - DEBUG_LOG(Log::sceIo, "%s", data.c_str()); + Core_SendDebugOutput(LogLevel::LINFO, data); if (PSP_CoreParameter().collectDebugOutput) *PSP_CoreParameter().collectDebugOutput += data; } @@ -2055,12 +2054,14 @@ static u32 sceIoDevctl(const char *name, int cmd, u32 argAddr, int argLen, u32 o case EMULATOR_DEVCTL__EMIT_SCREENSHOT: { - PSPPointer topaddr; - u32 linesize; - - __DisplayGetFramebuf(&topaddr, &linesize, nullptr, 0); + // TODO: Add a high-res path for screenshots, and maybe a way to specify the filename. // TODO: Convert based on pixel format / mode / something? - System_SendDebugScreenshot(&topaddr[0], linesize, 272); + DebugScreenshotDesc desc; + PSPPointer topaddr; + __DisplayGetFramebuf(&topaddr, &desc.stride, &desc.format, 0); + desc.data = &topaddr[0]; + desc.height = 272; + Core_SendDebugScreenshot(desc); return hleLogDebug(Log::sceIo, 0); } case EMULATOR_DEVCTL__TOGGLE_FASTFORWARD: diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 3b0212eb64..06488ec44d 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -1913,10 +1913,12 @@ int __KernelGPUReplay() { } if (result == GPURecord::ReplayResult::Done && PSP_CoreParameter().headLess && !PSP_CoreParameter().startBreak) { + DebugScreenshotDesc desc; PSPPointer topaddr; - u32 linesize = 512; - __DisplayGetFramebuf(&topaddr, &linesize, nullptr, 0); - System_SendDebugScreenshot(&topaddr[0], linesize, 272); + __DisplayGetFramebuf(&topaddr, &desc.stride, &desc.format, 0); + desc.data = &topaddr[0]; + desc.height = 272; + Core_SendDebugScreenshot(desc); Core_Stop(); } diff --git a/GPU/Debugger/Record.cpp b/GPU/Debugger/Record.cpp index 9d92296a28..0cc3eb881d 100644 --- a/GPU/Debugger/Record.cpp +++ b/GPU/Debugger/Record.cpp @@ -798,7 +798,8 @@ void Recorder::NotifyBeginFrame() { CheckEdramTrans(); struct DisplayBufData { PSPPointer topaddr; - u32 linesize, pixelFormat; + u32 linesize; + GEBufferFormat pixelFormat; }; DisplayBufData disp; diff --git a/SDL/SDLMain.cpp b/SDL/SDLMain.cpp index 65b1a59eb2..dfb7f52e7f 100644 --- a/SDL/SDLMain.cpp +++ b/SDL/SDLMain.cpp @@ -1240,9 +1240,6 @@ 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) {} - void UpdateWindowState(SDL_Window *window) { SDL_SetWindowTitle(window, g_windowState.title.c_str()); if (g_windowState.applyFullScreenNextFrame) { diff --git a/UI/ImDebugger/ImGe.cpp b/UI/ImDebugger/ImGe.cpp index edb8852837..4457f661e2 100644 --- a/UI/ImDebugger/ImGe.cpp +++ b/UI/ImDebugger/ImGe.cpp @@ -268,7 +268,7 @@ void DrawDisplayWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManag PSPPointer topaddr; u32 linesize; - u32 pixelFormat; + GEBufferFormat pixelFormat; __DisplayGetFramebuf(&topaddr, &linesize, &pixelFormat, cfg.displayLatched); diff --git a/UWP/PPSSPP_UWPMain.cpp b/UWP/PPSSPP_UWPMain.cpp index f9f6dd9c0b..7f585c1ea5 100644 --- a/UWP/PPSSPP_UWPMain.cpp +++ b/UWP/PPSSPP_UWPMain.cpp @@ -402,9 +402,6 @@ 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/main.cpp b/Windows/main.cpp index e6fd35dd8d..f27468b19c 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -890,9 +890,6 @@ 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 d8dafa02a2..029c05c7eb 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -291,9 +291,6 @@ 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 34b4587103..fa5edfecc1 100644 --- a/headless/Headless.cpp +++ b/headless/Headless.cpp @@ -144,10 +144,6 @@ void FlushDebugOutput() { } } -void SetWriteDebugOutput(bool flag) { - g_writeDebugOutput = flag; -} - void SetComparisonScreenshot(const Path &filename, double maxError) { g_comparisonScreenshot = filename; g_maxScreenshotError = maxError; @@ -174,31 +170,27 @@ void SendDebugOutput(std::string_view output) { } } -bool System_SendDebugOutput(std::string_view data) { - SendDebugOutput(data); - return true; -} - -void SendAndCollectOutput(const std::string &output) { +void SendAndCollectOutput(std::string_view 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; +void SendDebugScreenshot(const DebugScreenshotDesc &desc) { + const u8 *pixbuf = (const u8 *)desc.data; + u32 w = desc.stride; + u32 h = desc.height; // We ignore the current framebuffer parameters and just grab the full screen. + // TOOD: Uh, why not use them? They should be the same. 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); + const std::vector pixels = TranslateDebugBufferToCompare(&buffer, FRAME_STRIDE, FRAME_HEIGHT); // If a screenshot save path is set, save unconditionally. if (!g_screenshotSavePath.empty()) { @@ -301,8 +293,9 @@ static bool RunAutoTest(GraphicsContext *graphicsContext, CoreParameter &corePar g_screenshotFailed = false; std::string output; - if (opt.compare || opt.bench) + if (opt.compare || opt.bench) { coreParameter.collectDebugOutput = &output; + } if (!PSP_InitStart(coreParameter)) { // Shouldn't really happen anymore, the errors happen later in PSP_InitUpdate. @@ -397,7 +390,7 @@ static bool RunAutoTest(GraphicsContext *graphicsContext, CoreParameter &corePar passed = CompareOutput(coreParameter.fileToStart, output, opt.verbose, opt.printEqualLines); } - // Screenshot comparison failures are recorded in System_SendDebugScreenshot. + // Screenshot comparison failures are recorded in SendDebugScreenshot. if (!g_comparisonScreenshot.empty() && g_screenshotFailed) { passed = false; } @@ -621,6 +614,8 @@ int main(int argc, const char* argv[]) { g_Config.RestoreDefaults(RestoreSettingsBits::SETTINGS | RestoreSettingsBits::CONTROLS | RestoreSettingsBits::RECENT, false); + Core_RegisterDebugOutputListeners(&SendDebugOutput, &SendDebugScreenshot); + // Needs to be after log so we don't interfere with test output. g_threadManager.Init(cpu_info.num_cores, cpu_info.logical_cpu_count); @@ -786,8 +781,9 @@ int main(int argc, const char* argv[]) { if (cmdLineOptions.screenshotSaveKeepAlpha.has_value()) { g_screenshotSaveKeepAlpha = cmdLineOptions.screenshotSaveKeepAlpha.value(); } + SetWriteFailureScreenshot(!getenv("GITHUB_ACTIONS") && !testOptions.bench); - SetWriteDebugOutput(!testOptions.compare && !testOptions.bench); + g_writeDebugOutput = !testOptions.compare && !testOptions.bench; #if PPSSPP_PLATFORM(ANDROID) // For some reason the debugger installs it with this name? diff --git a/ios/main.mm b/ios/main.mm index 51c646b507..ccbcee2576 100644 --- a/ios/main.mm +++ b/ios/main.mm @@ -664,9 +664,6 @@ 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/libretro/libretro.cpp b/libretro/libretro.cpp index 142a0176e4..2a11aa6d5c 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -1939,9 +1939,6 @@ int64_t System_GetPropertyInt(SystemProperty prop) { return -1; } -bool System_SendDebugOutput(std::string_view data) { return false; } -void System_SendDebugScreenshot(const uint8_t *data, int width, int height) {} - float System_GetPropertyFloat(SystemProperty prop) { switch (prop) { case SYSPROP_DISPLAY_REFRESH_RATE: diff --git a/unittest/UnitTest.cpp b/unittest/UnitTest.cpp index 4b77ccc20b..f8c3578d6d 100644 --- a/unittest/UnitTest.cpp +++ b/unittest/UnitTest.cpp @@ -127,8 +127,6 @@ 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) {} std::vector System_GetCameraDeviceList() { return std::vector(); } // Temporary hacks around annoying linking errors. Copied from Headless.