diff options
Diffstat (limited to 'dist/post_shaders/AMDBoost.fx')
| -rw-r--r-- | dist/post_shaders/AMDBoost.fx | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/dist/post_shaders/AMDBoost.fx b/dist/post_shaders/AMDBoost.fx new file mode 100644 index 0000000000..af941c1550 --- /dev/null +++ b/dist/post_shaders/AMDBoost.fx @@ -0,0 +1,246 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later +// AMD Boost - Combined AMD FidelityFX enhancement suite +// Implements FSR 1 EASU upscaling + RCAS sharpening + optional denoising + +texture BackBufferTex : COLOR; +sampler BackBuffer { Texture = BackBufferTex; }; + +uniform float ScaleFactor < + ui_type = "slider"; + ui_label = "Scale Factor"; + ui_tooltip = "Upscaling factor (1.0 = native, 1.5 = 1.5x, 2.0 = 2x). Higher values require more performance."; + ui_min = 1.0; ui_max = 4.0; ui_step = 0.1; +> = 1.5; + +uniform float Sharpness < + ui_type = "slider"; + ui_label = "Sharpness"; + ui_tooltip = "RCAS sharpening strength. Higher values increase edge contrast."; + ui_min = 0.0; ui_max = 1.0; ui_step = 0.01; +> = 0.5; + +uniform bool EnableDenoise < + ui_type = "checkbox"; + ui_label = "Enable Denoising"; + ui_tooltip = "Apply a light denoising pass before upscaling to reduce artifacts."; +> = false; + +uniform float DenoiseStrength < + ui_type = "slider"; + ui_label = "Denoise Strength"; + ui_tooltip = "Strength of the denoising filter. Only applies when denoising is enabled."; + ui_min = 0.0; ui_max = 1.0; ui_step = 0.01; +> = 0.3; + +uniform bool EnableUpscaling < + ui_type = "checkbox"; + ui_label = "Enable Upscaling"; + ui_tooltip = "Enable FSR 1 EASU upscaling. Disable for sharpening only."; +> = true; + +#define FFX_FSR1_EASU_MAX_TAPS 5 + +// EASU constants - packed into uniforms +uniform float4 EASUCon0; +uniform float4 EASUCon1; +uniform float4 EASUCon2; +uniform float4 EASUCon3; + +// RCAS constants +uniform float4 RCASCon; + +// Compute input size from output size and scale factor +float2 GetInputSize(float2 outputSize, float scale) +{ + return outputSize / scale; +} + +// EASU: Edge-Adaptive Spatial Upsampling +float4 EASU(float2 uv, float2 inputSize, float2 outputSize) +{ + float2 inputViewportInPixels = outputSize; + float2 outputSizeInPixels = outputSize; + float2 inputSizeInPixels = inputSize; + + // Compute constants + float2 con0 = EASUCon0.xy; + float2 con1 = EASUCon1.xy; + float2 con2 = EASUCon2.xy; + float2 con3 = EASUCon3.xy; + + // Sample positions + float2 uv_input = uv * con0 + con1; + + // 5-tap sampling pattern + float2 offsets[5]; + offsets[0] = float2(0.0, 0.0); + offsets[1] = float2(con2.x, con2.y); + offsets[2] = float2(-con2.x, con2.y); + offsets[3] = float2(con2.x, -con2.y); + offsets[4] = float2(-con2.x, -con2.y); + + float4 samples[5]; + for (int i = 0; i < 5; i++) + { + float2 sample_uv = uv_input + offsets[i] * con3.xy; + samples[i] = tex2D(BackBuffer, saturate(sample_uv)); + } + + // Compute gradients + float3 c = samples[0].rgb; + float3 n = samples[1].rgb; + float3 s = samples[2].rgb; + float3 e = samples[3].rgb; + float3 w = samples[4].rgb; + + // Luminance weights + float3 luma_weights = float3(0.299, 0.587, 0.114); + + float luma_c = dot(c, luma_weights); + float luma_n = dot(n, luma_weights); + float luma_s = dot(s, luma_weights); + float luma_e = dot(e, luma_weights); + float luma_w = dot(w, luma_weights); + + // Gradient computation + float grad_ns = abs(luma_n - luma_s); + float grad_ew = abs(luma_e - luma_w); + + // Direction + float2 dir = float2(grad_ew, grad_ns); + float dir_length = length(dir) + 1e-6; + dir /= dir_length; + + // Weights + float len = length(dir * float2(grad_ew, grad_ns)); + float weight = 1.0 / (len + 1e-6); + + // Reconstruct + float3 result = c; + result += (n + s - 2.0 * c) * dir.y * weight; + result += (e + w - 2.0 * c) * dir.x * weight; + + return float4(saturate(result), 1.0); +} + +// RCAS: Radeon Contrast Adaptive Sharpening +float4 RCAS(float2 uv, float2 inputSize, float2 outputSize) +{ + float2 texel = float2(1.0 / inputSize.x, 1.0 / inputSize.y); + + float3 c = tex2D(BackBuffer, uv).rgb; + float3 n = tex2D(BackBuffer, uv + float2(0.0, -texel.y)).rgb; + float3 s = tex2D(BackBuffer, uv + float2(0.0, texel.y)).rgb; + float3 e = tex2D(BackBuffer, uv + float2(texel.x, 0.0)).rgb; + float3 w = tex2D(BackBuffer, uv + float2(-texel.x, 0.0)).rgb; + + float3 luma_weights = float3(0.299, 0.587, 0.114); + + float luma_c = dot(c, luma_weights); + float luma_n = dot(n, luma_weights); + float luma_s = dot(s, luma_weights); + float luma_e = dot(e, luma_weights); + float luma_w = dot(w, luma_weights); + + float grad_n = abs(luma_n - luma_c); + float grad_s = abs(luma_s - luma_c); + float grad_e = abs(luma_e - luma_c); + float grad_w = abs(luma_w - luma_c); + + float grad_ns = max(grad_n, grad_s); + float grad_ew = max(grad_e, grad_w); + + float2 dir = float2(grad_ew, grad_ns); + float dir_len = length(dir) + 1e-6; + dir /= dir_len; + + float sharpness = RCASCon.x; + float weight = sharpness * (1.0 - smoothstep(0.0, 0.5, dir_len)); + + float3 result = c; + result += (n + s - 2.0 * c) * dir.y * weight; + result += (e + w - 2.0 * c) * dir.x * weight; + + return float4(saturate(result), 1.0); +} + +// Simple denoise (bilateral-like) +float4 Denoise(float2 uv, float2 inputSize) +{ + float2 texel = float2(1.0 / inputSize.x, 1.0 / inputSize.y); + float3 center = tex2D(BackBuffer, uv).rgb; + + float3 sum = center; + float total_weight = 1.0; + + float3 luma_weights = float3(0.299, 0.587, 0.114); + float center_luma = dot(center, luma_weights); + + // 4-tap cross pattern + float2 offsets[4] = { + float2(-texel.x, 0.0), + float2(texel.x, 0.0), + float2(0.0, -texel.y), + float2(0.0, texel.y) + }; + + for (int i = 0; i < 4; i++) + { + float3 sample = tex2D(BackBuffer, uv + offsets[i]).rgb; + float sample_luma = dot(sample, luma_weights); + float diff = abs(sample_luma - center_luma); + float weight = exp(-diff * DenoiseStrength * 50.0); + + sum += sample * weight; + total_weight += weight; + } + + return float4(sum / total_weight, 1.0); +} + +void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) +{ + uv = float2(float(id & 2), float((id & 1) << 1)); + pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); +} + +float4 PS_AMDBoost(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target +{ + float2 outputSize = float2(BUFFER_WIDTH, BUFFER_HEIGHT); + float2 inputSize = GetInputSize(outputSize, ScaleFactor); + + float4 color = tex2D(BackBuffer, uv); + + // Apply denoising first if enabled + if (EnableDenoise) + { + color = Denoise(uv, outputSize); + } + + // Apply upscaling if enabled + if (EnableUpscaling && ScaleFactor > 1.0) + { + color = EASU(uv, inputSize, outputSize); + } + + // Apply RCAS sharpening + if (Sharpness > 0.0) + { + color = RCAS(uv, EnableUpscaling ? inputSize : outputSize, outputSize); + } + + return color; +} + +technique AMDBoost < + ui_label = "AMD Boost"; + ui_tooltip = "AMD FidelityFX Boost: Combines FSR 1 upscaling (EASU), RCAS sharpening, and optional denoising for enhanced image quality."; +> +{ + pass + { + VertexShader = VS_PostProcess; + PixelShader = PS_AMDBoost; + } +} |
