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()
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)

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 "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<GuestShader> g_gaussianBlurShaders[GAUSSIAN_BLUR_COUNT];
static std::unique_ptr<GuestShader> 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<GuestShader>(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<true>(reinterpret_cast<uint32_t*>(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<uint32_t>* 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);