Add supersampling anti-aliasing

Late christmas present. Due to the way alpha works on the model3 adding regular anti-aliasing doesn't really work. Supersampling is very much a brute force solution, render the scene at a higher resolution and mipmap it.

It's enabled via command line with the -ss option, for example -ss=4 for 4x supersampling or by adding Supersampling = 4 in the config file.

Note non power of two values work as well, so 3 gives a very good balance between speed and quality. 8 will make your GPU bleed, since it is essentially rendering 64 pixels for every visible pixel on the screen.
This commit is contained in:
Ian Curtis 2023-12-26 18:25:03 +00:00
parent 33b84c89aa
commit c039d08c03
17 changed files with 297 additions and 35 deletions

View file

@ -116,6 +116,7 @@ SRC_FILES = \
Src/Graphics/New3D/R3DScrollFog.cpp \
Src/Graphics/FBO.cpp \
Src/Graphics/Render2D.cpp \
Src/Graphics/SuperAA.cpp \
Src/Model3/TileGen.cpp \
Src/Model3/Model3.cpp \
Src/CPU/PowerPC/ppc.cpp \

View file

@ -17,7 +17,7 @@ public:
virtual void UploadTextures(unsigned level, unsigned x, unsigned y, unsigned width, unsigned height) = 0;
virtual void AttachMemory(const uint32_t *cullingRAMLoPtr, const uint32_t *cullingRAMHiPtr, const uint32_t *polyRAMPtr, const uint32_t *vromPtr, const uint16_t *textureRAMPtr) = 0;
virtual void SetStepping(int stepping) = 0;
virtual bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes) = 0;
virtual bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes, unsigned aaTarget) = 0;
virtual void SetSunClamp(bool enable) = 0;
virtual void SetSignedShade(bool enable) = 0;
virtual float GetLosValue(int layer) = 0;

View file

@ -964,6 +964,10 @@ void CLegacy3D::RenderFrame(void)
// Begin frame
ClearErrors(); // must be cleared each frame
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, m_aaTarget); // if we have an AA target draw to it instead of the default back buffer
}
// Z buffering (Z buffer is cleared by display list viewport nodes)
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
@ -1039,6 +1043,10 @@ void CLegacy3D::RenderFrame(void)
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, 0); // restore target if needed
}
}
void CLegacy3D::EndFrame(void)
@ -1089,7 +1097,7 @@ void CLegacy3D::SetStepping(int stepping)
DebugLog("Legacy3D set to Step %d.%d\n", (step>>4)&0xF, step&0xF);
}
bool CLegacy3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam)
bool CLegacy3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam, unsigned aaTarget)
{
// Allocate memory for texture buffer
textureBuffer = new(std::nothrow) GLfloat[1024*1024*4];
@ -1098,6 +1106,8 @@ bool CLegacy3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
glGetError(); // clear error flag
m_aaTarget = aaTarget;
// Create model caches and VBOs
if (CreateModelCache(&VROMCache, NUM_STATIC_VERTS, NUM_LOCAL_VERTS, NUM_STATIC_MODELS, 0x4000000/4, NUM_DISPLAY_LIST_ITEMS, false))
return FAIL;
@ -1310,7 +1320,8 @@ float CLegacy3D::GetLosValue(int layer)
}
CLegacy3D::CLegacy3D(const Util::Config::Node &config)
: m_config(config)
: m_config(config),
m_aaTarget(0)
{
cullingRAMLo = NULL;
cullingRAMHi = NULL;

View file

@ -328,7 +328,7 @@ public:
* occurred. Any allocated memory will not be freed until the
* destructor is called. Prints own error messages.
*/
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes, unsigned aaTarget);
/*
* SetSunClamp(bool enable);
@ -457,6 +457,7 @@ private:
GLfloat spotColor[3];
GLint viewportX, viewportY;
GLint viewportWidth, viewportHeight;
GLuint m_aaTarget;
// Scene graph processing
int listDepth; // how many lists have we recursed into

View file

@ -22,7 +22,8 @@ CNew3D::CNew3D(const Util::Config::Node &config, const std::string& gameName) :
m_r3dScrollFog(config),
m_gameName(gameName),
m_textureBuffer(0),
m_vao(0)
m_vao(0),
m_aaTarget(0)
{
m_cullingRAMLo = nullptr;
m_cullingRAMHi = nullptr;
@ -121,7 +122,7 @@ void CNew3D::SetStepping(int stepping)
}
}
bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam)
bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXResParam, unsigned totalYResParam, unsigned aaTarget)
{
// Resolution and offset within physical display area
m_xRatio = xRes * (float)(1.0 / 496.0);
@ -132,6 +133,7 @@ bool CNew3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yR
m_yRes = yRes;
m_totalXRes = totalXResParam;
m_totalYRes = totalYResParam;
m_aaTarget = aaTarget;
m_r3dFrameBuffers.DestroyFBO(); // remove any old ones if created
m_r3dFrameBuffers.CreateFBO(totalXResParam, totalYResParam);
@ -440,7 +442,16 @@ void CNew3D::RenderFrame(void)
}
m_r3dFrameBuffers.SetFBO(Layer::none);
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, m_aaTarget); // if we have an AA target draw to it instead of the default back buffer
}
m_r3dFrameBuffers.Draw();
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
}
void CNew3D::BeginFrame(void)
@ -1009,6 +1020,7 @@ void CNew3D::RenderViewport(UINT32 addr)
m_planes.bnrv = Util::Uint32AsFloat(vpnode[0x11]);
m_planes.bnbu = Util::Uint32AsFloat(vpnode[0x12]);
m_planes.bnbw = Util::Uint32AsFloat(vpnode[0x13]);
m_planes.correction = 1.0f; // might get changed by the calc viewport method
vp->angle_left = (0.0f - jo) / cv;
vp->angle_right = (1.0f - jo) / cv;

View file

@ -141,7 +141,7 @@ public:
* occurred. Any allocated memory will not be freed until the
* destructor is called. Prints own error messages.
*/
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes, unsigned aaTarget);
/*
* SetSunClamp(bool enable);
@ -223,6 +223,7 @@ private:
void DisableRenderStates();
void TranslateLosPosition(int inX, int inY, int& outX, int& outY);
bool ProcessLos(int priority);
void CalcViewport(Viewport* vp);
/*
* Data
@ -286,6 +287,7 @@ private:
R3DShader m_r3dShader;
R3DScrollFog m_r3dScrollFog;
R3DFrameBuffers m_r3dFrameBuffers;
GLuint m_aaTarget; // optional, maybe zero
int m_currentPriority;
@ -301,8 +303,6 @@ private:
float bnbw;
float correction;
} m_planes;
void CalcViewport (Viewport* vp);
};
} // New3D

View file

@ -298,7 +298,6 @@ void R3DFrameBuffers::AllocShaderTrans()
void R3DFrameBuffers::Draw()
{
SetFBO (Layer::none); // make sure to draw on the back buffer
glViewport (0, 0, m_width, m_height); // cover the entire screen
glDisable (GL_DEPTH_TEST); // disable depth testing / writing
glDisable (GL_CULL_FACE);

View file

@ -310,11 +310,19 @@ void CRender2D::Setup2D(bool isBottom)
// Clear everything if requested or just overscan areas for wide screen mode
if (isBottom)
{
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, m_aaTarget); // set target if needed
}
glClearColor(0.0, 0.0, 0.0, 0.0);
glViewport (0, 0, m_totalXPixels, m_totalYPixels);
glDisable (GL_SCISSOR_TEST); // scissor is enabled to fix the 2d/3d miss match problem
glClear (GL_COLOR_BUFFER_BIT); // we want to clear outside the scissored areas so must disable it
glEnable (GL_SCISSOR_TEST);
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, 0); // restore target if needed
}
}
// Set up the viewport and orthogonal projection
@ -327,6 +335,10 @@ void CRender2D::Setup2D(bool isBottom)
void CRender2D::DrawSurface(GLuint textureID)
{
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, m_aaTarget); // set target if needed
}
m_shader.EnableShader();
glEnable (GL_BLEND);
@ -338,6 +350,10 @@ void CRender2D::DrawSurface(GLuint textureID)
glDisable (GL_BLEND);
m_shader.DisableShader();
if (m_aaTarget) {
glBindFramebuffer(GL_FRAMEBUFFER, 0); // restore target if needed
}
}
float CRender2D::LineToPercentStart(int lineNumber)
@ -470,7 +486,7 @@ void CRender2D::AttachVRAM(const uint8_t* vramPtr)
DebugLog("Render2D attached VRAM\n");
}
bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes)
bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes, unsigned aaTarget)
{
// Resolution
m_xPixels = xRes;
@ -480,6 +496,7 @@ bool CRender2D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned
m_totalXPixels = totalXRes;
m_totalYPixels = totalYRes;
m_correction = (UINT32)(((yRes / 384.f) * 2) + 0.5f); // for some reason the 2d layer is 2 pixels off the 3D
m_aaTarget = aaTarget;
return OKAY;
}

View file

@ -158,7 +158,7 @@ public:
* OKAY is successful, otherwise FAILED if a non-recoverable error
* occurred. Prints own error messages.
*/
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes);
bool Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned yRes, unsigned totalXRes, unsigned totalYRes, unsigned aaTarget);
/*
* CRender2D(config):
@ -198,6 +198,7 @@ private:
unsigned m_totalXPixels = 0; // total display surface resolution
unsigned m_totalYPixels = 0;
unsigned m_correction = 0;
GLuint m_aaTarget = 0;
GLuint m_vao;
GLSLShader m_shader;

137
Src/Graphics/SuperAA.cpp Normal file
View file

@ -0,0 +1,137 @@
#include "SuperAA.h"
#include <string>
SuperAA::SuperAA(int aaValue) :
m_aa(aaValue),
m_vao(0),
m_width(0),
m_height(0)
{
if (aaValue > 1) {
const char* vertexShader = R"glsl(
#version 410 core
// outputs
out vec2 fsTexCoord;
void main(void)
{
const vec4 vertices[] = vec4[](vec4(-1.0, -1.0, 0.0, 1.0),
vec4(-1.0, 1.0, 0.0, 1.0),
vec4( 1.0, -1.0, 0.0, 1.0),
vec4( 1.0, 1.0, 0.0, 1.0));
fsTexCoord = (vertices[gl_VertexID % 4].xy + 1.0) / 2.0;
gl_Position = vertices[gl_VertexID % 4];
}
)glsl";
std::string fragmentShaderVersion = R"glsl(
#version 410 core
)glsl";
std::string aaString = "const int aa = ";
aaString += std::to_string(aaValue);
aaString += ";\n";
std::string fragmentShader = R"glsl(
// inputs
uniform sampler2D tex1; // base tex
in vec2 fsTexCoord;
// outputs
out vec4 fragColor;
vec4 GetTextureValue(sampler2D s, vec2 texCoord)
{
vec2 size = vec2(textureSize(s,0)); // want the values as floats
ivec2 texPos = ivec2(size * texCoord);
vec4 texColour = vec4(0.0);
for(int i=0; i < aa; i++) {
for(int j=0; j < aa; j++) {
texColour += texelFetch(s,ivec2(texPos.x+i,texPos.y+j),0);
}
}
texColour /= float(aa * aa);
return texColour;
}
void main()
{
fragColor = GetTextureValue(tex1, fsTexCoord);
}
)glsl";
std::string fragmentShaderString = fragmentShaderVersion + aaString + fragmentShader;
// load shaders
m_shader.LoadShaders(vertexShader, fragmentShaderString.c_str());
m_shader.GetUniformLocationMap("tex1");
// setup uniform memory
m_shader.EnableShader();
glUniform1i(m_shader.attribLocMap["tex1"], 0); // texture will be bound to unit zero
m_shader.DisableShader();
glGenVertexArrays(1, &m_vao);
glBindVertexArray(m_vao);
// no states needed since we do it in the shader
glBindVertexArray(0);
}
}
// need an active context bound to the current thread to destroy our objects
SuperAA::~SuperAA()
{
m_shader.UnloadShaders();
m_fbo.Destroy();
if (m_vao) {
glDeleteVertexArrays(1, &m_vao);
m_vao = 0;
}
}
void SuperAA::Init(int width, int height)
{
if (m_aa > 1) {
m_fbo.Destroy();
m_fbo.Create(width * m_aa, height * m_aa);
m_width = width;
m_height = height;
}
}
void SuperAA::Draw()
{
if (m_aa > 1) {
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()
{
return m_fbo.GetFBOID(); // will return 0 if no render target which will be our default frame buffer (back buffer)
}

31
Src/Graphics/SuperAA.h Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include "FBO.h"
#include "New3D/GLSLShader.h"
// This class just implements super sampling. Super sampling looks fantastic but is quite expensive.
// 8x and beyond values can start to eat ridiculous amounts of memory / gpu time, for less and less noticable returns
// 4x works and looks great
// values such as 3 are also possible, that works out 9 samples per pixel
// The algorithm is super simple, just add up all samples and divide by the number
class SuperAA
{
public:
SuperAA(int aaValue);
~SuperAA();
void Init(int width, int height); // width & height are real window dimensions
void Draw(); // this is a no-op if AA is 1, since we'll be drawing straight on the back buffer anyway
GLuint GetTargetID();
private:
FBO m_fbo;
GLSLShader m_shader;
const int m_aa;
GLuint m_vao;
int m_width;
int m_height;
};

View file

@ -35,6 +35,7 @@ class CRender2D;
class IRender3D;
class CInputs;
class COutputs;
class SuperAA;
/*
* IEmulator:
@ -150,7 +151,7 @@ public:
* Render2DPtr Pointer to a tile renderer object.
* Render3DPtr Same as above but for a 3D renderer.
*/
virtual void AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr) = 0;
virtual void AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr, SuperAA *superAA) = 0;
/*
* AttachInputs(InputsPtr):

View file

@ -2163,6 +2163,7 @@ void CModel3::RenderFrame(void)
TileGen.RenderFrameTop();
GPU.EndFrame();
TileGen.EndFrame();
m_superAA->Draw();
}
EndFrameVideo();
@ -3062,10 +3063,11 @@ bool CModel3::LoadGame(const Game &game, const ROMSet &rom_set)
return OKAY;
}
void CModel3::AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr)
void CModel3::AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr, SuperAA *superAA)
{
TileGen.AttachRenderer(Render2DPtr);
GPU.AttachRenderer(Render3DPtr);
m_superAA = superAA;
}
void CModel3::AttachInputs(CInputs *InputsPtr)
@ -3209,7 +3211,8 @@ CModel3::CModel3(Util::Config::Node &config)
TileGen(config),
GPU(config),
SoundBoard(config),
m_jtag(GPU)
m_jtag(GPU),
m_superAA(nullptr)
{
// Initialize pointers so dtor can know whether to free them
memoryPool = NULL;

View file

@ -45,6 +45,8 @@
#include "Network/INetBoard.h"
#endif // NET_BOARD
#include "Util/NewConfig.h"
#include "Graphics/SuperAA.h"
/*
* FrameTimings
@ -92,7 +94,7 @@ public:
void RenderFrame(void);
void Reset(void);
const Game &GetGame(void) const;
void AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr);
void AttachRenderers(CRender2D *Render2DPtr, IRender3D *Render3DPtr, SuperAA *superAA);
void AttachInputs(CInputs *InputsPtr);
void AttachOutputs(COutputs *OutputsPtr);
bool Init(void);
@ -321,6 +323,7 @@ private:
CDriveBoard *DriveBoard; // Drive board
CCrypto m_cryptoDevice; // Encryption device
CJTAG m_jtag; // JTAG interface
SuperAA *m_superAA;
#ifdef NET_BOARD
INetBoard *NetBoard; // Net board
bool m_runNetBoard;

View file

@ -94,6 +94,7 @@
#include "Model3/Model3.h"
#include "OSD/Audio.h"
#include "Graphics/New3D/VBO.h"
#include "Graphics/SuperAA.h"
#include <iostream>
#include "Util/BMPFile.h"
@ -122,6 +123,7 @@ SDL_Window *s_window = nullptr;
static unsigned xOffset, yOffset; // offset of renderer output within OpenGL viewport
static unsigned xRes, yRes; // renderer output resolution (can be smaller than GL viewport)
static unsigned totalXRes, totalYRes; // total resolution (the whole GL viewport)
static int aaValue = 1; // default is 1 which is no aa
/*
* Crosshair stuff
@ -185,11 +187,11 @@ static bool SetGLGeometry(unsigned *xOffsetPtr, unsigned *yOffsetPtr, unsigned *
// Scissor box (to clip visible area)
if (s_runtime_config["WideScreen"].ValueAsDefault<bool>(false))
{
glScissor(0, correction, *totalXResPtr, *totalYResPtr - (correction * 2));
glScissor(0* aaValue, correction* aaValue, *totalXResPtr * aaValue, (*totalYResPtr - (correction * 2)) * aaValue);
}
else
{
glScissor(*xOffsetPtr + correction, *yOffsetPtr + correction, *xResPtr - (correction * 2), *yResPtr - (correction * 2));
glScissor((*xOffsetPtr + correction) * aaValue, (*yOffsetPtr + correction) * aaValue, (*xResPtr - (correction * 2)) * aaValue, (*yResPtr - (correction * 2)) * aaValue);
}
return OKAY;
}
@ -971,13 +973,17 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
uint64_t nextTime = 0;
// Initialize the renderers
SuperAA* superAA = new SuperAA(aaValue);
superAA->Init(totalXRes, totalYRes); // pass actual frame sizes here
CRender2D *Render2D = new CRender2D(s_runtime_config);
IRender3D *Render3D = s_runtime_config["New3DEngine"].ValueAs<bool>() ? ((IRender3D *) new New3D::CNew3D(s_runtime_config, Model3->GetGame().name)) : ((IRender3D *) new Legacy3D::CLegacy3D(s_runtime_config));
if (OKAY != Render2D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
if (OKAY != Render2D->Init(xOffset*aaValue, yOffset*aaValue, xRes*aaValue, yRes*aaValue, totalXRes*aaValue, totalYRes*aaValue, superAA->GetTargetID()))
goto QuitError;
if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
if (OKAY != Render3D->Init(xOffset*aaValue, yOffset*aaValue, xRes*aaValue, yRes*aaValue, totalXRes*aaValue, totalYRes*aaValue, superAA->GetTargetID()))
goto QuitError;
Model3->AttachRenderers(Render2D,Render3D);
Model3->AttachRenderers(Render2D,Render3D, superAA);
// Reset emulator
Model3->Reset();
@ -1105,8 +1111,8 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
// Delete renderers and recreate them afterwards since GL context will most likely be lost when switching from/to fullscreen
delete Render2D;
delete Render3D;
Render2D = NULL;
Render3D = NULL;
Render2D = nullptr;
Render3D = nullptr;
// Resize screen
totalXRes = xRes = s_runtime_config["XResolution"].ValueAs<unsigned>();
@ -1117,13 +1123,15 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
goto QuitError;
// Recreate renderers and attach to the emulator
superAA->Init(totalXRes, totalYRes);
Render2D = new CRender2D(s_runtime_config);
Render3D = s_runtime_config["New3DEngine"].ValueAs<bool>() ? ((IRender3D *) new New3D::CNew3D(s_runtime_config, Model3->GetGame().name)) : ((IRender3D *) new Legacy3D::CLegacy3D(s_runtime_config));
if (OKAY != Render2D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
if (OKAY != Render2D->Init(xOffset * aaValue, yOffset * aaValue, xRes * aaValue, yRes * aaValue, totalXRes * aaValue, totalYRes * aaValue, superAA->GetTargetID()))
goto QuitError;
if (OKAY != Render3D->Init(xOffset, yOffset, xRes, yRes, totalXRes, totalYRes))
if (OKAY != Render3D->Init(xOffset * aaValue, yOffset * aaValue, xRes * aaValue, yRes * aaValue, totalXRes * aaValue, totalYRes * aaValue, superAA->GetTargetID()))
goto QuitError;
Model3->AttachRenderers(Render2D,Render3D);
Model3->AttachRenderers(Render2D, Render3D, superAA);
Render3D->UploadTextures(0, 0, 0, 2048, 2048); // sync texture memory
@ -1331,6 +1339,7 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
// Shut down renderers
delete Render2D;
delete Render3D;
delete superAA;
return 0;
@ -1338,6 +1347,8 @@ int Supermodel(const Game &game, ROMSet *rom_set, IEmulator *Model3, CInputs *In
QuitError:
delete Render2D;
delete Render3D;
delete superAA;
return 1;
}
@ -1478,7 +1489,7 @@ static Util::Config::Node DefaultConfig()
config.SetEmpty("WindowYPosition");
config.Set("FullScreen", false);
config.Set("BorderlessWindow", false);
config.Set("Supersampling", 1);
config.Set("WideScreen", false);
config.Set("Stretch", false);
config.Set("WideBackground", false);
@ -1558,6 +1569,7 @@ static void Help(void)
puts("");
puts("Video Options:");
puts(" -res=<x>,<y> Resolution [Default: 496,384]");
puts(" -ss=<n> Supersampling (range 1-8)");
puts(" -window-pos=<x>,<y> Window position [Default: centered]");
puts(" -window Windowed mode [Default]");
puts(" -borderless Windowed mode with no border");
@ -1819,6 +1831,29 @@ static ParsedCommandLine ParseCommandLine(int argc, char **argv)
}
}
}
else if (arg == "-ss" || arg.find("-ss=") == 0) {
std::vector<std::string> parts = Util::Format(arg).Split('=');
if (parts.size() != 2)
{
ErrorLog("'-ss' requires an integer argument (e.g., '-ss=2').");
cmd_line.error = true;
}
else {
try {
int val = std::stoi(parts[1]);
val = std::clamp(val, 1, 8);
cmd_line.config.Set("Supersampling", val);
}
catch (...) {
ErrorLog("'-ss' requires an integer argument (e.g., '-ss=2').");
cmd_line.error = true;
}
}
}
else if (arg == "-true-hz")
cmd_line.config.Set("RefreshRate", 57.524f);
else if (arg == "-print-gl-info")
@ -1967,6 +2002,8 @@ int main(int argc, char **argv)
#endif // SUPERMODEL_DEBUGGER
std::string selectedInputSystem = s_runtime_config["InputSystem"].ValueAs<std::string>();
aaValue = s_runtime_config["Supersampling"].ValueAs<int>();
// Create a window
xRes = 496;
yRes = 384;

View file

@ -328,6 +328,7 @@ xcopy /D /Y "$(ProjectDir)..\Assets\*" "$(TargetDir)Assets"</Command>
<ClCompile Include="..\Src\Graphics\New3D\Vec.cpp" />
<ClCompile Include="..\Src\Graphics\Render2D.cpp" />
<ClCompile Include="..\Src\Graphics\Shader.cpp" />
<ClCompile Include="..\Src\Graphics\SuperAA.cpp" />
<ClCompile Include="..\Src\Inputs\Input.cpp" />
<ClCompile Include="..\Src\Inputs\Inputs.cpp" />
<ClCompile Include="..\Src\Inputs\InputSource.cpp" />
@ -507,6 +508,7 @@ xcopy /D /Y "$(ProjectDir)..\Assets\*" "$(TargetDir)Assets"</Command>
<ClInclude Include="..\Src\Graphics\Render2D.h" />
<ClInclude Include="..\Src\Graphics\Shader.h" />
<ClInclude Include="..\Src\Graphics\Shaders2D.h" />
<ClInclude Include="..\Src\Graphics\SuperAA.h" />
<ClInclude Include="..\Src\Inputs\Input.h" />
<ClInclude Include="..\Src\Inputs\Inputs.h" />
<ClInclude Include="..\Src\Inputs\InputSource.h" />

View file

@ -470,6 +470,9 @@
<ClCompile Include="..\Src\Graphics\FBO.cpp">
<Filter>Source Files\Graphics</Filter>
</ClCompile>
<ClCompile Include="..\Src\Graphics\SuperAA.cpp">
<Filter>Source Files\Graphics</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<MASM Include="..\Src\CPU\68K\Turbo68K\Turbo68K.asm">
@ -856,6 +859,9 @@
<ClInclude Include="..\Src\Graphics\New3D\R3DShaderCommon.h">
<Filter>Header Files\Graphics\New</Filter>
</ClInclude>
<ClInclude Include="..\Src\Graphics\SuperAA.h">
<Filter>Header Files\Graphics</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\Src\Debugger\ReadMe.txt">