Replace System_SendDebugOutput with a registered callback

I normally try to avoid registrations when not needed, but in this case
only headless uses this, so it's motivated.
This commit is contained in:
Henrik Rydgård committed 2026-08-11 22:36:47 +02:00
1 parent e9e27382af
commit a44c2c0bde
18 files changed
+72 -55

No files matched your search

-2
View File
@@ -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);
+22
View File
@@ -155,6 +155,28 @@ static MIPSExceptionInfo g_exceptionInfo;
// This is called on EmuThread before RunLoop.
static bool Core_ProcessStepping(MIPSDebugInterface *cpu);
static std::function<void(std::string_view)> g_debugOutputListener;
static std::function<void(const DebugScreenshotDesc &)> g_debugScreenshotListener;
void Core_RegisterDebugOutputListeners(std::function<void(std::string_view)> listener, std::function<void(const DebugScreenshotDesc &)> 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;
}
+15
View File
@@ -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<void(std::string_view)> listener, std::function<void(const DebugScreenshotDesc &)> 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);
+1 -1
View File
@@ -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<std::mutex> guard(pendingWriteMutex);
+1 -1
View File
@@ -940,7 +940,7 @@ int sceDisplaySetFramebuf(u32 topaddr, int linesize, int pixelformat, int sync)
}
}
bool __DisplayGetFramebuf(PSPPointer<u8> *topaddr, u32 *linesize, u32 *pixelFormat, int latchedMode) {
bool __DisplayGetFramebuf(PSPPointer<u8> *topaddr, u32 *linesize, GEBufferFormat *pixelFormat, int latchedMode) {
const FrameBufferState &fbState = latchedMode == PSP_DISPLAY_SETBUF_NEXTFRAME ? latchedFramebuf : framebuf;
if (topaddr != nullptr)
(*topaddr).ptr = fbState.topaddr;
+3 -1
View File
@@ -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<u8> *topaddr, u32 *linesize, u32 *pixelFormat, int mode);
bool __DisplayGetFramebuf(PSPPointer<u8> *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
+8 -7
View File
@@ -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<u8> 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<u8> 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:
+5 -3
View File
@@ -1913,10 +1913,12 @@ int __KernelGPUReplay() {
}
if (result == GPURecord::ReplayResult::Done && PSP_CoreParameter().headLess && !PSP_CoreParameter().startBreak) {
DebugScreenshotDesc desc;
PSPPointer<u8> 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();
}
+2 -1
View File
@@ -798,7 +798,8 @@ void Recorder::NotifyBeginFrame() {
CheckEdramTrans();
struct DisplayBufData {
PSPPointer<u8> topaddr;
u32 linesize, pixelFormat;
u32 linesize;
GEBufferFormat pixelFormat;
};
DisplayBufData disp;
-3
View File
@@ -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) {
+1 -1
View File
@@ -268,7 +268,7 @@ void DrawDisplayWindow(ImConfig &cfg, FramebufferManagerCommon *framebufferManag
PSPPointer<u8> topaddr;
u32 linesize;
u32 pixelFormat;
GEBufferFormat pixelFormat;
__DisplayGetFramebuf(&topaddr, &linesize, &pixelFormat, cfg.displayLatched);
-3
View File
@@ -402,9 +402,6 @@ std::vector<std::string> 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) {
-3
View File
@@ -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
-3
View File
@@ -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:
+14 -18
View File
@@ -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<u32> pixels = TranslateDebugBufferToCompare(&buffer, 512, 272);
const std::vector<u32> 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?
-3
View File
@@ -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)) {
-3
View File
@@ -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:
-2
View File
@@ -127,8 +127,6 @@ void System_RunOnMainThread(std::function<void()>) {}
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<std::string> System_GetCameraDeviceList() { return std::vector<std::string>(); }
// Temporary hacks around annoying linking errors. Copied from Headless.