Implement anti aliased filtering for point filtered CSD casts.

This commit is contained in:
Skyth 2024-12-08 17:01:11 +03:00
parent f327eb8357
commit 087b1eb220
3 changed files with 99 additions and 0 deletions

View file

@ -291,6 +291,7 @@ function(compile_pixel_shader FILE_PATH)
endfunction() endfunction()
compile_vertex_shader(copy_vs) compile_vertex_shader(copy_vs)
compile_pixel_shader(csd_filter_ps)
compile_pixel_shader(gaussian_blur_3x3) compile_pixel_shader(gaussian_blur_3x3)
compile_pixel_shader(gaussian_blur_5x5) compile_pixel_shader(gaussian_blur_5x5)
compile_pixel_shader(gaussian_blur_7x7) compile_pixel_shader(gaussian_blur_7x7)

View file

@ -0,0 +1,44 @@
#include "../../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h"
#ifdef __spirv__
#define s0_Texture2DDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 0)
#define s0_SamplerDescriptorIndex vk::RawBufferLoad<uint>(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<float4> 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;
}

View file

@ -34,6 +34,8 @@
#include "../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h" #include "../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h"
#include "shader/copy_vs.hlsl.dxil.h" #include "shader/copy_vs.hlsl.dxil.h"
#include "shader/copy_vs.hlsl.spirv.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.dxil.h"
#include "shader/gaussian_blur_3x3.hlsl.spirv.h" #include "shader/gaussian_blur_3x3.hlsl.spirv.h"
#include "shader/gaussian_blur_5x5.hlsl.dxil.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); 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 enum class RenderCommandType
{ {
SetRenderState, SetRenderState,
@ -742,6 +753,7 @@ struct RenderCommand
uint32_t primitiveCount; uint32_t primitiveCount;
UploadAllocation vertexStreamZeroData; UploadAllocation vertexStreamZeroData;
uint32_t vertexStreamZeroStride; uint32_t vertexStreamZeroStride;
CsdFilterState csdFilterState;
} drawPrimitiveUP; } drawPrimitiveUP;
struct struct
@ -1044,6 +1056,9 @@ enum
static std::unique_ptr<GuestShader> g_gaussianBlurShaders[GAUSSIAN_BLUR_COUNT]; static std::unique_ptr<GuestShader> g_gaussianBlurShaders[GAUSSIAN_BLUR_COUNT];
static std::unique_ptr<GuestShader> g_csdFilterShader;
static GuestShader* g_csdShader;
#define CREATE_SHADER(NAME) \ #define CREATE_SHADER(NAME) \
g_device->createShader( \ g_device->createShader( \
g_vulkan ? g_##NAME##_spirv : g_##NAME##_dxil, \ 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_7X7]->shader = CREATE_SHADER(gaussian_blur_7x7);
g_gaussianBlurShaders[GAUSSIAN_BLUR_9X9]->shader = CREATE_SHADER(gaussian_blur_9x9); g_gaussianBlurShaders[GAUSSIAN_BLUR_9X9]->shader = CREATE_SHADER(gaussian_blur_9x9);
g_csdFilterShader = std::make_unique<GuestShader>(ResourceType::PixelShader);
g_csdFilterShader->shader = CREATE_SHADER(csd_filter_ps);
CreateImGuiBackend(); CreateImGuiBackend();
auto gammaCorrectionShader = CREATE_SHADER(gamma_correction_ps); 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.primitiveCount = primitiveCount;
cmd.drawPrimitiveUP.vertexStreamZeroData = g_uploadAllocators[g_frame].allocate<true>(reinterpret_cast<uint32_t*>(vertexStreamZeroData), primitiveCount * vertexStreamZeroStride, 0x4); cmd.drawPrimitiveUP.vertexStreamZeroData = g_uploadAllocators[g_frame].allocate<true>(reinterpret_cast<uint32_t*>(vertexStreamZeroData), primitiveCount * vertexStreamZeroStride, 0x4);
cmd.drawPrimitiveUP.vertexStreamZeroStride = vertexStreamZeroStride; cmd.drawPrimitiveUP.vertexStreamZeroStride = vertexStreamZeroStride;
cmd.drawPrimitiveUP.csdFilterState = g_csdFilterState;
queue.submit(); queue.submit();
} }
@ -3440,6 +3459,13 @@ static void ProcDrawPrimitiveUP(const RenderCommand& cmd)
else if (!g_triangleFanSupported && args.primitiveType == D3DPT_TRIANGLEFAN) else if (!g_triangleFanSupported && args.primitiveType == D3DPT_TRIANGLEFAN)
indexCount = g_triangleFanIndexData.prepare(args.primitiveCount); 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(); FlushRenderStateForRenderThread();
if (indexCount != 0) if (indexCount != 0)
@ -3788,6 +3814,9 @@ static GuestShader* CreateShader(const be<uint32_t>* function, ResourceType reso
else else
shader->AddRef(); shader->AddRef();
if (hash == 0x31173204A896098A)
g_csdShader = shader;
return shader; return shader;
} }
@ -5481,6 +5510,14 @@ static void ModelConsumerThread()
SanitizePipelineState(pipelineState); SanitizePipelineState(pipelineState);
EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled Pipeline"); 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; g_pendingPipelineStateCache = false;
@ -5727,6 +5764,23 @@ void VideoConfigValueChangedCallback(IConfigDef* config)
config == &Config::ShadowResolution; 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_82BD99B0, CreateDevice);
GUEST_FUNCTION_HOOK(sub_82BE6230, DestructResource); GUEST_FUNCTION_HOOK(sub_82BE6230, DestructResource);