Files
ppsspp/libretro/LibretroGraphicsContext.h
T
libretroadmin 320e4554c4 libretro: fix software renderer OOB read, oversized buffer, extra copy
SOFT_BMP_SIZE was a byte count used as the element count of a u16
array, keeping ~1 MB resident per context, and SwapBuffers memcpy'd a
full frame out of the vector ConvertFramebufferForLibretro returns empty
for unsupported formats, a heap OOB read. The intermediate buffer and
its per-frame memcpy are gone: the converted vector goes to video_cb
directly, with the 16:9 crop done by pointer offset. Before a GPU exists,
or when conversion fails, a frame dupe is sent instead of stale data.
2026-10-08 09:18:49 +02:00

125 lines
3.3 KiB
C++

#pragma once
#include <atomic>
#include <libretro.h>
#include "Common/GPU/GraphicsContext.h"
#include "Common/GPU/thin3d_create.h"
#include "Common/CommonTypes.h"
#include "Core/Config.h"
#include "Core/System.h"
#include "GPU/GPUState.h"
#include "GPU/Software/SoftGpu.h"
#include "headless/Compare.h"
#include "Common/Data/Convert/ColorConv.h"
#define NATIVEWIDTH 480
#define NATIVEHEIGHT 272
class LibretroGraphicsContext : public GraphicsContext {
public:
LibretroGraphicsContext() {}
~LibretroGraphicsContext() override { ShutdownAPI(); }
virtual void SetRenderTarget() {}
virtual GPUCore GetGPUCore() = 0;
virtual const char *Ident() = 0;
void ShutdownAPI() override {
DestroyDrawContext();
}
virtual void SwapBuffers() = 0;
void Resize() override {}
// True if ThreadFrame() has queued work to run (emu thread backends only).
virtual bool ThreadFramePending() { return false; }
virtual void GotBackbuffer();
virtual void LostBackbuffer();
virtual void CreateDrawContext() {}
virtual void DestroyDrawContext() {
if (!draw_) {
return;
}
delete draw_;
draw_ = nullptr;
}
Draw::DrawContext *GetDrawContext() override { return draw_; }
static LibretroGraphicsContext *CreateGraphicsContext();
static retro_video_refresh_t video_cb;
protected:
Draw::DrawContext *draw_ = nullptr;
};
class LibretroHWRenderContext : public LibretroGraphicsContext {
public:
LibretroHWRenderContext(retro_hw_context_type context_type, unsigned version_major = 0, unsigned version_minor = 0);
bool InitHW(bool cache_context);
void SetRenderTarget() override {}
void SwapBuffers() override {
video_cb(RETRO_HW_FRAME_BUFFER_VALID, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight, 0);
}
virtual void ContextReset();
virtual void ContextDestroy();
protected:
retro_hw_render_callback hw_render_ = {};
};
std::vector<u32> ConvertFramebufferForLibretro(const GPUDebugBuffer *buffer, u32 stride, u32 h);
class LibretroSoftwareContext : public LibretroGraphicsContext {
public:
LibretroSoftwareContext() {}
void SwapBuffers() override {
u32 w = NATIVEWIDTH;
u32 h = NATIVEHEIGHT;
if (!gpu) {
// Nothing rendered yet, dupe the previous frame.
video_cb(NULL, w, h, w * sizeof(u32));
return;
}
GPUDebugBuffer buf;
gpu->GetOutputFramebuffer(buf);
// video_cb is synchronous, so the converted vector can be handed over directly.
const std::vector<u32> pixels = ConvertFramebufferForLibretro(&buf, w, h);
if (pixels.size() < (size_t)w * h) {
// Unsupported debug buffer format, conversion returned nothing.
video_cb(NULL, w, h, w * sizeof(u32));
return;
}
const u32 *data = pixels.data();
if (g_Config.bDisplayCropTo16x9) {
// 480x272 -> 480x270: skip the first row, drop the last.
data += w;
h -= 2;
}
video_cb(data, w, h, w * sizeof(u32));
}
GPUCore GetGPUCore() override { return GPUCORE_SOFTWARE; }
const char *Ident() override { return "Software"; }
};
namespace Libretro {
extern LibretroGraphicsContext *ctx;
extern retro_environment_t environ_cb;
extern retro_hw_context_type backend;
enum class EmuThreadState {
DISABLED,
RUNNING,
PAUSE_REQUESTED,
PAUSED,
QUIT_REQUESTED,
STOPPED,
};
extern bool useEmuThread;
extern std::atomic<EmuThreadState> emuThreadState;
void EmuThreadStart();
void EmuThreadStop();
void EmuThreadPause();
} // namespace Libretro