From 087b1eb220e95c01339e4cc3ae6d14a9a447e4c4 Mon Sep 17 00:00:00 2001 From: Skyth <19259897+blueskythlikesclouds@users.noreply.github.com> Date: Sun, 8 Dec 2024 17:01:11 +0300 Subject: [PATCH] Implement anti aliased filtering for point filtered CSD casts. --- UnleashedRecomp/CMakeLists.txt | 1 + UnleashedRecomp/gpu/shader/csd_filter_ps.hlsl | 44 +++++++++++++++ UnleashedRecomp/gpu/video.cpp | 54 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 UnleashedRecomp/gpu/shader/csd_filter_ps.hlsl diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 4b22df6..c205ff2 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -291,6 +291,7 @@ function(compile_pixel_shader FILE_PATH) endfunction() compile_vertex_shader(copy_vs) +compile_pixel_shader(csd_filter_ps) compile_pixel_shader(gaussian_blur_3x3) compile_pixel_shader(gaussian_blur_5x5) compile_pixel_shader(gaussian_blur_7x7) diff --git a/UnleashedRecomp/gpu/shader/csd_filter_ps.hlsl b/UnleashedRecomp/gpu/shader/csd_filter_ps.hlsl new file mode 100644 index 0000000..1c7da5a --- /dev/null +++ b/UnleashedRecomp/gpu/shader/csd_filter_ps.hlsl @@ -0,0 +1,44 @@ +#include "../../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h" + +#ifdef __spirv__ + +#define s0_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) +#define s0_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 192) + +#else + +cbuffer SharedConstants : register(b2, space4) +{ + uint s0_Texture2DDescriptorIndex : packoffset(c0.x); + uint s0_SamplerDescriptorIndex : packoffset(c12.x); + DEFINE_SHARED_CONSTANTS(); +}; + +#endif + +float4 main( + in float4 iPosition : SV_Position, + in float4 iTexCoord0 : TEXCOORD0, + in float4 iTexCoord1 : TEXCOORD1) : SV_Target +{ + Texture2D texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex]; + SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex]; + + uint2 dimensions; + texture.GetDimensions(dimensions.x, dimensions.y); + + // https://www.shadertoy.com/view/csX3RH + float2 uvTexspace = iTexCoord1.xy * dimensions; + float2 seam = floor(uvTexspace + 0.5); + uvTexspace = (uvTexspace - seam) / fwidth(uvTexspace) + seam; + uvTexspace = clamp(uvTexspace, seam - 0.5, seam + 0.5); + float2 texCoord = uvTexspace / dimensions; + + float4 color = texture.Sample(samplerState, texCoord); + color *= iTexCoord0; + + // The game enables alpha test for CSD, but the alpha threshold doesn't seem to be assigned anywhere? Weird. + clip(color.a - g_AlphaThreshold); + + return color; +} diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index 9459fb1..6503734 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -34,6 +34,8 @@ #include "../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h" #include "shader/copy_vs.hlsl.dxil.h" #include "shader/copy_vs.hlsl.spirv.h" +#include "shader/csd_filter_ps.hlsl.dxil.h" +#include "shader/csd_filter_ps.hlsl.spirv.h" #include "shader/gaussian_blur_3x3.hlsl.dxil.h" #include "shader/gaussian_blur_3x3.hlsl.spirv.h" #include "shader/gaussian_blur_5x5.hlsl.dxil.h" @@ -587,6 +589,15 @@ static void LoadEmbeddedResources() g_buttonBcDiff = decompressZstd(g_button_bc_diff, g_button_bc_diff_uncompressed_size); } +enum class CsdFilterState +{ + Unknown, + On, + Off +}; + +static CsdFilterState g_csdFilterState; + enum class RenderCommandType { SetRenderState, @@ -742,6 +753,7 @@ struct RenderCommand uint32_t primitiveCount; UploadAllocation vertexStreamZeroData; uint32_t vertexStreamZeroStride; + CsdFilterState csdFilterState; } drawPrimitiveUP; struct @@ -1044,6 +1056,9 @@ enum static std::unique_ptr g_gaussianBlurShaders[GAUSSIAN_BLUR_COUNT]; +static std::unique_ptr g_csdFilterShader; +static GuestShader* g_csdShader; + #define CREATE_SHADER(NAME) \ g_device->createShader( \ g_vulkan ? g_##NAME##_spirv : g_##NAME##_dxil, \ @@ -1443,6 +1458,9 @@ void Video::CreateHostDevice() g_gaussianBlurShaders[GAUSSIAN_BLUR_7X7]->shader = CREATE_SHADER(gaussian_blur_7x7); g_gaussianBlurShaders[GAUSSIAN_BLUR_9X9]->shader = CREATE_SHADER(gaussian_blur_9x9); + g_csdFilterShader = std::make_unique(ResourceType::PixelShader); + g_csdFilterShader->shader = CREATE_SHADER(csd_filter_ps); + CreateImGuiBackend(); auto gammaCorrectionShader = CREATE_SHADER(gamma_correction_ps); @@ -3415,6 +3433,7 @@ static void DrawPrimitiveUP(GuestDevice* device, uint32_t primitiveType, uint32_ cmd.drawPrimitiveUP.primitiveCount = primitiveCount; cmd.drawPrimitiveUP.vertexStreamZeroData = g_uploadAllocators[g_frame].allocate(reinterpret_cast(vertexStreamZeroData), primitiveCount * vertexStreamZeroStride, 0x4); cmd.drawPrimitiveUP.vertexStreamZeroStride = vertexStreamZeroStride; + cmd.drawPrimitiveUP.csdFilterState = g_csdFilterState; queue.submit(); } @@ -3440,6 +3459,13 @@ static void ProcDrawPrimitiveUP(const RenderCommand& cmd) else if (!g_triangleFanSupported && args.primitiveType == D3DPT_TRIANGLEFAN) indexCount = g_triangleFanIndexData.prepare(args.primitiveCount); + if (args.csdFilterState != CsdFilterState::Unknown && + (g_pipelineState.pixelShader == g_csdShader || g_pipelineState.pixelShader == g_csdFilterShader.get())) + { + SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.pixelShader, + args.csdFilterState == CsdFilterState::On ? g_csdFilterShader.get() : g_csdShader); + } + FlushRenderStateForRenderThread(); if (indexCount != 0) @@ -3788,6 +3814,9 @@ static GuestShader* CreateShader(const be* function, ResourceType reso else shader->AddRef(); + if (hash == 0x31173204A896098A) + g_csdShader = shader; + return shader; } @@ -5481,6 +5510,14 @@ static void ModelConsumerThread() SanitizePipelineState(pipelineState); EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled Pipeline"); } + + // Compile the CSD filter shader that we pass to the game when point filtering is used. + if (pipelineState.pixelShader == g_csdShader) + { + pipelineState.pixelShader = g_csdFilterShader.get(); + SanitizePipelineState(pipelineState); + EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled CSD Filter Pipeline"); + } } g_pendingPipelineStateCache = false; @@ -5727,6 +5764,23 @@ void VideoConfigValueChangedCallback(IConfigDef* config) config == &Config::ShadowResolution; } +// SWA::CCsdTexListMirage::SetFilter +PPC_FUNC_IMPL(__imp__sub_825E4300); +PPC_FUNC(sub_825E4300) +{ + g_csdFilterState = ctx.r5.u32 == 0 ? CsdFilterState::On : CsdFilterState::Off; + ctx.r5.u32 = 1; + __imp__sub_825E4300(ctx, base); +} + +// SWA::CCsdPlatformMirage::EndScene +PPC_FUNC_IMPL(__imp__sub_825E2F78); +PPC_FUNC(sub_825E2F78) +{ + g_csdFilterState = CsdFilterState::Unknown; + __imp__sub_825E2F78(ctx, base); +} + GUEST_FUNCTION_HOOK(sub_82BD99B0, CreateDevice); GUEST_FUNCTION_HOOK(sub_82BE6230, DestructResource);