diff --git a/Makefiles/Rules.inc b/Makefiles/Rules.inc index 80facbb..0a815c1 100644 --- a/Makefiles/Rules.inc +++ b/Makefiles/Rules.inc @@ -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 \ diff --git a/Src/Graphics/IRender3D.h b/Src/Graphics/IRender3D.h index 9d3088a..a7e808f 100644 --- a/Src/Graphics/IRender3D.h +++ b/Src/Graphics/IRender3D.h @@ -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; diff --git a/Src/Graphics/Legacy3D/Legacy3D.cpp b/Src/Graphics/Legacy3D/Legacy3D.cpp index 513cd3a..6b947c9 100644 --- a/Src/Graphics/Legacy3D/Legacy3D.cpp +++ b/Src/Graphics/Legacy3D/Legacy3D.cpp @@ -963,6 +963,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); @@ -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]; @@ -1097,6 +1105,8 @@ bool CLegacy3D::Init(unsigned xOffset, unsigned yOffset, unsigned xRes, unsigned return ErrorLog("Insufficient memory for texture decode buffer."); 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)) @@ -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; diff --git a/Src/Graphics/Legacy3D/Legacy3D.h b/Src/Graphics/Legacy3D/Legacy3D.h index d4b1040..bf2f7a1 100644 --- a/Src/Graphics/Legacy3D/Legacy3D.h +++ b/Src/Graphics/Legacy3D/Legacy3D.h @@ -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 diff --git a/Src/Graphics/New3D/New3D.cpp b/Src/Graphics/New3D/New3D.cpp index 88e5be6..e908eed 100644 --- a/Src/Graphics/New3D/New3D.cpp +++ b/Src/Graphics/New3D/New3D.cpp @@ -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; diff --git a/Src/Graphics/New3D/New3D.h b/Src/Graphics/New3D/New3D.h index e2f8fa6..1b63273 100644 --- a/Src/Graphics/New3D/New3D.h +++ b/Src/Graphics/New3D/New3D.h @@ -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; @@ -300,9 +302,7 @@ private: float bnbu; float bnbw; float correction; - } m_planes; - - void CalcViewport (Viewport* vp); + } m_planes; }; } // New3D diff --git a/Src/Graphics/New3D/R3DFrameBuffers.cpp b/Src/Graphics/New3D/R3DFrameBuffers.cpp index 6948a9f..8c66399 100644 --- a/Src/Graphics/New3D/R3DFrameBuffers.cpp +++ b/Src/Graphics/New3D/R3DFrameBuffers.cpp @@ -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); diff --git a/Src/Graphics/Render2D.cpp b/Src/Graphics/Render2D.cpp index 29a7bba..487519f 100644 --- a/Src/Graphics/Render2D.cpp +++ b/Src/Graphics/Render2D.cpp @@ -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,16 +486,17 @@ 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; - m_yPixels = yRes; - m_xOffset = xOffset; - m_yOffset = yOffset; - 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_xPixels = xRes; + m_yPixels = yRes; + m_xOffset = xOffset; + m_yOffset = yOffset; + 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; } diff --git a/Src/Graphics/Render2D.h b/Src/Graphics/Render2D.h index c13b644..2aa7e68 100644 --- a/Src/Graphics/Render2D.h +++ b/Src/Graphics/Render2D.h @@ -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; diff --git a/Src/Graphics/SuperAA.cpp b/Src/Graphics/SuperAA.cpp new file mode 100644 index 0000000..3aad96d --- /dev/null +++ b/Src/Graphics/SuperAA.cpp @@ -0,0 +1,137 @@ +#include "SuperAA.h" +#include + +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) +} diff --git a/Src/Graphics/SuperAA.h b/Src/Graphics/SuperAA.h new file mode 100644 index 0000000..70d8a55 --- /dev/null +++ b/Src/Graphics/SuperAA.h @@ -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; +}; + diff --git a/Src/Model3/IEmulator.h b/Src/Model3/IEmulator.h index 46ab331..c36162d 100644 --- a/Src/Model3/IEmulator.h +++ b/Src/Model3/IEmulator.h @@ -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): diff --git a/Src/Model3/Model3.cpp b/Src/Model3/Model3.cpp index e780d95..c8b26ca 100644 --- a/Src/Model3/Model3.cpp +++ b/Src/Model3/Model3.cpp @@ -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; diff --git a/Src/Model3/Model3.h b/Src/Model3/Model3.h index 07a515c..f0bc8f7 100644 --- a/Src/Model3/Model3.h +++ b/Src/Model3/Model3.h @@ -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; diff --git a/Src/OSD/SDL/Main.cpp b/Src/OSD/SDL/Main.cpp index f8af4ee..9b28034 100644 --- a/Src/OSD/SDL/Main.cpp +++ b/Src/OSD/SDL/Main.cpp @@ -94,6 +94,7 @@ #include "Model3/Model3.h" #include "OSD/Audio.h" #include "Graphics/New3D/VBO.h" +#include "Graphics/SuperAA.h" #include #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(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() ? ((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(); @@ -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() ? ((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=, Resolution [Default: 496,384]"); + puts(" -ss= Supersampling (range 1-8)"); puts(" -window-pos=, 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 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(); + aaValue = s_runtime_config["Supersampling"].ValueAs(); + // Create a window xRes = 496; yRes = 384; diff --git a/VS2008/Supermodel.vcxproj b/VS2008/Supermodel.vcxproj index 1b61a27..e4abddc 100644 --- a/VS2008/Supermodel.vcxproj +++ b/VS2008/Supermodel.vcxproj @@ -328,6 +328,7 @@ xcopy /D /Y "$(ProjectDir)..\Assets\*" "$(TargetDir)Assets" + @@ -507,6 +508,7 @@ xcopy /D /Y "$(ProjectDir)..\Assets\*" "$(TargetDir)Assets" + diff --git a/VS2008/Supermodel.vcxproj.filters b/VS2008/Supermodel.vcxproj.filters index 8c2a4cd..acebf72 100644 --- a/VS2008/Supermodel.vcxproj.filters +++ b/VS2008/Supermodel.vcxproj.filters @@ -470,6 +470,9 @@ Source Files\Graphics + + Source Files\Graphics + @@ -856,6 +859,9 @@ Header Files\Graphics\New + + Header Files\Graphics +