diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 00000000..11149d55 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,50 @@ +{ + "version": 8, + "configurePresets": [ + { + "name": "windows-base", + "hidden": true, + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "installDir": "${sourceDir}/out/install/${presetName}", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang-cl.exe", + "CMAKE_CXX_COMPILER": "clang-cl.exe", + "CMAKE_TOOLCHAIN_FILE": { + "value": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "type": "FILEPATH" + } + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "toolset": "ClangCL" + }, + { + "name": "x64-Clang-Debug", + "displayName": "Debug", + "inherits": "windows-base", + "architecture": { + "value": "x64", + "strategy": "external" + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "VCPKG_TARGET_TRIPLET": { + "value": "x64-windows-static", + "type": "STRING" + } + } + }, + { + "name": "x64-Clang-Release", + "displayName": "Release", + "inherits": "x64-Clang-Debug", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + } + ] +} diff --git a/CMakeSettings.json b/CMakeSettings.json deleted file mode 100644 index 0827846c..00000000 --- a/CMakeSettings.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "configurations": [ - { - "name": "x64-Clang-Debug", - "generator": "Ninja", - "configurationType": "Debug", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "", - "inheritEnvironments": [ "clang_cl_x64_x64" ], - "variables": [ - { - "name": "VCPKG_TARGET_TRIPLET", - "value": "x64-windows-static", - "type": "STRING" - } - ] - }, - { - "name": "x64-Clang-Release", - "generator": "Ninja", - "configurationType": "RelWithDebInfo", - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "", - "buildCommandArgs": "", - "ctestCommandArgs": "", - "inheritEnvironments": [ "clang_cl_x64_x64" ], - "variables": [ - { - "name": "VCPKG_TARGET_TRIPLET", - "value": "x64-windows-static", - "type": "STRING" - } - ] - } - ] -} \ No newline at end of file diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index ff8536d8..6fb3e98b 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -79,6 +79,7 @@ set(SWA_UI_CXX_SOURCES ) set(SWA_CXX_SOURCES + "app.cpp" "main.cpp" "misc_impl.cpp" "stdafx.cpp" @@ -188,6 +189,7 @@ function(compile_pixel_shader FILE_PATH) endfunction() compile_vertex_shader(copy_vs) +compile_pixel_shader(gamma_correction_ps) compile_pixel_shader(imgui_ps) compile_vertex_shader(imgui_vs) compile_pixel_shader(movie_ps) diff --git a/UnleashedRecomp/app.cpp b/UnleashedRecomp/app.cpp new file mode 100644 index 00000000..722429f9 --- /dev/null +++ b/UnleashedRecomp/app.cpp @@ -0,0 +1,19 @@ +#include +#include +#include + +double g_deltaTime; + +// CApplication::Update +PPC_FUNC_IMPL(__imp__sub_822C1130); +PPC_FUNC(sub_822C1130) +{ + g_deltaTime = ctx.f1.f64; + + SDL_PumpEvents(); + SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); + + Window::Update(); + + __imp__sub_822C1130(ctx, base); +} diff --git a/UnleashedRecomp/app.h b/UnleashedRecomp/app.h new file mode 100644 index 00000000..4e1d379e --- /dev/null +++ b/UnleashedRecomp/app.h @@ -0,0 +1,3 @@ +#pragma once + +extern double g_deltaTime; diff --git a/UnleashedRecomp/gpu/shader/gamma_correction_ps.hlsl b/UnleashedRecomp/gpu/shader/gamma_correction_ps.hlsl new file mode 100644 index 00000000..dfed91b0 --- /dev/null +++ b/UnleashedRecomp/gpu/shader/gamma_correction_ps.hlsl @@ -0,0 +1,24 @@ +#include "../../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.hlsli" + +#ifdef __spirv__ + +#define g_Gamma vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) +#define g_TextureDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 12) + +#else + +cbuffer SharedConstants : register(b2, space4) +{ + float3 g_Gamma : packoffset(c0.x); + uint g_TextureDescriptorIndex : packoffset(c0.w); +}; + +#endif + +float4 main(in float4 position : SV_Position) : SV_Target +{ + Texture2D texture = g_Texture2DDescriptorHeap[g_TextureDescriptorIndex]; + float4 color = texture.Load(int3(position.xy, 0)); + color.rgb = pow(color.rgb, g_Gamma); + return color; +} diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index f870a5fd..180c714e 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -15,6 +15,8 @@ #include "shader/copy_vs.hlsl.dxil.h" #include "shader/copy_vs.hlsl.spirv.h" +#include "shader/gamma_correction_ps.hlsl.dxil.h" +#include "shader/gamma_correction_ps.hlsl.spirv.h" #include "shader/imgui_ps.hlsl.dxil.h" #include "shader/imgui_ps.hlsl.spirv.h" #include "shader/imgui_vs.hlsl.dxil.h" @@ -171,11 +173,20 @@ static std::unique_ptr g_swapChain; static bool g_swapChainValid; static bool g_needsResize; +static constexpr RenderFormat BACKBUFFER_FORMAT = RenderFormat::B8G8R8A8_UNORM; + static std::unique_ptr g_acquireSemaphores[NUM_FRAMES]; static std::unique_ptr g_renderSemaphores[NUM_FRAMES]; static uint32_t g_backBufferIndex; static GuestSurface* g_backBuffer; +static std::unique_ptr g_intermediaryBackBufferTexture; +static uint32_t g_intermediaryBackBufferTextureWidth; +static uint32_t g_intermediaryBackBufferTextureHeight; +static uint32_t g_intermediaryBackBufferTextureDescriptorIndex; + +static std::unique_ptr g_gammaCorrectionPipeline; + struct std::unique_ptr g_textureDescriptorSet; struct std::unique_ptr g_samplerDescriptorSet; @@ -1080,7 +1091,7 @@ static void CreateImGuiBackend() pipelineDesc.pipelineLayout = g_imPipelineLayout.get(); pipelineDesc.vertexShader = vertexShader.get(); pipelineDesc.pixelShader = pixelShader.get(); - pipelineDesc.renderTargetFormat[0] = RenderFormat::B8G8R8A8_UNORM; + pipelineDesc.renderTargetFormat[0] = BACKBUFFER_FORMAT; pipelineDesc.renderTargetBlend[0] = RenderBlendDesc::AlphaBlend(); pipelineDesc.renderTargetCount = 1; pipelineDesc.inputElements = inputElements; @@ -1121,7 +1132,7 @@ static void CreateHostDevice() g_copyCommandList = g_device->createCommandList(RenderCommandListType::COPY); g_copyCommandFence = g_device->createCommandFence(); - g_swapChain = g_queue->createSwapChain(Window::s_handle, Config::TripleBuffering ? 3 : 2, RenderFormat::B8G8R8A8_UNORM); + g_swapChain = g_queue->createSwapChain(Window::s_handle, Config::TripleBuffering ? 3 : 2, BACKBUFFER_FORMAT); g_swapChain->setVsyncEnabled(Config::VSync); g_swapChainValid = !g_swapChain->needsResize(); @@ -1253,6 +1264,17 @@ static void CreateHostDevice() } CreateImGuiBackend(); + + auto gammaCorrectionShader = CREATE_SHADER(gamma_correction_ps); + + RenderGraphicsPipelineDesc desc; + desc.pipelineLayout = g_pipelineLayout.get(); + desc.vertexShader = copyShader.get(); + desc.pixelShader = gammaCorrectionShader.get(); + desc.renderTargetFormat[0] = BACKBUFFER_FORMAT; + desc.renderTargetBlend[0] = RenderBlendDesc::Copy(); + desc.renderTargetCount = 1; + g_gammaCorrectionPipeline = g_device->createGraphicsPipeline(desc); } static void WaitForGPU() @@ -1290,7 +1312,7 @@ static void BeginCommandList() g_depthStencil = nullptr; g_framebuffer = nullptr; - g_pipelineState.renderTargetFormat = g_backBuffer->format; + g_pipelineState.renderTargetFormat = BACKBUFFER_FORMAT; g_pipelineState.depthStencilFormat = RenderFormat::UNKNOWN; g_swapChainValid &= !g_swapChain->needsResize(); @@ -1307,9 +1329,40 @@ static void BeginCommandList() g_swapChainValid = g_swapChain->acquireTexture(g_acquireSemaphores[g_frame].get(), &g_backBufferIndex); if (g_swapChainValid) - g_backBuffer->texture = g_swapChain->getTexture(g_backBufferIndex); + { + bool applyingGammaCorrection = Config::Xbox360ColourCorrection || abs(Config::Brightness - 0.5f) > 0.001f; + + if (applyingGammaCorrection) + { + uint32_t width = g_swapChain->getWidth(); + uint32_t height = g_swapChain->getHeight(); + + if (g_intermediaryBackBufferTextureWidth != width || + g_intermediaryBackBufferTextureHeight != height) + { + if (g_intermediaryBackBufferTextureDescriptorIndex == NULL) + g_intermediaryBackBufferTextureDescriptorIndex = g_textureDescriptorAllocator.allocate(); + + WaitForGPU(); // Fine to wait for GPU, this'll only happen during resize. + + g_intermediaryBackBufferTexture = g_device->createTexture(RenderTextureDesc::Texture2D(width, height, 1, BACKBUFFER_FORMAT, RenderTextureFlag::RENDER_TARGET)); + g_textureDescriptorSet->setTexture(g_intermediaryBackBufferTextureDescriptorIndex, g_intermediaryBackBufferTexture.get(), RenderTextureLayout::SHADER_READ); + + g_intermediaryBackBufferTextureWidth = width; + g_intermediaryBackBufferTextureHeight = height; + } + + g_backBuffer->texture = g_intermediaryBackBufferTexture.get(); + } + else + { + g_backBuffer->texture = g_swapChain->getTexture(g_backBufferIndex); + } + } else + { g_backBuffer->texture = g_backBuffer->textureHolder.get(); + } g_backBuffer->layout = RenderTextureLayout::UNKNOWN; @@ -1339,8 +1392,8 @@ static uint32_t CreateDevice(uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, g_backBuffer = g_userHeap.AllocPhysical(ResourceType::RenderTarget); g_backBuffer->width = 1280; g_backBuffer->height = 720; - g_backBuffer->format = RenderFormat::B8G8R8A8_UNORM; - g_backBuffer->textureHolder = g_device->createTexture(RenderTextureDesc::Texture2D(16, 16, 1, g_backBuffer->format, RenderTextureFlag::RENDER_TARGET)); + g_backBuffer->format = BACKBUFFER_FORMAT; + g_backBuffer->textureHolder = g_device->createTexture(RenderTextureDesc::Texture2D(1, 1, 1, BACKBUFFER_FORMAT, RenderTextureFlag::RENDER_TARGET)); BeginCommandList(); @@ -1639,12 +1692,84 @@ static void Present() g_renderQueue.enqueue(cmd); } +static void SetRootDescriptor(const UploadAllocation& allocation, size_t index) +{ + auto& commandList = g_commandLists[g_frame]; + + if (g_vulkan) + commandList->setGraphicsPushConstants(0, &allocation.deviceAddress, 8 * index, 8); + else + commandList->setGraphicsRootDescriptor(allocation.buffer->at(allocation.offset), index); +} + static void ProcPresent(const RenderCommand& cmd) { if (g_swapChainValid) - AddBarrier(g_backBuffer, RenderTextureLayout::PRESENT); + { + auto swapChainTexture = g_swapChain->getTexture(g_backBufferIndex); + if (g_backBuffer->texture == g_intermediaryBackBufferTexture.get()) + { + struct + { + float gammaR; + float gammaG; + float gammaB; + uint32_t textureDescriptorIndex; + } constants; - FlushBarriers(); + if (Config::Xbox360ColourCorrection) + { + constants.gammaR = 1.2f; + constants.gammaG = 1.17f; + constants.gammaB = 0.98f; + } + else + { + constants.gammaR = 1.0f; + constants.gammaG = 1.0f; + constants.gammaB = 1.0f; + } + + float offset = (Config::Brightness - 0.5f) * 1.2f; + + constants.gammaR = 1.0f / std::clamp(constants.gammaR + offset, 0.1f, 4.0f); + constants.gammaG = 1.0f / std::clamp(constants.gammaG + offset, 0.1f, 4.0f); + constants.gammaB = 1.0f / std::clamp(constants.gammaB + offset, 0.1f, 4.0f); + constants.textureDescriptorIndex = g_intermediaryBackBufferTextureDescriptorIndex; + + auto& framebuffer = g_backBuffer->framebuffers[swapChainTexture]; + if (!framebuffer) + { + RenderFramebufferDesc desc; + desc.colorAttachments = const_cast(&swapChainTexture); + desc.colorAttachmentsCount = 1; + framebuffer = g_device->createFramebuffer(desc); + } + + RenderTextureBarrier srcBarriers[] = + { + RenderTextureBarrier(g_intermediaryBackBufferTexture.get(), RenderTextureLayout::SHADER_READ), + RenderTextureBarrier(swapChainTexture, RenderTextureLayout::COLOR_WRITE) + }; + + auto& commandList = g_commandLists[g_frame]; + commandList->barriers(RenderBarrierStage::GRAPHICS, srcBarriers, std::size(srcBarriers)); + commandList->setGraphicsPipelineLayout(g_pipelineLayout.get()); + commandList->setPipeline(g_gammaCorrectionPipeline.get()); + commandList->setGraphicsDescriptorSet(g_textureDescriptorSet.get(), 0); + SetRootDescriptor(g_uploadAllocators[g_frame].allocate(&constants, sizeof(constants), 0x100), 2); + commandList->setFramebuffer(framebuffer.get()); + commandList->setViewports(RenderViewport(0.0f, 0.0f, g_intermediaryBackBufferTextureWidth, g_intermediaryBackBufferTextureHeight)); + commandList->setScissors(RenderRect(0, 0, g_intermediaryBackBufferTextureWidth, g_intermediaryBackBufferTextureHeight)); + commandList->drawInstanced(6, 1, 0, 0); + commandList->barriers(RenderBarrierStage::GRAPHICS, RenderTextureBarrier(swapChainTexture, RenderTextureLayout::PRESENT)); + } + else + { + AddBarrier(g_backBuffer, RenderTextureLayout::PRESENT); + FlushBarriers(); + } + } auto& commandList = g_commandLists[g_frame]; commandList->end(); @@ -2537,16 +2662,6 @@ static void ProcSetSamplerState(const RenderCommand& cmd) } } -static void SetRootDescriptor(const UploadAllocation& allocation, size_t index) -{ - auto& commandList = g_commandLists[g_frame]; - - if (g_vulkan) - commandList->setGraphicsPushConstants(0, &allocation.deviceAddress, 8 * index, 8); - else - commandList->setGraphicsRootDescriptor(allocation.buffer->at(allocation.offset), index); -} - static void ProcSetVertexShaderConstants(const RenderCommand& cmd) { SetRootDescriptor(cmd.setVertexShaderConstants.allocation, 0); diff --git a/UnleashedRecomp/patches/fps_patches.cpp b/UnleashedRecomp/patches/fps_patches.cpp index d55c5e90..1ad63b10 100644 --- a/UnleashedRecomp/patches/fps_patches.cpp +++ b/UnleashedRecomp/patches/fps_patches.cpp @@ -2,6 +2,7 @@ #include #include #include +#include float m_lastLoadingFrameDelta = 0.0f; std::chrono::steady_clock::time_point m_lastLoadingFrameTime; @@ -36,14 +37,16 @@ static double ComputeLerpFactor(double t, double deltaTime) return 1.0 - pow(1.0 - t, (30.0 + bias) / (fps + bias)); } -void CameraLerpFixMidAsmHook(PPCRegister& t, PPCRegister& deltaTime) +// It's important to use global delta time here instead of function provided +// delta time, as it might be time scaled and not match with 30 FPS behavior. +void CameraLerpFixMidAsmHook(PPCRegister& t) { - t.f64 = ComputeLerpFactor(t.f64, deltaTime.f64); + t.f64 = ComputeLerpFactor(t.f64, g_deltaTime); } -void CameraTargetSideOffsetLerpFixMidAsmHook(PPCVRegister& v13, PPCVRegister& v62, PPCRegister& deltaTime) +void CameraTargetSideOffsetLerpFixMidAsmHook(PPCVRegister& v13, PPCVRegister& v62) { - float factor = float(ComputeLerpFactor(double(v13.f32[0] * v62.f32[0]), deltaTime.f64)); + float factor = float(ComputeLerpFactor(double(v13.f32[0] * v62.f32[0]), g_deltaTime)); for (size_t i = 0; i < 4; i++) { diff --git a/UnleashedRecomp/patches/video_patches.cpp b/UnleashedRecomp/patches/video_patches.cpp index 3bf6ebe5..d3e1f0d2 100644 --- a/UnleashedRecomp/patches/video_patches.cpp +++ b/UnleashedRecomp/patches/video_patches.cpp @@ -41,6 +41,9 @@ void CameraBoostAspectRatioMidAsmHook(PPCRegister& r31, PPCRegister& f0) void CSDAspectRatioMidAsmHook(PPCRegister& f1, PPCRegister& f2) { + if (Config::UIScaleMode == EUIScaleMode::Stretch) + return; + auto newAspectRatio = (float)Window::s_width / (float)Window::s_height; if (newAspectRatio > m_baseAspectRatio) diff --git a/UnleashedRecomp/ui/window.cpp b/UnleashedRecomp/ui/window.cpp index 7f587044..2df75973 100644 --- a/UnleashedRecomp/ui/window.cpp +++ b/UnleashedRecomp/ui/window.cpp @@ -1,7 +1,6 @@ #include "window.h" #include "sdl_listener.h" #include -#include #include bool m_isFullscreenKeyReleased = true; @@ -175,15 +174,3 @@ void Window::Update() Config::WindowHeight = Window::s_height; } } - -// CApplication::Update -PPC_FUNC_IMPL(__imp__sub_822C1130); -PPC_FUNC(sub_822C1130) -{ - SDL_PumpEvents(); - SDL_FlushEvents(SDL_FIRSTEVENT, SDL_LASTEVENT); - - Window::Update(); - - __imp__sub_822C1130(ctx, base); -} diff --git a/UnleashedRecomp/ui/window.h b/UnleashedRecomp/ui/window.h index cab4fbf5..b560981e 100644 --- a/UnleashedRecomp/ui/window.h +++ b/UnleashedRecomp/ui/window.h @@ -63,7 +63,7 @@ public: if (!title) { title = Config::Language == ELanguage::Japanese - ? "Sonic World Adventure" + ? "SONIC WORLD ADVENTURE" : "SONIC UNLEASHED"; } diff --git a/UnleashedRecompLib/config/SWA.toml b/UnleashedRecompLib/config/SWA.toml index 8d2fe1a3..e154e37b 100644 --- a/UnleashedRecompLib/config/SWA.toml +++ b/UnleashedRecompLib/config/SWA.toml @@ -213,52 +213,52 @@ jump_address = 0x8247DD3C [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247DD48 # Slope -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247DDFC # Dash path binormal -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247E280 # Target front offset -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247E300 # Target -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247E3C8 # Target -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247E4A0 # Target up positive -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247E4E8 # Target up negative -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247E9FC # Position -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247EA84 # Position up negative -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247EA50 # Position up positive -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraDeltaTimeFixMidAsmHook" @@ -269,7 +269,7 @@ jump_address = 0x8247ED34 [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247ED40 # Distance offset 1 -registers = ["f0", "f21"] +registers = ["f0"] [[midasm_hook]] name = "CameraDeltaTimeFixMidAsmHook" @@ -280,12 +280,12 @@ jump_address = 0x8247ED58 [[midasm_hook]] name = "CameraLerpFixMidAsmHook" address = 0x8247ED64 # Distance offset 2 -registers = ["f13", "f21"] +registers = ["f13"] [[midasm_hook]] name = "CameraTargetSideOffsetLerpFixMidAsmHook" address = 0x8247F12C # Target side offset -registers = ["v13", "v62", "f21"] +registers = ["v13", "v62"] # 2D camera HFR fixes [[midasm_hook]] diff --git a/thirdparty/PowerRecomp b/thirdparty/PowerRecomp index a7c970d3..d8676283 160000 --- a/thirdparty/PowerRecomp +++ b/thirdparty/PowerRecomp @@ -1 +1 @@ -Subproject commit a7c970d32497b7f1624a2871fdd67435d724b1ef +Subproject commit d8676283fd1e8990b415cc0e4810c6db895b9fba