From 9f9898fb9479fe9ec19a4fd038b88fa638cde760 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Mon, 2 Nov 2026 23:04:09 -0500 Subject: [PATCH 1/2] Resolution Scaling (no SSAA) --- MarathonRecomp/CMakeLists.txt | 1 + MarathonRecomp/gpu/render_resolution.cpp | 72 ++++++++++++ MarathonRecomp/gpu/render_resolution.h | 13 +++ MarathonRecomp/gpu/video.cpp | 67 ++++------- MarathonRecomp/gpu/video.h | 21 ++++ .../patches/aspect_ratio_patches.cpp | 55 ++++++++- MarathonRecomp/patches/video_patches.cpp | 104 +++--------------- MarathonRecomp/ui/options_menu.cpp | 2 +- MarathonRecompLib/config/Marathon.toml.in | 26 +++-- 9 files changed, 218 insertions(+), 143 deletions(-) create mode 100644 MarathonRecomp/gpu/render_resolution.cpp create mode 100644 MarathonRecomp/gpu/render_resolution.h diff --git a/MarathonRecomp/CMakeLists.txt b/MarathonRecomp/CMakeLists.txt index 33c6c8f2a..34b33253c 100644 --- a/MarathonRecomp/CMakeLists.txt +++ b/MarathonRecomp/CMakeLists.txt @@ -123,6 +123,7 @@ set(MARATHON_RECOMP_CPU_CXX_SOURCES ) set(MARATHON_RECOMP_GPU_CXX_SOURCES + "gpu/render_resolution.cpp" "gpu/video.cpp" "gpu/resources.cpp" "gpu/guest_convert.cpp" diff --git a/MarathonRecomp/gpu/render_resolution.cpp b/MarathonRecomp/gpu/render_resolution.cpp new file mode 100644 index 000000000..610add0bc --- /dev/null +++ b/MarathonRecomp/gpu/render_resolution.cpp @@ -0,0 +1,72 @@ +#include "render_resolution.h" +#include +#include +#include + +static uint32_t ScaleDimension(uint32_t value, float scale) +{ + return uint32_t(float(value) * scale); +} + +static float GetReflectionScale() +{ + switch (Config::ReflectionResolution) + { + case EReflectionResolution::Eighth: + return 0.5f; + case EReflectionResolution::Half: + return 2.0f; + case EReflectionResolution::Full: + return 4.0f; + default: + return 1.0f; + } +} + +BufferSize RenderResolution::GetInternalSize() +{ + return { ScaleDimension(Video::s_viewportWidth, Config::ResolutionScale), ScaleDimension(Video::s_viewportHeight, Config::ResolutionScale) }; +} + +std::optional RenderResolution::FindBufferSize(std::string_view name, const char* pBlockName) +{ + const auto internalSize = GetInternalSize(); + const auto blockName = std::string_view(pBlockName != nullptr ? pBlockName : ""); + + if (name == "framebuffer0" || name == "framebuffer1" || name == "framebuffer_hdr") + return internalSize; + + if (name.starts_with("coframebuffer") || name == "codepthstencil") + return BufferSize { internalSize.Width / 2, internalSize.Height }; + + // Downsampled chain, e.g. "framebuffer_1_4_0" is a quarter of the internal resolution. + if (name.starts_with("framebuffer_1_")) + { + uint32_t divisor = 0; + std::from_chars(name.data() + 14, name.data() + name.size(), divisor); + + if (divisor != 0) + return BufferSize { internalSize.Width / divisor, internalSize.Height / divisor }; + } + + // Reflections are an eighth of the internal resolution at the default setting. + if (name == "reflection0" || name == "depthstencil_1_4") + return BufferSize { ScaleDimension(internalSize.Width >> 3, GetReflectionScale()), ScaleDimension(internalSize.Height >> 3, GetReflectionScale()) }; + + if (name == "radermap" || blockName == "radermap0") + return BufferSize { uint32_t(g_radarMapScale), uint32_t(g_radarMapScale) }; + + const auto shadowResolution = uint32_t(Config::ShadowResolution.Value); + + if (shadowResolution > 0) + { + // RenderMefiress + if (name == "user0" || (blockName == "user0" && name == "depthstencil_256")) + return BufferSize { shadowResolution, shadowResolution }; + + if (name == "csm") + return BufferSize { shadowResolution, shadowResolution }; + } + + return {}; +} diff --git a/MarathonRecomp/gpu/render_resolution.h b/MarathonRecomp/gpu/render_resolution.h new file mode 100644 index 000000000..f8f7702fe --- /dev/null +++ b/MarathonRecomp/gpu/render_resolution.h @@ -0,0 +1,13 @@ +#pragma once + +struct BufferSize +{ + uint32_t Width{}; + uint32_t Height{}; +}; + +namespace RenderResolution +{ + BufferSize GetInternalSize(); + std::optional FindBufferSize(std::string_view name, const char* pBlockName); +} diff --git a/MarathonRecomp/gpu/video.cpp b/MarathonRecomp/gpu/video.cpp index 8bc38a343..f4005d704 100644 --- a/MarathonRecomp/gpu/video.cpp +++ b/MarathonRecomp/gpu/video.cpp @@ -3,13 +3,14 @@ #include "guest_convert.h" #include "guest_device.h" #include "guest_shader.h" -#include "surface_variant.h" -#include "video_internal.h" -#include "pipeline.h" #include "imgui/imgui_common.h" #include "imgui/imgui_snapshot.h" +#include "pipeline.h" #include "render_command.h" +#include "render_resolution.h" #include "resources.h" +#include "surface_variant.h" +#include "video_internal.h" #include #include @@ -2472,38 +2473,14 @@ void Video::Resize(uint32_t width, uint32_t height) if (!pRenderTargetContainer || !pMyGraphicsDevice) return; - width *= Config::ResolutionScale; - height *= Config::ResolutionScale; + const auto internalSize = RenderResolution::GetInternalSize(); - if (!width || !height) + if (!internalSize.Width || !internalSize.Height) { LOGN_ERROR("Attempting to resize buffers to 0x0."); return; } - struct BufferInfo - { - uint32_t Width; - uint32_t Height; - uint32_t Index; - uint32_t Flags; - }; - - static std::map buffers{}; - - // Update buffer dimensions. - buffers["framebuffer0"] = { width, height, 0, 4 }; - buffers["framebuffer1"] = { width, height, 0, 0 }; - buffers["framebuffer_1_4_0"] = { width >> 2, height >> 2, 3, 2 }; - buffers["framebuffer_1_4_1"] = { width >> 2, height >> 2, 0, 2 }; - buffers["framebuffer_1_8_0"] = { width >> 3, height >> 3, 3, 2 }; - buffers["framebuffer_1_8_1"] = { width >> 3, height >> 3, 0, 2 }; - buffers["framebuffer_1_16_0"] = { width >> 4, height >> 4, 3, 2 }; - buffers["framebuffer_1_16_1"] = { width >> 4, height >> 4, 0, 2 }; - buffers["framebuffer_1_32_0"] = { width >> 5, height >> 5, 3, 2 }; - buffers["framebuffer_1_32_1"] = { width >> 5, height >> 5, 0, 2 }; - buffers["depthstencil_1_4"] = { width, height, 6, 0 }; - if (g_backBuffer && g_backBuffer != pApp->m_pBackBufferSurface.get()) g_backBuffer->Release(); @@ -2513,7 +2490,7 @@ void Video::Resize(uint32_t width, uint32_t height) auto& rDeviceInfo = pApp->m_DeviceInfo; auto surfaceParams = D3DXBSURFACE_PARAMETERS(0, 0, 0); - const auto surfaceBase = static_cast(height * 1.155555555555556); // 720p: 0x340 + const auto surfaceBase = static_cast(internalSize.Height * 1.155555555555556); // 720p: 0x340 // Recreate main buffers. ReleaseResource(static_cast(pApp->m_pFrontBufferTexture.get())); @@ -2525,21 +2502,21 @@ void Video::Resize(uint32_t width, uint32_t height) surfaceParams.Base = surfaceParams.Base + surfaceBase; ReleaseResource(static_cast(pApp->m_pDepthStencilSurface.get())); - pApp->m_pDepthStencilSurface = CreateSurface(width, height, D3DFMT_D24FS8, 0, reinterpret_cast(&surfaceParams)); + pApp->m_pDepthStencilSurface = CreateSurface(internalSize.Width, internalSize.Height, D3DFMT_D24FS8, 0, reinterpret_cast(&surfaceParams)); rDeviceInfo.SurfaceParamsB = surfaceParams; rDeviceInfo.SurfaceParamsC = rDeviceInfo.SurfaceParamsB; rDeviceInfo.SurfaceParamsC.Base = rDeviceInfo.SurfaceParamsB.Base + surfaceBase; ReleaseResource(static_cast(pApp->m_pColorTile2x.get())); - pApp->m_pColorTile2x = CreateSurface(width, height, D3DFMT_A16B16G16R16F_2, 1, reinterpret_cast(&surfaceParams)); + pApp->m_pColorTile2x = CreateSurface(internalSize.Width, internalSize.Height, D3DFMT_A16B16G16R16F_2, 1, reinterpret_cast(&surfaceParams)); ReleaseResource(static_cast(pApp->m_pDepthTile2x.get())); - pApp->m_pDepthTile2x = CreateSurface(width, height, D3DFMT_D24FS8, 1, reinterpret_cast(&surfaceParams)); + pApp->m_pDepthTile2x = CreateSurface(internalSize.Width, internalSize.Height, D3DFMT_D24FS8, 1, reinterpret_cast(&surfaceParams)); ReleaseResource(static_cast(pApp->m_pColorTile4x.get())); - pApp->m_pColorTile4x = CreateSurface(width, height, D3DFMT_A16B16G16R16F_2, 2, reinterpret_cast(&surfaceParams)); + pApp->m_pColorTile4x = CreateSurface(internalSize.Width, internalSize.Height, D3DFMT_A16B16G16R16F_2, 2, reinterpret_cast(&surfaceParams)); ReleaseResource(static_cast(pApp->m_pDepthTile4x.get())); - pApp->m_pDepthTile4x = CreateSurface(width, height, D3DFMT_D24FS8, 2, reinterpret_cast(&surfaceParams)); + pApp->m_pDepthTile4x = CreateSurface(internalSize.Width, internalSize.Height, D3DFMT_D24FS8, 2, reinterpret_cast(&surfaceParams)); // Viewport is reset here because we're using the game's backbuffer directly. SetRenderTarget(static_cast(pApp->m_pDevice.get()), 0, static_cast(pApp->m_pBackBufferSurface.get())); @@ -2605,15 +2582,16 @@ void Video::Resize(uint32_t width, uint32_t height) { const auto pSurfaceName = rSurface.first.c_str(); - if (!buffers.contains(pSurfaceName)) + if (!g_BufferInfo.contains(pSurfaceName)) continue; - const auto& rBufferInfo = buffers[pSurfaceName]; + const auto& rBufferInfo = g_BufferInfo.at(pSurfaceName); + const auto size = *RenderResolution::FindBufferSize(pSurfaceName, nullptr); auto pSurfaceParams = getSurfaceParams(rBufferInfo.Index, rBufferInfo.Flags); - auto pNewSurface = CreateSurface(rBufferInfo.Width, rBufferInfo.Height, pFormatConfigs[rBufferInfo.Index].SurfaceFormat, 0, reinterpret_cast(pSurfaceParams)); + auto pNewSurface = CreateSurface(size.Width, size.Height, pFormatConfigs[rBufferInfo.Index].SurfaceFormat, 0, reinterpret_cast(pSurfaceParams)); - GuestToHostFunction(sub_82592E98, rSurface.second.get(), pNewSurface, rBufferInfo.Width, rBufferInfo.Height); + GuestToHostFunction(sub_82592E98, rSurface.second.get(), pNewSurface, size.Width, size.Height); } // Update frame buffer textures. @@ -2621,21 +2599,22 @@ void Video::Resize(uint32_t width, uint32_t height) { const auto pTextureName = rTexture.first.c_str(); - if (!buffers.contains(pTextureName)) + if (!g_BufferInfo.contains(pTextureName)) continue; - const auto& rBufferInfo = buffers[pTextureName]; + const auto& rBufferInfo = g_BufferInfo.at(pTextureName); + const auto size = *RenderResolution::FindBufferSize(pTextureName, nullptr); - CreateTextureLocal(rTexture.second.get(), rBufferInfo.Width, rBufferInfo.Height, 1, 1, pFormatConfigs[rBufferInfo.Index].Usage, pFormatConfigs[rBufferInfo.Index].TextureFormat, 0, 3); + CreateTextureLocal(rTexture.second.get(), size.Width, size.Height, 1, 1, pFormatConfigs[rBufferInfo.Index].Usage, pFormatConfigs[rBufferInfo.Index].TextureFormat, 0, 3); const auto surfaceFormatIndex = (rBufferInfo.Flags & 4) == 0 ? rBufferInfo.Index : 3; auto pSurfaceParams = getSurfaceParams(surfaceFormatIndex, rBufferInfo.Flags); - auto pNewSurface = CreateSurface(rBufferInfo.Width, rBufferInfo.Height, pFormatConfigs[surfaceFormatIndex].SurfaceFormat, 0, reinterpret_cast(pSurfaceParams)); + auto pNewSurface = CreateSurface(size.Width, size.Height, pFormatConfigs[surfaceFormatIndex].SurfaceFormat, 0, reinterpret_cast(pSurfaceParams)); - GuestToHostFunction(sub_82592E98, rTexture.second->m_Surfaces[0].get(), pNewSurface, rBufferInfo.Width, rBufferInfo.Height); + GuestToHostFunction(sub_82592E98, rTexture.second->m_Surfaces[0].get(), pNewSurface, size.Width, size.Height); } // Clear post-process buffers. diff --git a/MarathonRecomp/gpu/video.h b/MarathonRecomp/gpu/video.h index 08836adf0..1a3b1e29f 100644 --- a/MarathonRecomp/gpu/video.h +++ b/MarathonRecomp/gpu/video.h @@ -518,6 +518,27 @@ enum GuestTextureAddress D3DTADDRESS_BORDER = 6 }; +struct BufferInfo +{ + uint32_t Index; + uint32_t Flags; +}; + +static const std::map g_BufferInfo +{ + { "framebuffer0", { 0, 4 } }, + { "framebuffer1", { 0, 0 } }, + { "framebuffer_1_4_0", { 3, 2 } }, + { "framebuffer_1_4_1", { 0, 2 } }, + { "framebuffer_1_8_0", { 3, 2 } }, + { "framebuffer_1_8_1", { 0, 2 } }, + { "framebuffer_1_16_0", { 3, 2 } }, + { "framebuffer_1_16_1", { 0, 2 } }, + { "framebuffer_1_32_0", { 3, 2 } }, + { "framebuffer_1_32_1", { 0, 2 } }, + { "depthstencil_1_4", { 6, 0 } }, +}; + extern std::unique_ptr LoadTexture(const uint8_t* data, size_t dataSize, RenderComponentMapping componentMapping = RenderComponentMapping()); extern void VideoConfigValueChangedCallback(const class IConfigDef* config); diff --git a/MarathonRecomp/patches/aspect_ratio_patches.cpp b/MarathonRecomp/patches/aspect_ratio_patches.cpp index c051a94a3..f18ae22cd 100644 --- a/MarathonRecomp/patches/aspect_ratio_patches.cpp +++ b/MarathonRecomp/patches/aspect_ratio_patches.cpp @@ -789,12 +789,65 @@ void Draw(PPCContext& ctx, uint8_t* base, PPCFunc* original, uint32_t stride) auto r3 = ctx.r3; auto r5 = ctx.r5; - auto drawOriginal = [&]() + auto targetScaleX = 1.0f; + auto targetScaleY = 1.0f; + + if (App::s_pApp && App::s_pApp->m_pDevice) + { + auto pDevice = static_cast(App::s_pApp->m_pDevice.get()); + targetScaleX = float(pDevice->viewport.width) / vpWidth; + targetScaleY = float(pDevice->viewport.height) / vpHeight; + + auto fbo = App::s_pApp->m_pDoc->m_pMyGraphicsDevice->m_spFrameBufferObject.get(); + if (fbo && fbo->m_FrameBufferSurfaces[0]) + { + auto fboName = std::string_view(fbo->m_FrameBufferSurfaces[0]->m_MgrResourceName.c_str()); + + // Always unstretch backbuffer CSD draws. + if (fboName == "backbuffer") + { + targetScaleX = 1.0f; + targetScaleY = 1.0f; + } + // coframebufferN is stretched 2x wide on the x-axis. + // Following plain viewport will squash CSD. + else if (fboName.starts_with("coframebuffer")) + { + targetScaleX *= 2; + } + } + } + + auto needsTargetScale = targetScaleX != 1.0f || targetScaleY != 1.0f; + + auto drawOriginal = [&] { ctx.r3 = r3; ctx.r4 = ctx.r1; ctx.r5 = r5; + + if (!needsTargetScale) + { + original(ctx, base); + return; + } + + auto scaledSize = (size + 0xF) & ~0xF; + ctx.r1.u32 -= scaledSize; + ctx.r4 = ctx.r1; + + auto scaled = base + ctx.r1.u32; + memcpy(scaled, stack, size); + + for (size_t i = 0; i < r5.u32; i++) + { + auto vertex = reinterpret_cast(scaled + (i * stride)); + vertex->X = round(vertex->X * targetScaleX); + vertex->Y = round(vertex->Y * targetScaleY); + } + original(ctx, base); + ctx.r1.u32 += scaledSize; }; if (isRepeatLeft || isRepeatRight) diff --git a/MarathonRecomp/patches/video_patches.cpp b/MarathonRecomp/patches/video_patches.cpp index 3b88d805b..50864914a 100644 --- a/MarathonRecomp/patches/video_patches.cpp +++ b/MarathonRecomp/patches/video_patches.cpp @@ -1,8 +1,8 @@ -#include "gpu/video.h" +#include +#include #include #include #include -#include #include const char* g_pBlockName{}; @@ -33,18 +33,12 @@ PPC_FUNC(sub_826078D8) __imp__sub_826078D8(ctx, base); } -float ReflectionScaleFactor(EReflectionResolution ref) { - switch (ref) { - case EReflectionResolution::Eighth: - return 0.5f; - case EReflectionResolution::Quarter: - return 1.0f; - case EReflectionResolution::Half: - return 2.0f; - case EReflectionResolution::Full: - return 4.0f; - default: - return 1.0f; +static void ApplyBufferSize(PPCContext& ctx, const char* pName) +{ + if (auto size = RenderResolution::FindBufferSize(pName, g_pBlockName)) + { + ctx.r5.u32 = size->Width; + ctx.r6.u32 = size->Height; } } @@ -53,27 +47,7 @@ PPC_FUNC_IMPL(__imp__sub_82619D00); PPC_FUNC(sub_82619D00) { auto pName = (stdx::string*)g_memory.Translate(ctx.r4.u32); - - if (*pName == "radermap") - { - ctx.r5.u32 = g_radarMapScale; - ctx.r6.u32 = g_radarMapScale; - } - - if (*pName == "reflection0") - { - ctx.r5.u32 = static_cast(static_cast(ctx.r5.u32) * - ReflectionScaleFactor(Config::ReflectionResolution)); - ctx.r6.u32 = static_cast(static_cast(ctx.r6.u32) * - ReflectionScaleFactor(Config::ReflectionResolution)); - } - - // RenderMefiress - if (*pName == "user0") - { - ctx.r5.u32 = static_cast(Config::ShadowResolution.Value); - ctx.r6.u32 = static_cast(Config::ShadowResolution.Value); - } + ApplyBufferSize(ctx, pName->c_str()); #if _DEBUG auto width = ctx.r5.u32; @@ -94,31 +68,8 @@ PPC_FUNC_IMPL(__imp__sub_82619B88); PPC_FUNC(sub_82619B88) { auto pName = (stdx::string*)g_memory.Translate(ctx.r4.u32); + ApplyBufferSize(ctx, pName->c_str()); - if (g_pBlockName) - { - if (strcmp(g_pBlockName, "radermap0") == 0) - { - ctx.r5.u32 = g_radarMapScale; - ctx.r6.u32 = g_radarMapScale; - } - - // RenderMefiress - if (strcmp(g_pBlockName, "user0") == 0 && *pName == "depthstencil_256") - { - ctx.r5.u32 = static_cast(Config::ShadowResolution.Value); - ctx.r6.u32 = static_cast(Config::ShadowResolution.Value); - } - } - - if (*pName == "depthstencil_1_4") - { - ctx.r5.u32 = static_cast(static_cast(ctx.r5.u32) * - ReflectionScaleFactor(Config::ReflectionResolution)); - ctx.r6.u32 = static_cast(static_cast(ctx.r6.u32) * - ReflectionScaleFactor(Config::ReflectionResolution)); - } - #if _DEBUG auto width = ctx.r5.u32; auto height = ctx.r6.u32; @@ -140,36 +91,12 @@ PPC_FUNC(sub_82619B88) #endif } -float ShadowScaleFactor(EShadowResolution ref) { - switch (ref) { - case EShadowResolution::x512: - return 0.5f; - case EShadowResolution::x1024: - return 1.0f; - case EShadowResolution::x2048: - return 2.0f; - case EShadowResolution::x4096: - return 4.0f; - case EShadowResolution::x8192: - return 8.0f; - default: - return 1.0f; - } -} - // CreateArrayTexture PPC_FUNC_IMPL(__imp__sub_82619FF0); PPC_FUNC(sub_82619FF0) { auto pName = (stdx::string*)g_memory.Translate(ctx.r4.u32); - - if (*pName == "csm") - { - ctx.r5.u32 = static_cast(static_cast(ctx.r5.u32) * - ShadowScaleFactor(Config::ShadowResolution)); - ctx.r6.u32 = static_cast(static_cast(ctx.r6.u32) * - ShadowScaleFactor(Config::ShadowResolution)); - } + ApplyBufferSize(ctx, pName->c_str()); g_surfaceCreationName = pName->c_str(); __imp__sub_82619FF0(ctx, base); @@ -270,13 +197,12 @@ void SetMsaaLevelForBuffers(PPCRegister& r31, PPCRegister& r4) } } -void FixMsaaSurfaceSize(PPCRegister& r1, PPCRegister& r3, PPCRegister& r4) +void FixSurfaceSize(PPCRegister& r3, PPCRegister& r4) { - const auto* backBufferSize = static_cast*>(g_memory.Translate(r1.u32 + 0x60)); + const auto internalSize = RenderResolution::GetInternalSize(); - // Update MSAA surface size to full back-buffer width/height. - r3.u32 = backBufferSize[0]; - r4.u32 = backBufferSize[1]; + r3.u32 = internalSize.Width; + r4.u32 = internalSize.Height; } void FixFrameBufferTileRects(PPCRegister& r26, PPCRegister& r1) diff --git a/MarathonRecomp/ui/options_menu.cpp b/MarathonRecomp/ui/options_menu.cpp index 8257c8f1a..0bf0a285c 100644 --- a/MarathonRecomp/ui/options_menu.cpp +++ b/MarathonRecomp/ui/options_menu.cpp @@ -857,7 +857,7 @@ static void DrawOptions(ImVec2 min, ImVec2 max) DrawOption(rowCount++, &Config::Monitor, canChangeMonitor, monitorReason, 0, 0, displayCount - 1, false); DrawOption(rowCount++, &Config::AspectRatio, true); - DrawOption(rowCount++, &Config::ResolutionScale, true, nullptr, 0.25f, 1.0f, 2.0f); + DrawOption(rowCount++, &Config::ResolutionScale, true, nullptr, 0.25f, 0.5f, 1.0f); DrawOption(rowCount++, &Config::Fullscreen, true); DrawOption(rowCount++, &Config::VSync, true); // TODO DrawOption(rowCount++, &Config::FPS, true, nullptr, FPS_MIN, 120, FPS_MAX); diff --git a/MarathonRecompLib/config/Marathon.toml.in b/MarathonRecompLib/config/Marathon.toml.in index ae1c5f6ad..badb7f350 100644 --- a/MarathonRecompLib/config/Marathon.toml.in +++ b/MarathonRecompLib/config/Marathon.toml.in @@ -644,24 +644,34 @@ address = 0x82607CD4 registers = ["r31", "r4"] [[midasm_hook]] -name = "FixMsaaSurfaceSize" +name = "FixSurfaceSize" +address = 0x825B1DF8 +registers = ["r3", "r4"] + +[[midasm_hook]] +name = "FixSurfaceSize" +address = 0x825B1D94 +registers = ["r3", "r4"] + +[[midasm_hook]] +name = "FixSurfaceSize" address = 0x825B1E70 -registers = ["r1", "r3", "r4"] +registers = ["r3", "r4"] [[midasm_hook]] -name = "FixMsaaSurfaceSize" +name = "FixSurfaceSize" address = 0x825B1EDC -registers = ["r1", "r3", "r4"] +registers = ["r3", "r4"] [[midasm_hook]] -name = "FixMsaaSurfaceSize" +name = "FixSurfaceSize" address = 0x825B1F30 -registers = ["r1", "r3", "r4"] +registers = ["r3", "r4"] [[midasm_hook]] -name = "FixMsaaSurfaceSize" +name = "FixSurfaceSize" address = 0x825B1F94 -registers = ["r1", "r3", "r4"] +registers = ["r3", "r4"] [[midasm_hook]] name = "FixFrameBufferTileRects" From 3fa80bbf24454351e10320bd8af8893f777c3d32 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz Date: Thu, 5 Nov 2026 00:27:44 -0500 Subject: [PATCH 2/2] SSAA --- MarathonRecomp/CMakeLists.txt | 1 + MarathonRecomp/gpu/guest_shader.cpp | 6 ++++ .../gpu/shader/hlsl/downsample_ps.hlsl | 35 +++++++++++++++++++ .../gpu/shader/msl/downsample_ps.metal | 30 ++++++++++++++++ MarathonRecomp/ui/options_menu.cpp | 2 +- 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 MarathonRecomp/gpu/shader/hlsl/downsample_ps.hlsl create mode 100644 MarathonRecomp/gpu/shader/msl/downsample_ps.metal diff --git a/MarathonRecomp/CMakeLists.txt b/MarathonRecomp/CMakeLists.txt index 34b33253c..13204d7ce 100644 --- a/MarathonRecomp/CMakeLists.txt +++ b/MarathonRecomp/CMakeLists.txt @@ -492,6 +492,7 @@ compile_pixel_shader(conditional_survey_ps) compile_vertex_shader(copy_vs) compile_pixel_shader(copy_color_ps) compile_pixel_shader(copy_depth_ps) +compile_pixel_shader(downsample_ps) compile_vertex_shader(enhanced_burnout_blur_vs) compile_pixel_shader(enhanced_burnout_blur_ps) compile_pixel_shader(movie_ps) diff --git a/MarathonRecomp/gpu/guest_shader.cpp b/MarathonRecomp/gpu/guest_shader.cpp index 07a8cefe2..bb65d1ebb 100644 --- a/MarathonRecomp/gpu/guest_shader.cpp +++ b/MarathonRecomp/gpu/guest_shader.cpp @@ -15,6 +15,8 @@ static std::unique_ptr g_enhancedBurnoutBlurPSShader; static std::unique_ptr g_MoviePSShader; +static std::unique_ptr g_downsamplePSShader; + std::unique_ptr g_conditionalSurveyPSShader; void LoadEmbeddedResources() @@ -47,6 +49,8 @@ void CreateReplacementShaders() g_enhancedBurnoutBlurPSShader = CREATE_SHADER(enhanced_burnout_blur_ps); g_MoviePSShader = CREATE_SHADER(movie_ps); + g_downsamplePSShader = CREATE_SHADER(downsample_ps); + g_conditionalSurveyPSShader = std::make_unique(); g_conditionalSurveyPSShader->type = ResourceType::PixelShader; g_conditionalSurveyPSShader->shader = CREATE_SHADER(conditional_survey_ps); @@ -73,6 +77,8 @@ RenderShader* GetReplacementShader(const GuestShader* guestShader) break; case 0x88EB0AA82E4257B0: return g_MoviePSShader.get(); + case 0x62573AB4C9EC44EE: + return g_downsamplePSShader.get(); default: break; } diff --git a/MarathonRecomp/gpu/shader/hlsl/downsample_ps.hlsl b/MarathonRecomp/gpu/shader/hlsl/downsample_ps.hlsl new file mode 100644 index 000000000..561dcc2ca --- /dev/null +++ b/MarathonRecomp/gpu/shader/hlsl/downsample_ps.hlsl @@ -0,0 +1,35 @@ +#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" + +#ifdef __spirv__ + +#define s0_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) + +#else + +cbuffer SharedConstants : register(b2, space4) +{ + uint s0_Texture2DDescriptorIndex : packoffset(c0.x); + DEFINE_SHARED_CONSTANTS(); +}; + +#endif + +float4 shaderMain( + in float4 oPos : SV_Position, + in float4 oTexCoord0 : TEXCOORD0) : SV_Target +{ + Texture2D texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex]; + + float2 pixelCoord = oTexCoord0.xy * float2(getTexture2DDimensions(texture)); + float2 step = float2(ddx(pixelCoord.x), ddy(pixelCoord.y)) / 4.0; + + float4 color = 0.0; + + for (int y = 0; y < 4; y++) + { + for (int x = 0; x < 4; x++) + color += texture.Load(int3(int2(pixelCoord + (float2(x, y) - 1.5) * step), 0)); + } + + return color / 16.0; +} diff --git a/MarathonRecomp/gpu/shader/msl/downsample_ps.metal b/MarathonRecomp/gpu/shader/msl/downsample_ps.metal new file mode 100644 index 000000000..07a2178ab --- /dev/null +++ b/MarathonRecomp/gpu/shader/msl/downsample_ps.metal @@ -0,0 +1,30 @@ +#include "../../../../tools/XenosRecomp/XenosRecomp/shader_common.h" + +#define s0_TextureDescriptorIndex (*(reinterpret_cast(g_PushConstants.SharedConstants + 0))) + +struct Interpolators +{ + float4 oPos [[position]]; + float4 oTexCoord0 [[user(TEXCOORD0)]]; +}; + +[[fragment]] +float4 shaderMain(Interpolators input [[stage_in]], + constant Texture2DDescriptorHeap* g_Texture2DDescriptorHeap [[buffer(0)]], + constant PushConstants& g_PushConstants [[buffer(8)]]) +{ + texture2d texture = g_Texture2DDescriptorHeap[s0_TextureDescriptorIndex].tex; + + float2 pixelCoord = input.oTexCoord0.xy * float2(getTexture2DDimensions(texture)); + float2 step = float2(dfdx(pixelCoord.x), dfdy(pixelCoord.y)) / 4.0; + + float4 color = 0.0; + + for (int y = 0; y < 4; y++) + { + for (int x = 0; x < 4; x++) + color += texture.read(uint2(pixelCoord + (float2(x, y) - 1.5) * step)); + } + + return color / 16.0; +} diff --git a/MarathonRecomp/ui/options_menu.cpp b/MarathonRecomp/ui/options_menu.cpp index 0bf0a285c..8257c8f1a 100644 --- a/MarathonRecomp/ui/options_menu.cpp +++ b/MarathonRecomp/ui/options_menu.cpp @@ -857,7 +857,7 @@ static void DrawOptions(ImVec2 min, ImVec2 max) DrawOption(rowCount++, &Config::Monitor, canChangeMonitor, monitorReason, 0, 0, displayCount - 1, false); DrawOption(rowCount++, &Config::AspectRatio, true); - DrawOption(rowCount++, &Config::ResolutionScale, true, nullptr, 0.25f, 0.5f, 1.0f); + DrawOption(rowCount++, &Config::ResolutionScale, true, nullptr, 0.25f, 1.0f, 2.0f); DrawOption(rowCount++, &Config::Fullscreen, true); DrawOption(rowCount++, &Config::VSync, true); // TODO DrawOption(rowCount++, &Config::FPS, true, nullptr, FPS_MIN, 120, FPS_MAX);