Add enhanced motion blur.

This commit is contained in:
Skyth 2024-12-08 19:34:02 +03:00
parent 087b1eb220
commit facc910200
8 changed files with 190 additions and 63 deletions

View file

@ -292,6 +292,7 @@ endfunction()
compile_vertex_shader(copy_vs)
compile_pixel_shader(csd_filter_ps)
compile_pixel_shader(enhanced_motion_blur_ps)
compile_pixel_shader(gaussian_blur_3x3)
compile_pixel_shader(gaussian_blur_5x5)
compile_pixel_shader(gaussian_blur_7x7)

View file

@ -0,0 +1,75 @@
#include "../../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h"
#ifdef __spirv__
#define g_BlurRate vk::RawBufferLoad<float4>(g_PushConstants.PixelShaderConstants + 2400, 0x10)
#define g_ViewportSize vk::RawBufferLoad<float4>(g_PushConstants.PixelShaderConstants + 384, 0x10)
#define sampColor_Texture2DDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 0)
#define sampColor_SamplerDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 192)
#define sampVelocityMap_Texture2DDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 4)
#define sampVelocityMap_SamplerDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 196)
#define sampZBuffer_Texture2DDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 8)
#define sampZBuffer_SamplerDescriptorIndex vk::RawBufferLoad<uint>(g_PushConstants.SharedConstants + 200)
#else
cbuffer PixelShaderConstants : register(b1, space4)
{
float4 g_BlurRate : packoffset(c150);
float4 g_ViewportSize : packoffset(c24);
};
cbuffer SharedConstants : register(b2, space4)
{
uint sampColor_Texture2DDescriptorIndex : packoffset(c0.x);
uint sampColor_SamplerDescriptorIndex : packoffset(c12.x);
uint sampVelocityMap_Texture2DDescriptorIndex : packoffset(c0.y);
uint sampVelocityMap_SamplerDescriptorIndex : packoffset(c12.y);
uint sampZBuffer_Texture2DDescriptorIndex : packoffset(c0.z);
uint sampZBuffer_SamplerDescriptorIndex : packoffset(c12.z);
DEFINE_SHARED_CONSTANTS();
};
#endif
float4 main(in float4 position : SV_Position, in float2 texCoord : TEXCOORD0) : SV_Target
{
Texture2D<float4> sampColor = g_Texture2DDescriptorHeap[sampColor_Texture2DDescriptorIndex];
Texture2D<float4> sampVelocityMap = g_Texture2DDescriptorHeap[sampVelocityMap_Texture2DDescriptorIndex];
Texture2D<float4> sampZBuffer = g_Texture2DDescriptorHeap[sampZBuffer_Texture2DDescriptorIndex];
SamplerState sampColor_s = g_SamplerDescriptorHeap[sampColor_SamplerDescriptorIndex];
SamplerState sampVelocityMap_s = g_SamplerDescriptorHeap[sampVelocityMap_SamplerDescriptorIndex];
SamplerState sampZBuffer_s = g_SamplerDescriptorHeap[sampZBuffer_SamplerDescriptorIndex];
float depth = sampZBuffer.SampleLevel(sampZBuffer_s, texCoord, 0).x;
float4 velocityMap = sampVelocityMap.SampleLevel(sampVelocityMap_s, texCoord, 0);
float2 velocity = (velocityMap.xz + velocityMap.yw / 255.0) * 2.0 - 1.0;
int sampleCount = min(64, round(length(velocity * g_ViewportSize.xy)));
float2 sampleOffset = velocity / (float) sampleCount;
float3 color = sampColor.SampleLevel(sampColor_s, texCoord, 0).rgb;
int count = 1;
for (int i = 1; i <= sampleCount; i++)
{
float2 sampleCoord = texCoord + sampleOffset * i;
float3 sampleColor = sampColor.SampleLevel(sampColor_s, sampleCoord, 0).rgb;
float sampleDepth = sampZBuffer.SampleLevel(sampZBuffer_s, sampleCoord, 0).x;
if (sampleDepth - depth < 0.01)
{
color += sampleColor;
count += 1;
}
}
return float4(color / count, g_BlurRate.x * saturate(dot(abs(velocity), g_ViewportSize.xy) / 8.0) * saturate(count - 1));
}

View file

@ -63,7 +63,7 @@ float4 main(in float4 iPosition : SV_Position, in float4 iTexCoord0 : TEXCOORD0)
float2 offset = lerp(offsets[int(scaled)], offsets[min(int(scaled) + 1, 2)], frac(scaled));
float offsetScale = 1.0 / 0.75;
float weight = ComputeWeight(lerp(-offsetScale, offsetScale, step));
color += texture.Sample(samplerState, iTexCoord0.xy + offset) * weight;
color += texture.SampleLevel(samplerState, iTexCoord0.xy + offset, 0) * weight;
weightSum += weight;
}

View file

@ -36,6 +36,8 @@
#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/enhanced_motion_blur_ps.hlsl.dxil.h"
#include "shader/enhanced_motion_blur_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"
@ -1059,6 +1061,8 @@ static std::unique_ptr<GuestShader> g_gaussianBlurShaders[GAUSSIAN_BLUR_COUNT];
static std::unique_ptr<GuestShader> g_csdFilterShader;
static GuestShader* g_csdShader;
static std::unique_ptr<GuestShader> g_enhancedMotionBlurShader;
#define CREATE_SHADER(NAME) \
g_device->createShader( \
g_vulkan ? g_##NAME##_spirv : g_##NAME##_dxil, \
@ -1461,6 +1465,9 @@ void Video::CreateHostDevice()
g_csdFilterShader = std::make_unique<GuestShader>(ResourceType::PixelShader);
g_csdFilterShader->shader = CREATE_SHADER(csd_filter_ps);
g_enhancedMotionBlurShader = std::make_unique<GuestShader>(ResourceType::PixelShader);
g_enhancedMotionBlurShader->shader = CREATE_SHADER(enhanced_motion_blur_ps);
CreateImGuiBackend();
auto gammaCorrectionShader = CREATE_SHADER(gamma_correction_ps);
@ -3902,48 +3909,54 @@ static void ProcSetPixelShader(const RenderCommand& cmd)
{
GuestShader* shader = cmd.setPixelShader.shader;
if (shader != nullptr &&
shader->shaderCacheEntry != nullptr &&
shader->shaderCacheEntry->hash == 0x4294510C775F4EE8)
shader->shaderCacheEntry != nullptr)
{
size_t shaderIndex = GAUSSIAN_BLUR_3X3;
switch (Config::DepthOfFieldQuality)
if (shader->shaderCacheEntry->hash == 0x4294510C775F4EE8)
{
case EDepthOfFieldQuality::Low:
shaderIndex = GAUSSIAN_BLUR_3X3;
break;
size_t shaderIndex = GAUSSIAN_BLUR_3X3;
case EDepthOfFieldQuality::Medium:
shaderIndex = GAUSSIAN_BLUR_5X5;
break;
case EDepthOfFieldQuality::High:
shaderIndex = GAUSSIAN_BLUR_7X7;
break;
case EDepthOfFieldQuality::Ultra:
shaderIndex = GAUSSIAN_BLUR_9X9;
break;
default:
{
size_t height = round(g_swapChain->getHeight() * Config::ResolutionScale);
// Use the middle point between resolutions to have the transition less noticable.
if (height >= ((2160 + 1440) / 2))
shaderIndex = GAUSSIAN_BLUR_9X9;
else if (height >= ((1440 + 1080) / 2))
shaderIndex = GAUSSIAN_BLUR_7X7;
else if (height >= ((1080 + 720) / 2))
shaderIndex = GAUSSIAN_BLUR_5X5;
else
switch (Config::DepthOfFieldQuality)
{
case EDepthOfFieldQuality::Low:
shaderIndex = GAUSSIAN_BLUR_3X3;
break;
break;
}
}
case EDepthOfFieldQuality::Medium:
shaderIndex = GAUSSIAN_BLUR_5X5;
break;
shader = g_gaussianBlurShaders[shaderIndex].get();
case EDepthOfFieldQuality::High:
shaderIndex = GAUSSIAN_BLUR_7X7;
break;
case EDepthOfFieldQuality::Ultra:
shaderIndex = GAUSSIAN_BLUR_9X9;
break;
default:
{
size_t height = round(g_swapChain->getHeight() * Config::ResolutionScale);
// Use the middle point between resolutions to have the transition less noticable.
if (height >= ((2160 + 1440) / 2))
shaderIndex = GAUSSIAN_BLUR_9X9;
else if (height >= ((1440 + 1080) / 2))
shaderIndex = GAUSSIAN_BLUR_7X7;
else if (height >= ((1080 + 720) / 2))
shaderIndex = GAUSSIAN_BLUR_5X5;
else
shaderIndex = GAUSSIAN_BLUR_3X3;
break;
}
}
shader = g_gaussianBlurShaders[shaderIndex].get();
}
else if (shader->shaderCacheEntry->hash == 0x6B9732B4CD7E7740 && Config::MotionBlur == EMotionBlur::Enhanced)
{
shader = g_enhancedMotionBlurShader.get();
}
}
SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.pixelShader, shader);
@ -5474,6 +5487,12 @@ static void ModelConsumerThread()
if (Config::GITextureFiltering == EGITextureFiltering::Bicubic)
pipelineState.specConstants |= SPEC_CONSTANT_BICUBIC_GI_FILTER;
auto createGraphicsPipeline = [&](PipelineState& pipelineStateToCreate, const char* name)
{
SanitizePipelineState(pipelineStateToCreate);
EnqueueGraphicsPipelineCompilation(pipelineStateToCreate, emptyHolderPair, name);
};
// Compile both MSAA and non MSAA variants to work with reflection maps. The render formats are an assumption but it should hold true.
if (Config::AntiAliasing != EAntiAliasing::None &&
pipelineState.renderTargetFormat == RenderFormat::R16G16B16A16_FLOAT &&
@ -5489,34 +5508,40 @@ static void ModelConsumerThread()
msaaPipelineState.specConstants |= SPEC_CONSTANT_ALPHA_TO_COVERAGE;
}
SanitizePipelineState(msaaPipelineState);
EnqueueGraphicsPipelineCompilation(msaaPipelineState, emptyHolderPair, "Precompiled Pipeline MSAA");
createGraphicsPipeline(msaaPipelineState, "Precompiled Pipeline MSAA");
}
// Compile the custom gaussian blur shaders that we pass to the game.
if (pipelineState.pixelShader != nullptr &&
pipelineState.pixelShader->shaderCacheEntry != nullptr &&
pipelineState.pixelShader->shaderCacheEntry->hash == 0x4294510C775F4EE8)
pipelineState.pixelShader->shaderCacheEntry != nullptr)
{
for (auto& shader : g_gaussianBlurShaders)
XXH64_hash_t hash = pipelineState.pixelShader->shaderCacheEntry->hash;
// Compile the custom gaussian blur shaders that we pass to the game.
if (hash == 0x4294510C775F4EE8)
{
pipelineState.pixelShader = shader.get();
SanitizePipelineState(pipelineState);
EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled Gaussian Blur Pipeline");
for (auto& shader : g_gaussianBlurShaders)
{
auto newPipelineState = pipelineState;
newPipelineState.pixelShader = shader.get();
createGraphicsPipeline(newPipelineState, "Precompiled Gaussian Blur Pipeline");
}
}
// Compile enhanced motion blur shader.
else if (hash == 0x6B9732B4CD7E7740)
{
auto newPipelineState = pipelineState;
newPipelineState.pixelShader = g_enhancedMotionBlurShader.get();
createGraphicsPipeline(newPipelineState, "Precompiled Enhanced Motion Blur Pipeline");
}
}
else
{
SanitizePipelineState(pipelineState);
EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled Pipeline");
}
createGraphicsPipeline(pipelineState, "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");
createGraphicsPipeline(pipelineState, "Precompiled CSD Filter Pipeline");
}
}

View file

@ -319,10 +319,10 @@ CONFIG_DEFINE_ENUM_LOCALE(EDepthOfFieldQuality)
{
ELanguage::English,
{
{ EDepthOfFieldQuality::Auto, { "AUTO", "" } },
{ EDepthOfFieldQuality::Low, { "LOW", "" } },
{ EDepthOfFieldQuality::Medium, { "MEDIUM", "" } },
{ EDepthOfFieldQuality::High, { "HIGH", "" } },
{ EDepthOfFieldQuality::Auto, { "AUTO", "" } },
{ EDepthOfFieldQuality::Low, { "LOW", "" } },
{ EDepthOfFieldQuality::Medium, { "MEDIUM", "" } },
{ EDepthOfFieldQuality::High, { "HIGH", "" } },
{ EDepthOfFieldQuality::Ultra, { "ULTRA", "" } },
}
}
@ -333,6 +333,18 @@ CONFIG_DEFINE_LOCALE(MotionBlur)
{ ELanguage::English, { "Motion Blur", "Use per-object motion blur and radial blur." } }
};
CONFIG_DEFINE_ENUM_LOCALE(EMotionBlur)
{
{
ELanguage::English,
{
{ EMotionBlur::Off, { "OFF", "" } },
{ EMotionBlur::Original, { "ORIGINAL", "" } },
{ EMotionBlur::Enhanced, { "ENHANCED", "" } }
}
}
};
CONFIG_DEFINE_LOCALE(XboxColourCorrection)
{
{ ELanguage::English, { "Xbox Color Correction", "Use the warm tint from the Xbox version of the game." } }

View file

@ -26,5 +26,5 @@ void CSDAspectRatioMidAsmHook(PPCRegister& f1, PPCRegister& f2)
bool MotionBlurMidAsmHook()
{
return Config::MotionBlur;
return Config::MotionBlur != EMotionBlur::Off;
}

View file

@ -64,7 +64,7 @@ public:
CONFIG_DEFINE_ENUM_LOCALISED("Video", EShadowResolution, ShadowResolution, EShadowResolution::x4096);
CONFIG_DEFINE_ENUM_LOCALISED("Video", EGITextureFiltering, GITextureFiltering, EGITextureFiltering::Bicubic);
CONFIG_DEFINE_ENUM_LOCALISED("Video", EDepthOfFieldQuality, DepthOfFieldQuality, EDepthOfFieldQuality::Auto);
CONFIG_DEFINE_LOCALISED("Video", bool, MotionBlur, true);
CONFIG_DEFINE_ENUM_LOCALISED("Video", EMotionBlur, MotionBlur, EMotionBlur::Original);
CONFIG_DEFINE_LOCALISED("Video", bool, XboxColourCorrection, false);
CONFIG_DEFINE_ENUM_LOCALISED("Video", EMovieScaleMode, MovieScaleMode, EMovieScaleMode::Fit);
CONFIG_DEFINE_ENUM_LOCALISED("Video", EUIScaleMode, UIScaleMode, EUIScaleMode::Centre);

View file

@ -203,13 +203,27 @@ enum class EDepthOfFieldQuality : uint32_t
CONFIG_DEFINE_ENUM_TEMPLATE(EDepthOfFieldQuality)
{
{ "Auto", EDepthOfFieldQuality::Auto },
{ "Low", EDepthOfFieldQuality::Low },
{ "Medium", EDepthOfFieldQuality::Medium },
{ "High", EDepthOfFieldQuality::High },
{ "Auto", EDepthOfFieldQuality::Auto },
{ "Low", EDepthOfFieldQuality::Low },
{ "Medium", EDepthOfFieldQuality::Medium },
{ "High", EDepthOfFieldQuality::High },
{ "Ultra", EDepthOfFieldQuality::Ultra }
};
enum class EMotionBlur : uint32_t
{
Off,
Original,
Enhanced
};
CONFIG_DEFINE_ENUM_TEMPLATE(EMotionBlur)
{
{ "Off", EMotionBlur::Off },
{ "Original", EMotionBlur::Original },
{ "Enhanced", EMotionBlur::Enhanced }
};
enum class EMovieScaleMode : uint32_t
{
Stretch,