diff --git a/UnleashedRecomp/CMakeLists.txt b/UnleashedRecomp/CMakeLists.txt index 1ad1855..50459d3 100644 --- a/UnleashedRecomp/CMakeLists.txt +++ b/UnleashedRecomp/CMakeLists.txt @@ -284,6 +284,10 @@ function(compile_pixel_shader FILE_PATH) endfunction() compile_vertex_shader(copy_vs) +compile_pixel_shader(gaussian_blur_3x3) +compile_pixel_shader(gaussian_blur_5x5) +compile_pixel_shader(gaussian_blur_7x7) +compile_pixel_shader(gaussian_blur_9x9) compile_pixel_shader(gamma_correction_ps) compile_pixel_shader(imgui_ps) compile_vertex_shader(imgui_vs) diff --git a/UnleashedRecomp/gpu/shader/gaussian_blur.hlsli b/UnleashedRecomp/gpu/shader/gaussian_blur.hlsli new file mode 100644 index 0000000..2e24dcd --- /dev/null +++ b/UnleashedRecomp/gpu/shader/gaussian_blur.hlsli @@ -0,0 +1,71 @@ +#include "../../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h" + +#ifdef __spirv__ + +#define g_ViewportSize vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 384, 0x10) +#define g_offsets(INDEX) select((INDEX) < 74, vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + (150 + min(INDEX, 73)) * 16, 0x10), 0.0) +#define g_weights vk::RawBufferLoad(g_PushConstants.PixelShaderConstants + 2656, 0x10) + +#define s0_Texture2DDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 0) +#define s0_SamplerDescriptorIndex vk::RawBufferLoad(g_PushConstants.SharedConstants + 192) + +#else + +cbuffer PixelShaderConstants : register(b1, space4) +{ + float4 g_ViewportSize : packoffset(c24); + float4 g_offsets[2] : packoffset(c150); +#define g_offsets(INDEX) select((INDEX) < 74, g_offsets[min(INDEX, 73)], 0.0) + float4 g_weights : packoffset(c166); +}; + +cbuffer SharedConstants : register(b2, space4) +{ + uint s0_Texture2DDescriptorIndex : packoffset(c0.x); + uint s0_SamplerDescriptorIndex : packoffset(c12.x); + DEFINE_SHARED_CONSTANTS(); +}; + +#endif + +#ifdef __INTELLISENSE__ +#define KERNEL_SIZE 5 +#endif + +#define PI 3.14159265358979323846 + +float ComputeWeight(float x) +{ + float std = 0.952; + return exp(-(x * x) / (2.0 * std * std)) / (std * sqrt(2.0 * PI)); +} + +float4 main(in float4 iPosition : SV_Position, in float4 iTexCoord0 : TEXCOORD0) : SV_Target +{ + Texture2D texture = g_Texture2DDescriptorHeap[s0_Texture2DDescriptorIndex]; + SamplerState samplerState = g_SamplerDescriptorHeap[s0_SamplerDescriptorIndex]; + + float scale = g_ViewportSize.y / 360.0; + + float2 offsets[3]; + offsets[0] = g_offsets(0).xy * scale; + offsets[1] = g_offsets(0).zw * scale; + offsets[2] = g_offsets(1).xy * scale; + + float4 color = 0.0; + float weightSum = 0.0; + + [unroll] + for (int i = 0; i < KERNEL_SIZE; i++) + { + float step = i / float(KERNEL_SIZE - 1); + float scaled = step * 2; + 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; + weightSum += weight; + } + + return color / weightSum; +} diff --git a/UnleashedRecomp/gpu/shader/gaussian_blur_3x3.hlsl b/UnleashedRecomp/gpu/shader/gaussian_blur_3x3.hlsl new file mode 100644 index 0000000..9ad0906 --- /dev/null +++ b/UnleashedRecomp/gpu/shader/gaussian_blur_3x3.hlsl @@ -0,0 +1,2 @@ +#define KERNEL_SIZE 3 +#include "gaussian_blur.hlsli" diff --git a/UnleashedRecomp/gpu/shader/gaussian_blur_5x5.hlsl b/UnleashedRecomp/gpu/shader/gaussian_blur_5x5.hlsl new file mode 100644 index 0000000..727d2bf --- /dev/null +++ b/UnleashedRecomp/gpu/shader/gaussian_blur_5x5.hlsl @@ -0,0 +1,2 @@ +#define KERNEL_SIZE 5 +#include "gaussian_blur.hlsli" diff --git a/UnleashedRecomp/gpu/shader/gaussian_blur_7x7.hlsl b/UnleashedRecomp/gpu/shader/gaussian_blur_7x7.hlsl new file mode 100644 index 0000000..6aad31b --- /dev/null +++ b/UnleashedRecomp/gpu/shader/gaussian_blur_7x7.hlsl @@ -0,0 +1,2 @@ +#define KERNEL_SIZE 7 +#include "gaussian_blur.hlsli" diff --git a/UnleashedRecomp/gpu/shader/gaussian_blur_9x9.hlsl b/UnleashedRecomp/gpu/shader/gaussian_blur_9x9.hlsl new file mode 100644 index 0000000..98c9a84 --- /dev/null +++ b/UnleashedRecomp/gpu/shader/gaussian_blur_9x9.hlsl @@ -0,0 +1,2 @@ +#define KERNEL_SIZE 9 +#include "gaussian_blur.hlsli" diff --git a/UnleashedRecomp/gpu/video.cpp b/UnleashedRecomp/gpu/video.cpp index 24970e2..9459fb1 100644 --- a/UnleashedRecomp/gpu/video.cpp +++ b/UnleashedRecomp/gpu/video.cpp @@ -34,6 +34,14 @@ #include "../../thirdparty/ShaderRecomp/ShaderRecomp/shader_common.h" #include "shader/copy_vs.hlsl.dxil.h" #include "shader/copy_vs.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" +#include "shader/gaussian_blur_5x5.hlsl.spirv.h" +#include "shader/gaussian_blur_7x7.hlsl.dxil.h" +#include "shader/gaussian_blur_7x7.hlsl.spirv.h" +#include "shader/gaussian_blur_9x9.hlsl.dxil.h" +#include "shader/gaussian_blur_9x9.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" @@ -1025,6 +1033,17 @@ static const std::pair g_setRenderStateFunctions[] = static std::unique_ptr g_resolveMsaaDepthPipelines[3]; +enum +{ + GAUSSIAN_BLUR_3X3, + GAUSSIAN_BLUR_5X5, + GAUSSIAN_BLUR_7X7, + GAUSSIAN_BLUR_9X9, + GAUSSIAN_BLUR_COUNT +}; + +static std::unique_ptr g_gaussianBlurShaders[GAUSSIAN_BLUR_COUNT]; + #define CREATE_SHADER(NAME) \ g_device->createShader( \ g_vulkan ? g_##NAME##_spirv : g_##NAME##_dxil, \ @@ -1416,6 +1435,14 @@ void Video::CreateHostDevice() g_resolveMsaaDepthPipelines[i] = g_device->createGraphicsPipeline(desc); } + for (auto& shader : g_gaussianBlurShaders) + shader = std::make_unique(ResourceType::PixelShader); + + g_gaussianBlurShaders[GAUSSIAN_BLUR_3X3]->shader = CREATE_SHADER(gaussian_blur_3x3); + g_gaussianBlurShaders[GAUSSIAN_BLUR_5X5]->shader = CREATE_SHADER(gaussian_blur_5x5); + g_gaussianBlurShaders[GAUSSIAN_BLUR_7X7]->shader = CREATE_SHADER(gaussian_blur_7x7); + g_gaussianBlurShaders[GAUSSIAN_BLUR_9X9]->shader = CREATE_SHADER(gaussian_blur_9x9); + CreateImGuiBackend(); auto gammaCorrectionShader = CREATE_SHADER(gamma_correction_ps); @@ -3844,7 +3871,53 @@ static void SetPixelShader(GuestDevice* device, GuestShader* shader) static void ProcSetPixelShader(const RenderCommand& cmd) { - SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.pixelShader, cmd.setPixelShader.shader); + GuestShader* shader = cmd.setPixelShader.shader; + if (shader != nullptr && + shader->shaderCacheEntry != nullptr && + shader->shaderCacheEntry->hash == 0x4294510C775F4EE8) + { + size_t shaderIndex = GAUSSIAN_BLUR_3X3; + + switch (Config::DepthOfFieldQuality) + { + case EDepthOfFieldQuality::Low: + shaderIndex = GAUSSIAN_BLUR_3X3; + break; + + 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 + shaderIndex = GAUSSIAN_BLUR_3X3; + + break; + } + } + + shader = g_gaussianBlurShaders[shaderIndex].get(); + } + + SetDirtyValue(g_dirtyStates.pipelineState, g_pipelineState.pixelShader, shader); } static std::thread g_renderThread([] @@ -5391,8 +5464,23 @@ static void ModelConsumerThread() EnqueueGraphicsPipelineCompilation(msaaPipelineState, emptyHolderPair, "Precompiled Pipeline MSAA"); } - SanitizePipelineState(pipelineState); - EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled Pipeline"); + // 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) + { + for (auto& shader : g_gaussianBlurShaders) + { + pipelineState.pixelShader = shader.get(); + SanitizePipelineState(pipelineState); + EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled Gaussian Blur Pipeline"); + } + } + else + { + SanitizePipelineState(pipelineState); + EnqueueGraphicsPipelineCompilation(pipelineState, emptyHolderPair, "Precompiled Pipeline"); + } } g_pendingPipelineStateCache = false; diff --git a/UnleashedRecomp/locale/config_locale.h b/UnleashedRecomp/locale/config_locale.h index 86fced1..0c8382d 100644 --- a/UnleashedRecomp/locale/config_locale.h +++ b/UnleashedRecomp/locale/config_locale.h @@ -309,6 +309,25 @@ CONFIG_DEFINE_ENUM_LOCALE(EGITextureFiltering) } }; +CONFIG_DEFINE_LOCALE(DepthOfFieldQuality) +{ + { ELanguage::English, { "Depth of Field Quality", "[PLACEHOLDER]" } } +}; + +CONFIG_DEFINE_ENUM_LOCALE(EDepthOfFieldQuality) +{ + { + ELanguage::English, + { + { EDepthOfFieldQuality::Auto, { "AUTO", "" } }, + { EDepthOfFieldQuality::Low, { "LOW", "" } }, + { EDepthOfFieldQuality::Medium, { "MEDIUM", "" } }, + { EDepthOfFieldQuality::High, { "HIGH", "" } }, + { EDepthOfFieldQuality::Ultra, { "ULTRA", "" } }, + } + } +}; + CONFIG_DEFINE_LOCALE(MotionBlur) { { ELanguage::English, { "Motion Blur", "Use per-object motion blur and radial blur." } } diff --git a/UnleashedRecomp/user/config.h b/UnleashedRecomp/user/config.h index 44a14e6..d3e8241 100644 --- a/UnleashedRecomp/user/config.h +++ b/UnleashedRecomp/user/config.h @@ -63,6 +63,7 @@ public: CONFIG_DEFINE("Video", size_t, AnisotropicFiltering, 16); 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_LOCALISED("Video", bool, XboxColourCorrection, false); CONFIG_DEFINE_ENUM_LOCALISED("Video", EMovieScaleMode, MovieScaleMode, EMovieScaleMode::Fit); diff --git a/UnleashedRecomp/user/config_detail.h b/UnleashedRecomp/user/config_detail.h index 183bee0..7151b39 100644 --- a/UnleashedRecomp/user/config_detail.h +++ b/UnleashedRecomp/user/config_detail.h @@ -192,6 +192,24 @@ CONFIG_DEFINE_ENUM_TEMPLATE(EGITextureFiltering) { "Bicubic", EGITextureFiltering::Bicubic } }; +enum class EDepthOfFieldQuality : uint32_t +{ + Auto, + Low, + Medium, + High, + Ultra +}; + +CONFIG_DEFINE_ENUM_TEMPLATE(EDepthOfFieldQuality) +{ + { "Auto", EDepthOfFieldQuality::Auto }, + { "Low", EDepthOfFieldQuality::Low }, + { "Medium", EDepthOfFieldQuality::Medium }, + { "High", EDepthOfFieldQuality::High }, + { "Ultra", EDepthOfFieldQuality::Ultra } +}; + enum class EMovieScaleMode : uint32_t { Stretch,