Add native Libretro supersampling

This commit is contained in:
Andrea committed 2026-08-18 19:45:40 +02:00
1 parent 4be6d8222c
commit 800e6dfe11
9 files changed
+122 -69

No files matched your search

+1
View File
@@ -244,6 +244,7 @@ endif
ifneq ($(filter $(platform),android rpi64 aarch64),$(platform))
RENDERER_DEFINES += -DHAVE_CRT_COLOURS
RENDERER_DEFINES += -DHAVE_SUPERSAMPLING
endif
# GIT version
+22 -16
View File
@@ -4,9 +4,10 @@
SuperAA::SuperAA(int aaValue, CRTcolor CRTcolors) :
m_aa(aaValue),
m_crtcolors(CRTcolors),
m_vao(0),
m_width(0),
m_crtcolors(CRTcolors),
m_vao(0),
m_outputTarget(0),
m_width(0),
m_height(0)
{
if ((m_aa > 1) || (m_crtcolors != CRTcolor::None)) {
@@ -203,25 +204,30 @@ void SuperAA::Init(int width, int height)
}
}
void SuperAA::Draw()
{
if ((m_aa > 1) || (m_crtcolors != CRTcolor::None)) {
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, m_fbo.GetTextureID());
void SuperAA::Draw()
{
if ((m_aa > 1) || (m_crtcolors != CRTcolor::None)) {
glBindFramebuffer(GL_FRAMEBUFFER, m_outputTarget);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D, m_fbo.GetTextureID());
glBindVertexArray(m_vao);
glViewport(0, 0, m_width, m_height);
m_shader.EnableShader();
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
m_shader.DisableShader();
glBindVertexArray(0);
}
}
GLuint SuperAA::GetTargetID()
}
}
void SuperAA::SetOutputTarget(GLuint fbo)
{
m_outputTarget = fbo;
}
GLuint SuperAA::GetTargetID()
{
return m_fbo.GetFBOID(); // will return 0 if no render target which will be our default frame buffer (back buffer)
}
+8 -6
View File
@@ -16,17 +16,19 @@ public:
SuperAA(int aaValue, CRTcolor CRTcolors);
~SuperAA();
void Init(int width, int height); // width & height are real window dimensions
void Draw(); // this is a no-op if AA is 1 and CRTcolors 0, since we'll be drawing straight on the back buffer anyway
GLuint GetTargetID();
void Init(int width, int height); // width & height are real window dimensions
void Draw(); // this is a no-op if AA is 1 and CRTcolors 0, since we'll be drawing straight on the back buffer anyway
void SetOutputTarget(GLuint fbo); // optional resolved output FBO (0 = window backbuffer)
GLuint GetTargetID();
private:
FBO m_fbo;
GLSLShader m_shader;
const int m_aa;
const CRTcolor m_crtcolors;
GLuint m_vao;
int m_width;
GLuint m_vao;
GLuint m_outputTarget;
int m_width;
int m_height;
};
+1
View File
@@ -50,6 +50,7 @@ struct CoreOptions {
Renderer3D renderer_3d;
bool quad_rendering;
int crt_colors;
int supersampling;
int upscale_mode;
WidescreenMode widescreen_mode;
unsigned crosshairs; // native Supermodel mask: 0=off, 1=P1, 2=P2, 3=both
@@ -338,6 +338,7 @@ namespace LibretroConfigProvider {
!use_legacy_3d && g_options.quad_rendering);
config.Set("CRTcolors",
!use_legacy_3d ? g_options.crt_colors : 0);
config.Set("Supersampling", g_options.supersampling);
config.Set("UpscaleMode", g_options.upscale_mode);
config.Set("WideScreen",
g_options.widescreen_mode != WidescreenMode::Disabled);
+43 -42
View File
@@ -380,43 +380,45 @@ bool LibretroWrapper::InitRenderers()
superAA = new SuperAA(aaValue, CRTcolors);
superAA->Init(totalXRes, totalYRes);
GLuint renderTarget = superAA->GetTargetID();
// SuperAA skips FBO creation when aa==1 and no CRT filter.
// In that case we must provide our own FBO; otherwise glBlitFramebuffer
// reads from FBO-0 (the raw window backbuffer), which is invalid in libretro.
if (renderTarget == 0)
{
glGenFramebuffers(1, &m_libretrFBO);
glBindFramebuffer(GL_FRAMEBUFFER, m_libretrFBO);
glGenTextures(1, &m_libretrTex);
glBindTexture(GL_TEXTURE_2D, m_libretrTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
totalXRes, totalYRes,
0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, m_libretrTex, 0);
glGenRenderbuffers(1, &m_libretrDepth);
glBindRenderbuffer(GL_RENDERBUFFER, m_libretrDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
totalXRes, totalYRes);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, m_libretrDepth);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
ErrorLog("[Supermodel] Libretro FBO incomplete: 0x%X", status);
else
InfoLog("[Supermodel] Libretro FBO created: %ux%u (id=%u)",
totalXRes, totalYRes, m_libretrFBO);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
renderTarget = m_libretrFBO;
}
GLuint renderTarget = superAA->GetTargetID();
// Libretro always needs a base-resolution resolved target. With native
// supersampling/CRT colour active, SuperAA renders its high-resolution
// input down into this FBO. Without that pass, the Model 3 renderers draw
// here directly. Never submit SuperAA's high-resolution source FBO to the
// frontend: doing so crops the image to its lower-left base-size region.
glGenFramebuffers(1, &m_libretrFBO);
glBindFramebuffer(GL_FRAMEBUFFER, m_libretrFBO);
glGenTextures(1, &m_libretrTex);
glBindTexture(GL_TEXTURE_2D, m_libretrTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
totalXRes, totalYRes,
0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, m_libretrTex, 0);
glGenRenderbuffers(1, &m_libretrDepth);
glBindRenderbuffer(GL_RENDERBUFFER, m_libretrDepth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
totalXRes, totalYRes);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, m_libretrDepth);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
ErrorLog("[Supermodel] Libretro FBO incomplete: 0x%X", status);
else
InfoLog("[Supermodel] Libretro resolved FBO created: %ux%u (id=%u)",
totalXRes, totalYRes, m_libretrFBO);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (renderTarget != 0)
superAA->SetOutputTarget(m_libretrFBO);
else
renderTarget = m_libretrFBO;
Render2D = new CRender2D(s_runtime_config);
// Normal desktop builds can include both renderers and select at runtime.
@@ -907,11 +909,10 @@ bool LibretroWrapper::InitGL()
return InitRenderers();
}
GLuint LibretroWrapper::getSuperModelFBO() const
{
GLuint saaFBO = superAA ? superAA->GetTargetID() : 0;
return (saaFBO != 0) ? saaFBO : m_libretrFBO;
}
GLuint LibretroWrapper::getSuperModelFBO() const
{
return m_libretrFBO;
}
void LibretroWrapper::SetWidescreen(bool enabled, bool wideBackground)
{
+2 -2
View File
@@ -81,8 +81,8 @@ public:
void ShutDownSupermodel();
bool InitRenderers();
bool InitGL();
// Returns the actual FBO Supermodel renders into (SuperAA's or our own)
GLuint getSuperModelFBO() const;
// Returns the base-resolution frame resolved for Libretro submission.
GLuint getSuperModelFBO() const;
private:
float lastEngineMs = 0.0f;
+13 -3
View File
@@ -285,6 +285,7 @@ CoreOptions g_options = {
/* renderer_3d */ Renderer3D::New3D,
/* quad_rendering */ false,
/* crt_colors */ 0,
/* supersampling */ 1,
/* upscale_mode */ 2,
/* widescreen_mode */ WidescreenMode::Disabled,
/* crosshairs */ 0,
@@ -1099,6 +1100,7 @@ void retro_run(void)
Renderer3D old_renderer_3d = g_options.renderer_3d;
bool old_quad_rendering = g_options.quad_rendering;
int old_crt_colors = g_options.crt_colors;
int old_supersampling = g_options.supersampling;
WidescreenMode old_widescreen_mode = g_options.widescreen_mode;
int old_upscale_mode = g_options.upscale_mode;
bool old_legacy_sound_dsp = g_options.legacy_sound_dsp;
@@ -1123,10 +1125,11 @@ void retro_run(void)
if (g_options.renderer_3d != old_renderer_3d ||
g_options.quad_rendering != old_quad_rendering ||
g_options.crt_colors != old_crt_colors)
g_options.crt_colors != old_crt_colors ||
g_options.supersampling != old_supersampling)
{
static const struct retro_message message = {
"3D Renderer, Quad Rendering and CRT Colour changes require restarting the content.", 240
"Renderer and supersampling changes require restarting the content.", 240
};
environ_cb(RETRO_ENVIRONMENT_SET_MESSAGE, (void *)&message);
}
@@ -1338,7 +1341,14 @@ void retro_run(void)
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glViewport(0, 0, target_w, target_h);
glScissor(0, 0, target_w, target_h);
// Render2D re-enables the scissor test after clearing its target. The
// box is global GL state and must describe the supersampled render FBO,
// exactly as standalone scales its scissor by aaValue. Leaving this at
// base resolution clips a 2x source to its lower-left quarter before
// SuperAA resolves it.
const unsigned render_scale = static_cast<unsigned>(
std::max(1, wrapper.getAaValue()));
glScissor(0, 0, target_w * render_scale, target_h * render_scale);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClearDepth(1.0);
+31
View File
@@ -132,6 +132,28 @@ static struct retro_core_option_v2_definition option_defs[] = {
},
"0"
},
#endif
#ifdef HAVE_SUPERSAMPLING
{
"supermodel_supersampling",
"Supersampling",
NULL,
"Render each output pixel from multiple internal samples using Supermodel's native supersampling pass. The scale applies on top of Internal Resolution: 2x uses 4 samples per pixel, 3x uses 9, and 8x uses 64. Higher values can require substantial GPU time and memory. Takes effect after restarting content.",
NULL,
"video",
{
{ "1", "1x (Disabled, Default)" },
{ "2", "2x (4 Samples)" },
{ "3", "3x (9 Samples)" },
{ "4", "4x (16 Samples)" },
{ "5", "5x (25 Samples)" },
{ "6", "6x (36 Samples)" },
{ "7", "7x (49 Samples)" },
{ "8", "8x (64 Samples)" },
{ NULL, NULL },
},
"1"
},
#endif
{
"supermodel_timing_overlay",
@@ -571,6 +593,15 @@ void update_core_options(void)
g_options.crt_colors = 0;
#endif
#ifdef HAVE_SUPERSAMPLING
g_options.supersampling =
atoi(option_get("supermodel_supersampling", "1"));
if (g_options.supersampling < 1 || g_options.supersampling > 8)
g_options.supersampling = 1;
#else
g_options.supersampling = 1;
#endif
g_options.upscale_mode = atoi(option_get("supermodel_upscale_mode", "2"));
if (g_options.upscale_mode < 0 || g_options.upscale_mode > 3)
g_options.upscale_mode = 2;