From 81734e85b941e5fabe6e51c1b97e382b28ef030d Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Fri, 4 Sep 2026 22:45:02 -0400 Subject: [PATCH] Bundle extra shaders ported from PPSSPP --- dist/post_shaders/Bloom.fx | 75 +++++++++++++++++ dist/post_shaders/CRT.fx | 79 +++++++++++++++++ dist/post_shaders/Cartoon.fx | 84 +++++++++++++++++++ .../{EdenColorGrade.fx => ColorGrade.fx} | 19 +++-- dist/post_shaders/EdenSharpen.fx | 45 ---------- dist/post_shaders/FakeReflections.fx | 71 ++++++++++++++++ dist/post_shaders/NaturalColors.fx | 65 ++++++++++++++ dist/post_shaders/Scanlines.fx | 71 ++++++++++++++++ dist/post_shaders/Sharpen.fx | 48 +++++++++++ .../{EdenVignette.fx => Vignette.fx} | 19 +++-- src/video_core/CMakeLists.txt | 14 ++++ .../GenerateBundledEffects.cmake | 50 +++++++++++ src/video_core/post_processing/fx_effect.cpp | 13 ++- 13 files changed, 594 insertions(+), 59 deletions(-) create mode 100644 dist/post_shaders/Bloom.fx create mode 100644 dist/post_shaders/CRT.fx create mode 100644 dist/post_shaders/Cartoon.fx rename dist/post_shaders/{EdenColorGrade.fx => ColorGrade.fx} (68%) delete mode 100644 dist/post_shaders/EdenSharpen.fx create mode 100644 dist/post_shaders/FakeReflections.fx create mode 100644 dist/post_shaders/NaturalColors.fx create mode 100644 dist/post_shaders/Scanlines.fx create mode 100644 dist/post_shaders/Sharpen.fx rename dist/post_shaders/{EdenVignette.fx => Vignette.fx} (60%) create mode 100644 src/video_core/post_processing/GenerateBundledEffects.cmake diff --git a/dist/post_shaders/Bloom.fx b/dist/post_shaders/Bloom.fx new file mode 100644 index 0000000000..ad607c69a9 --- /dev/null +++ b/dist/post_shaders/Bloom.fx @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project +// SPDX-License-Identifier: GPL-2.0-or-later +// Ported from bloomnoblur.fsh in PPSSPP. + +texture BackBufferTex : COLOR; +sampler BackBuffer { Texture = BackBufferTex; }; + +uniform float Radius < + ui_type = "slider"; + ui_label = "Radius"; + ui_tooltip = "How far the glow spreads from bright areas."; + ui_min = 0.0; ui_max = 4.0; ui_step = 0.05; +> = 1.0; + +uniform float Amount < + ui_type = "slider"; + ui_label = "Amount"; + ui_tooltip = "Strength of the glow added on top of the image."; + ui_min = 0.0; ui_max = 2.0; ui_step = 0.05; +> = 0.6; + +void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) +{ + uv = float2(0.0, 0.0); + if (id == 2) + { + uv.x = 2.0; + } + if (id == 1) + { + uv.y = 2.0; + } + pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); +} + +float Weight(float3 color) +{ + float gray = (color.r + color.g + color.b) / 3.0; + float saturation = (abs(color.r - gray) + abs(color.g - gray) + abs(color.b - gray)) / 3.0; + return gray * gray / max(saturation, 0.25); +} + +float4 PS_Bloom(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target +{ + float3 color = tex2D(BackBuffer, uv).rgb; + + float gray = (color.r + color.g + color.b) / 3.0; + float saturation = (abs(color.r - gray) + abs(color.g - gray) + abs(color.b - gray)) / 3.0; + float spread = 0.002 * gray / max(saturation, 0.25) * Radius; + + float3 sum = float3(0.0, 0.0, 0.0); + for (int x = -3; x <= 3; x += 2) + { + for (int y = -3; y <= 3; y += 2) + { + float3 tap = tex2D(BackBuffer, uv + float2(x, y) * spread).rgb; + sum += tap * Weight(tap); + } + } + sum /= 16.0; + + return float4(saturate(color + sum * Amount), 1.0); +} + +technique Bloom +{ + pass + { + VertexShader = VS_PostProcess; + PixelShader = PS_Bloom; + } +} diff --git a/dist/post_shaders/CRT.fx b/dist/post_shaders/CRT.fx new file mode 100644 index 0000000000..528c6f9a3d --- /dev/null +++ b/dist/post_shaders/CRT.fx @@ -0,0 +1,79 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project +// SPDX-License-Identifier: GPL-2.0-or-later +// Ported from crt.fsh in PPSSPP, by KillaMaaki. + + +texture BackBufferTex : COLOR; +sampler BackBuffer { Texture = BackBufferTex; }; + +uniform float Timer < source = "timer"; > = 0.0; + +uniform float Density < + ui_type = "slider"; + ui_label = "Line Density"; + ui_min = 60.0; ui_max = 720.0; ui_step = 10.0; +> = 272.0; + +uniform float RollSpeed < + ui_type = "slider"; + ui_label = "Roll Speed"; + ui_tooltip = "Speed of the rolling bar. Zero disables it."; + ui_min = 0.0; ui_max = 2.0; ui_step = 0.05; +> = 1.0; + +uniform float Bleed < + ui_type = "slider"; + ui_label = "Colour Bleed"; + ui_tooltip = "Horizontal separation of the red and green channels."; + ui_min = 0.0; ui_max = 4.0; ui_step = 0.1; +> = 1.0; + +void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) +{ + uv = float2(0.0, 0.0); + if (id == 2) + { + uv.x = 2.0; + } + if (id == 1) + { + uv.y = 2.0; + } + pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); +} + +float4 PS_CRT(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target +{ + float seconds = Timer * 0.001; + float scan = floor((uv.y + seconds * RollSpeed * 0.5) * Density); + float line_intensity = frac(scan * 0.5) * 2.0; + + float2 shift = float2(line_intensity * 0.0005, 0.0); + float2 bleed = float2(BUFFER_RCP_WIDTH * Bleed, 0.0); + + float r = tex2D(BackBuffer, uv + bleed + shift).r; + float g = tex2D(BackBuffer, uv - bleed + shift).g; + float b = tex2D(BackBuffer, uv).b; + + float3 color = float3(r, g * 0.99, b) * clamp(line_intensity, 0.85, 1.0); + + if (RollSpeed > 0.0) + { + float rollbar = sin((uv.y + seconds * RollSpeed) * 4.0); + color += rollbar * 0.02; + } + + return float4(saturate(color), 1.0); +} + +technique CRT +{ + pass + { + VertexShader = VS_PostProcess; + PixelShader = PS_CRT; + } +} diff --git a/dist/post_shaders/Cartoon.fx b/dist/post_shaders/Cartoon.fx new file mode 100644 index 0000000000..2198673250 --- /dev/null +++ b/dist/post_shaders/Cartoon.fx @@ -0,0 +1,84 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project +// SPDX-FileCopyrightText: guest(r) +// SPDX-License-Identifier: GPL-2.0-or-later +// Ported from cartoon.fsh in PPSSPP, Advanced Cartoon shader I by guest(r). + +texture BackBufferTex : COLOR; +sampler BackBuffer { Texture = BackBufferTex; }; + +uniform float EdgeStrength < + ui_type = "slider"; + ui_label = "Edge Strength"; + ui_tooltip = "Darkness of the ink outline drawn around detected edges."; + ui_min = 0.0; ui_max = 2.0; ui_step = 0.05; +> = 0.5; + +uniform float Levels < + ui_type = "slider"; + ui_label = "Colour Levels"; + ui_tooltip = "How many bands the colours are quantised into."; + ui_min = 2.0; ui_max = 16.0; ui_step = 1.0; +> = 4.0; + +void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) +{ + uv = float2(0.0, 0.0); + if (id == 2) + { + uv.x = 2.0; + } + if (id == 1) + { + uv.y = 2.0; + } + pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); +} + +float4 PS_Cartoon(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target +{ + float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT); + + float3 c00 = tex2D(BackBuffer, uv + texel * float2(-1.0, -1.0)).rgb; + float3 c10 = tex2D(BackBuffer, uv + texel * float2( 0.0, -1.0)).rgb; + float3 c20 = tex2D(BackBuffer, uv + texel * float2( 1.0, -1.0)).rgb; + float3 c01 = tex2D(BackBuffer, uv + texel * float2(-1.0, 0.0)).rgb; + float3 c11 = tex2D(BackBuffer, uv).rgb; + float3 c21 = tex2D(BackBuffer, uv + texel * float2( 1.0, 0.0)).rgb; + float3 c02 = tex2D(BackBuffer, uv + texel * float2(-1.0, 1.0)).rgb; + float3 c12 = tex2D(BackBuffer, uv + texel * float2( 0.0, 1.0)).rgb; + float3 c22 = tex2D(BackBuffer, uv + texel * float2( 1.0, 1.0)).rgb; + + const float3 dt = float3(1.0, 1.0, 1.0); + + float d1 = dot(abs(c00 - c22), dt); + float d2 = dot(abs(c20 - c02), dt); + float hl = dot(abs(c01 - c21), dt); + float vl = dot(abs(c10 - c12), dt); + float edge = EdgeStrength * (d1 + d2 + hl + vl) / (dot(c11, dt) + 0.15); + + float lc = Levels * length(c11); + float f = frac(lc); + f *= f; + lc = (floor(lc) + f * f) / Levels + 0.05; + + float3 unit = normalize(max(c11, 0.0001)); + float3 quant = Levels * unit; + float3 frct = frac(quant); + frct *= frct; + quant = floor(quant) + 0.05 * dt + frct * frct; + + float3 color = lc * (1.1 - edge * sqrt(edge)) * quant / Levels; + return float4(saturate(color), 1.0); +} + +technique Cartoon +{ + pass + { + VertexShader = VS_PostProcess; + PixelShader = PS_Cartoon; + } +} diff --git a/dist/post_shaders/EdenColorGrade.fx b/dist/post_shaders/ColorGrade.fx similarity index 68% rename from dist/post_shaders/EdenColorGrade.fx rename to dist/post_shaders/ColorGrade.fx index bd80f8d4bb..bf5c48f9e1 100644 --- a/dist/post_shaders/EdenColorGrade.fx +++ b/dist/post_shaders/ColorGrade.fx @@ -1,5 +1,12 @@ -texture EdenBackBufferTex : COLOR; -sampler EdenBackBuffer { Texture = EdenBackBufferTex; }; +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project +// SPDX-License-Identifier: GPL-2.0-or-later +// Based on colorcorrection.fsh in PPSSPP. + +texture BackBufferTex : COLOR; +sampler BackBuffer { Texture = BackBufferTex; }; uniform float Saturation < ui_type = "slider"; @@ -25,7 +32,7 @@ uniform float Gamma < ui_min = 0.5; ui_max = 2.0; ui_step = 0.01; > = 1.0; -void VS_Eden(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) +void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) { uv = float2(0.0, 0.0); if (id == 2) @@ -41,7 +48,7 @@ void VS_Eden(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 float4 PS_ColorGrade(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target { - float3 rgb = tex2D(EdenBackBuffer, uv).rgb; + float3 rgb = tex2D(BackBuffer, uv).rgb; float luma = dot(rgb, float3(0.2126, 0.7152, 0.0722)); rgb = lerp(float3(luma, luma, luma), rgb, Saturation); @@ -52,11 +59,11 @@ float4 PS_ColorGrade(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target return float4(saturate(rgb), 1.0); } -technique EdenColorGrade +technique ColorGrade { pass { - VertexShader = VS_Eden; + VertexShader = VS_PostProcess; PixelShader = PS_ColorGrade; } } diff --git a/dist/post_shaders/EdenSharpen.fx b/dist/post_shaders/EdenSharpen.fx deleted file mode 100644 index 323d764719..0000000000 --- a/dist/post_shaders/EdenSharpen.fx +++ /dev/null @@ -1,45 +0,0 @@ -texture EdenBackBufferTex : COLOR; -sampler EdenBackBuffer { Texture = EdenBackBufferTex; }; - -uniform float Amount < - ui_type = "slider"; - ui_label = "Amount"; - ui_min = 0.0; ui_max = 3.0; ui_step = 0.01; -> = 0.6; - -void VS_Eden(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) -{ - uv = float2(0.0, 0.0); - if (id == 2) - { - uv.x = 2.0; - } - if (id == 1) - { - uv.y = 2.0; - } - pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); -} - -float4 PS_Sharpen(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target -{ - float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT); - - float3 centre = tex2D(EdenBackBuffer, uv).rgb; - float3 blur = tex2D(EdenBackBuffer, uv + float2(-texel.x, 0.0)).rgb; - blur += tex2D(EdenBackBuffer, uv + float2(texel.x, 0.0)).rgb; - blur += tex2D(EdenBackBuffer, uv + float2(0.0, -texel.y)).rgb; - blur += tex2D(EdenBackBuffer, uv + float2(0.0, texel.y)).rgb; - blur *= 0.25; - - return float4(centre + (centre - blur) * Amount, 1.0); -} - -technique EdenSharpen -{ - pass - { - VertexShader = VS_Eden; - PixelShader = PS_Sharpen; - } -} diff --git a/dist/post_shaders/FakeReflections.fx b/dist/post_shaders/FakeReflections.fx new file mode 100644 index 0000000000..3dbcf6875b --- /dev/null +++ b/dist/post_shaders/FakeReflections.fx @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project +// SPDX-License-Identifier: GPL-2.0-or-later +// Ported from fakereflections.fsh in PPSSPP. + +texture BackBufferTex : COLOR; +sampler BackBuffer { Texture = BackBufferTex; }; + +uniform float Amount < + ui_type = "slider"; + ui_label = "Amount"; + ui_tooltip = "Strength of the reflection added to the lower half of the screen."; + ui_min = 0.0; ui_max = 1.0; ui_step = 0.05; +> = 0.6; + +uniform float Power < + ui_type = "slider"; + ui_label = "Power"; + ui_tooltip = "How sharply the reflection falls off away from bright areas."; + ui_min = 0.0; ui_max = 1.0; ui_step = 0.05; +> = 0.5; + +void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) +{ + uv = float2(0.0, 0.0); + if (id == 2) + { + uv.x = 2.0; + } + if (id == 1) + { + uv.y = 2.0; + } + pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); +} + +float Wrap(float value, float period) +{ + return period * frac(value / period); +} + +float4 PS_FakeReflections(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target +{ + float3 color = tex2D(BackBuffer, uv).rgb; + + float gray = (color.r + color.g + color.b) / 3.0; + float saturation = (abs(color.r - gray) + abs(color.g - gray) + abs(color.b - gray)) / 3.0; + + float rndx = Wrap(uv.x + gray, 0.03) + Wrap(uv.y + saturation, 0.05); + float rndy = Wrap(uv.y + saturation, 0.03) + Wrap(uv.x + gray, 0.05); + + float falloff = (max(gray, saturation) + 0.1) * uv.y; + float2 offset = float2(rndx, rndy - min(uv.y, 0.25)) * falloff; + + float3 reflection = tex2D(BackBuffer, uv + offset).rgb; + reflection *= 4.0 * (1.0 - gray) * Amount; + reflection *= reflection * falloff * Power; + + return float4(saturate(color + reflection), 1.0); +} + +technique FakeReflections +{ + pass + { + VertexShader = VS_PostProcess; + PixelShader = PS_FakeReflections; + } +} diff --git a/dist/post_shaders/NaturalColors.fx b/dist/post_shaders/NaturalColors.fx new file mode 100644 index 0000000000..7dda32776f --- /dev/null +++ b/dist/post_shaders/NaturalColors.fx @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project +// SPDX-License-Identifier: GPL-2.0-or-later +// Ported from naturalA.fsh in PPSSPP, by ShadX, modified by SimoneT. + +texture BackBufferTex : COLOR; +sampler BackBuffer { Texture = BackBufferTex; }; + +uniform float Luma < + ui_type = "slider"; + ui_label = "Luma Curve"; + ui_tooltip = "Gamma applied to the luminance channel in YIQ space."; + ui_min = 0.5; ui_max = 2.0; ui_step = 0.01; +> = 1.2; + +uniform float Chroma < + ui_type = "slider"; + ui_label = "Chroma Gain"; + ui_tooltip = "Boost applied to the two colour difference channels."; + ui_min = 0.0; ui_max = 2.0; ui_step = 0.01; +> = 1.2; + +void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) +{ + uv = float2(0.0, 0.0); + if (id == 2) + { + uv.x = 2.0; + } + if (id == 1) + { + uv.y = 2.0; + } + pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); +} + +float4 PS_Natural(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target +{ + const float3x3 RGBtoYIQ = float3x3(0.299, 0.587, 0.114, + 0.596, -0.275, -0.321, + 0.212, -0.523, 0.311); + + const float3x3 YIQtoRGB = float3x3(1.0, 0.95568806, 0.61985809, + 1.0, -0.27158180, -0.64687382, + 1.0, -1.10817733, 1.70506456); + + float3 rgb = tex2D(BackBuffer, uv).rgb; + float3 yiq = mul(RGBtoYIQ, rgb); + + yiq.x = pow(max(yiq.x, 0.0), Luma); + yiq.yz *= Chroma; + + return float4(saturate(mul(YIQtoRGB, yiq)), 1.0); +} + +technique NaturalColors +{ + pass + { + VertexShader = VS_PostProcess; + PixelShader = PS_Natural; + } +} diff --git a/dist/post_shaders/Scanlines.fx b/dist/post_shaders/Scanlines.fx new file mode 100644 index 0000000000..f091efc372 --- /dev/null +++ b/dist/post_shaders/Scanlines.fx @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project +// SPDX-License-Identifier: GPL-2.0-or-later +// Ported from scanlines.fsh in PPSSPP. + + +texture BackBufferTex : COLOR; +sampler BackBuffer { Texture = BackBufferTex; }; + +uniform float Density < + ui_type = "slider"; + ui_label = "Line Density"; + ui_tooltip = "Number of scanline pairs across the screen."; + ui_min = 60.0; ui_max = 720.0; ui_step = 10.0; +> = 340.0; + +uniform float Intensity < + ui_type = "slider"; + ui_label = "Intensity"; + ui_tooltip = "How dark the gaps between lines become."; + ui_min = 0.0; ui_max = 1.0; ui_step = 0.05; +> = 0.5; + +uniform float Tint < + ui_type = "slider"; + ui_label = "Phosphor Tint"; + ui_tooltip = "Strength of the green-warm phosphor cast."; + ui_min = 0.0; ui_max = 1.0; ui_step = 0.05; +> = 1.0; + +void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) +{ + uv = float2(0.0, 0.0); + if (id == 2) + { + uv.x = 2.0; + } + if (id == 1) + { + uv.y = 2.0; + } + pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); +} + +float4 PS_Scanlines(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target +{ + float line_pos = uv.y * Density * 0.5; + float gate = cos((frac(line_pos) - 0.5) * 3.1415926 * Intensity) * 1.5; + + float3 rgb = tex2D(BackBuffer, uv).rgb; + float3 color = rgb * 0.5 + 0.5 * rgb * rgb * 1.2; + + float3 phosphor = lerp(float3(1.0, 1.0, 1.0), float3(0.9, 1.0, 0.7), Tint); + color *= phosphor; + + float2 diff = uv - 0.5; + color *= 1.1 - 0.6 * (dot(diff, diff) * 2.0); + + return float4(saturate(color * saturate(gate)), 1.0); +} + +technique Scanlines +{ + pass + { + VertexShader = VS_PostProcess; + PixelShader = PS_Scanlines; + } +} diff --git a/dist/post_shaders/Sharpen.fx b/dist/post_shaders/Sharpen.fx new file mode 100644 index 0000000000..a0e6f56052 --- /dev/null +++ b/dist/post_shaders/Sharpen.fx @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +texture BackBufferTex : COLOR; +sampler BackBuffer { Texture = BackBufferTex; }; + +uniform float Amount < + ui_type = "slider"; + ui_label = "Amount"; + ui_min = 0.0; ui_max = 3.0; ui_step = 0.01; +> = 0.6; + +void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) +{ + uv = float2(0.0, 0.0); + if (id == 2) + { + uv.x = 2.0; + } + if (id == 1) + { + uv.y = 2.0; + } + pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0); +} + +float4 PS_Sharpen(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target +{ + float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT); + + float3 centre = tex2D(BackBuffer, uv).rgb; + float3 blur = tex2D(BackBuffer, uv + float2(-texel.x, 0.0)).rgb; + blur += tex2D(BackBuffer, uv + float2(texel.x, 0.0)).rgb; + blur += tex2D(BackBuffer, uv + float2(0.0, -texel.y)).rgb; + blur += tex2D(BackBuffer, uv + float2(0.0, texel.y)).rgb; + blur *= 0.25; + + return float4(centre + (centre - blur) * Amount, 1.0); +} + +technique Sharpen +{ + pass + { + VertexShader = VS_PostProcess; + PixelShader = PS_Sharpen; + } +} diff --git a/dist/post_shaders/EdenVignette.fx b/dist/post_shaders/Vignette.fx similarity index 60% rename from dist/post_shaders/EdenVignette.fx rename to dist/post_shaders/Vignette.fx index 032a7699ef..f7c194c84e 100644 --- a/dist/post_shaders/EdenVignette.fx +++ b/dist/post_shaders/Vignette.fx @@ -1,5 +1,12 @@ -texture EdenBackBufferTex : COLOR; -sampler EdenBackBuffer { Texture = EdenBackBufferTex; }; +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project +// SPDX-License-Identifier: GPL-2.0-or-later +// Ported from vignette.fsh in PPSSPP, by Henrik Rydgard. + +texture BackBufferTex : COLOR; +sampler BackBuffer { Texture = BackBufferTex; }; uniform float Strength < ui_type = "slider"; @@ -14,7 +21,7 @@ uniform float Aspect < ui_min = 0.5; ui_max = 2.0; ui_step = 0.01; > = 1.0; -void VS_Eden(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) +void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD) { uv = float2(0.0, 0.0); if (id == 2) @@ -35,16 +42,16 @@ float4 PS_Vignette(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target diff.y /= max(Aspect, 0.0001); float falloff = 1.0 - min(1.0, Strength * dot(diff, diff) * 2.0); - float3 rgb = tex2D(EdenBackBuffer, uv).rgb; + float3 rgb = tex2D(BackBuffer, uv).rgb; return float4(rgb * falloff, 1.0); } -technique EdenVignette +technique Vignette { pass { - VertexShader = VS_Eden; + VertexShader = VS_PostProcess; PixelShader = PS_Vignette; } } diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 4e37901652..4b22a56793 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -300,6 +300,18 @@ if (ENABLE_LSFG) endif() if (ENABLE_RESHADE) + set(BUNDLED_FX_DIR ${CMAKE_SOURCE_DIR}/dist/post_shaders) + set(BUNDLED_FX_HEADER ${CMAKE_CURRENT_BINARY_DIR}/bundled_fx_effects.h) + file(GLOB BUNDLED_FX_FILES ${BUNDLED_FX_DIR}/*.fx) + + add_custom_command( + OUTPUT ${BUNDLED_FX_HEADER} + COMMAND ${CMAKE_COMMAND} -P + ${CMAKE_CURRENT_SOURCE_DIR}/post_processing/GenerateBundledEffects.cmake + ${BUNDLED_FX_DIR} ${BUNDLED_FX_HEADER} + DEPENDS ${BUNDLED_FX_FILES} + ) + target_sources(video_core PRIVATE post_processing/fx_chain.cpp post_processing/fx_chain.h @@ -309,7 +321,9 @@ if (ENABLE_RESHADE) post_processing/fx_effect.h renderer_vulkan/present/post_process.cpp renderer_vulkan/present/post_process.h + ${BUNDLED_FX_HEADER} ) + target_include_directories(video_core PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_link_libraries(video_core PRIVATE reshadefx::reshadefx) target_compile_definitions(video_core PUBLIC HAS_RESHADE) endif() diff --git a/src/video_core/post_processing/GenerateBundledEffects.cmake b/src/video_core/post_processing/GenerateBundledEffects.cmake new file mode 100644 index 0000000000..d3aca02571 --- /dev/null +++ b/src/video_core/post_processing/GenerateBundledEffects.cmake @@ -0,0 +1,50 @@ +# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +# SPDX-License-Identifier: GPL-3.0-or-later + +set(EFFECT_DIR ${CMAKE_ARGV3}) +set(HEADER_FILE ${CMAKE_ARGV4}) + +file(GLOB EFFECT_FILES ${EFFECT_DIR}/*.fx) +list(SORT EFFECT_FILES) + +set(ENTRIES "") +foreach(EFFECT_FILE IN LISTS EFFECT_FILES) + get_filename_component(EFFECT_NAME ${EFFECT_FILE} NAME) + file(READ ${EFFECT_FILE} EFFECT_BODY) + + string(REGEX REPLACE ";" "{{SEMICOLON}}" EFFECT_BODY "${EFFECT_BODY}") + string(REGEX REPLACE "\n" ";" EFFECT_BODY "${EFFECT_BODY}") + + set(EFFECT_TEXT "") + foreach(LINE IN LISTS EFFECT_BODY) + string(CONCAT EFFECT_TEXT "${EFFECT_TEXT}" " R\"(${LINE}\n)\"\n") + endforeach() + string(REGEX REPLACE "{{SEMICOLON}}" ";" EFFECT_TEXT "${EFFECT_TEXT}") + + string(CONCAT ENTRIES "${ENTRIES}" + " {\n \"${EFFECT_NAME}\",\n${EFFECT_TEXT} },\n") +endforeach() + +get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY) +make_directory(${OUTPUT_DIR}) + +file(WRITE ${HEADER_FILE} +"// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include + +namespace VideoCore { + +struct BundledFxEffect { + std::string_view name; + std::string_view source; +}; + +constexpr BundledFxEffect BUNDLED_FX_EFFECTS[]{ +${ENTRIES}}; + +} // namespace VideoCore +") diff --git a/src/video_core/post_processing/fx_effect.cpp b/src/video_core/post_processing/fx_effect.cpp index 9807594cf5..bfc61378cd 100644 --- a/src/video_core/post_processing/fx_effect.cpp +++ b/src/video_core/post_processing/fx_effect.cpp @@ -3,6 +3,8 @@ #include +#include "bundled_fx_effects.h" +#include "common/fs/file.h" #include "common/fs/fs.h" #include "common/fs/fs_util.h" #include "common/fs/path_util.h" @@ -228,11 +230,18 @@ void ReloadFxCatalog() { catalog_scanned = true; const auto root = GetFxRootDirectory(); - if (!Common::FS::Exists(root)) { - void(Common::FS::CreateDirs(root)); + if (!Common::FS::Exists(root) && !Common::FS::CreateDirs(root)) { return; } + for (const auto& bundled : BUNDLED_FX_EFFECTS) { + const auto path = root / bundled.name; + if (Common::FS::Exists(path)) { + continue; + } + void(Common::FS::WriteStringToFile(path, Common::FS::FileType::TextFile, bundled.source)); + } + std::vector effect_files; Common::FS::IterateDirEntriesRecursively( root,