mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-09-11 14:24:20 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3cf8d5eb49 |
@@ -1,11 +0,0 @@
|
||||
--- a/source/effect_lexer.cpp
|
||||
+++ b/source/effect_lexer.cpp
|
||||
@@ -1181,7 +1181,7 @@
|
||||
}
|
||||
else
|
||||
{
|
||||
-#if 0
|
||||
+#if !defined(__cpp_lib_to_chars)
|
||||
exponent += decimal_location - mantissa_size;
|
||||
|
||||
const bool exponent_negative = exponent < 0;
|
||||
@@ -85,8 +85,6 @@ option(ENABLE_WERROR "Enable -Werror diagnostics" ON)
|
||||
# Lossless Scaling frame generation. Only Android.
|
||||
cmake_dependent_option(ENABLE_LSFG "Enable Lossless Scaling frame generation" ON "ANDROID" OFF)
|
||||
|
||||
option(ENABLE_RESHADE "Enable ReShade FX post-processing effects" ON)
|
||||
|
||||
# non-linux bundled qt are static
|
||||
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
|
||||
set(YUZU_STATIC_BUILD ON)
|
||||
|
||||
@@ -238,14 +238,6 @@
|
||||
"repo": "stachenov/quazip",
|
||||
"version": "2e95c9001b"
|
||||
},
|
||||
"reshade": {
|
||||
"hash": "6fc9b39821ca6e2d91160f05846b3a42159fec759f5d6fba91d1a57801a1bdaf6047740578c136f0b1a60a9c29ad160d87dbfe0dbfbcdf6ea879a8a078377b8c",
|
||||
"patches": [
|
||||
"0001-from-chars-fallback.patch"
|
||||
],
|
||||
"repo": "crosire/reshade",
|
||||
"version": "v6.8.0"
|
||||
},
|
||||
"sdl3": {
|
||||
"hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3",
|
||||
"min_version": "3.2.10",
|
||||
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
name=Anime
|
||||
description=Cel shaded look: the picture is cleaned first, then the lighting is collapsed into flat bands with inked edges, finished with a small glow.
|
||||
chain=Denoise.fx|Denoise|Strength=0.18,Radius=1.2;CelShading.fx|CelShading|Bands=5,BandContrast=0.35,BandEdge=0.2,Detail=0.7,OutlineStrength=0.7,OutlineThreshold=0.2,Saturation=1.3;Bloom.fx|Bloom|Radius=1.2,Amount=0.25
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
name=Cinematic
|
||||
description=Film look: soft glow on the highlights, a filmic contrast curve, cool shadows against warm highlights, fine grain and a gentle vignette.
|
||||
chain=Bloom.fx|Bloom|Radius=1.6,Amount=0.35;FilmicCurve.fx|FilmicCurve|Toe=1.35,Shoulder=1.3,Amount=0.85;SplitToning.fx|SplitToning|ShadowHue=210,ShadowStrength=0.25,HighlightHue=38,HighlightStrength=0.3;FilmGrain.fx|FilmGrain|Intensity=0.022;Vignette.fx|Vignette|Strength=0.5
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
name=Clean
|
||||
description=Cleans up a low internal resolution: removes dithering and compression noise, smooths banded skies and brings edge detail back. The safe starting point if you are not sure what you want.
|
||||
chain=Denoise.fx|Denoise|Strength=0.14,Curve=1.2;Deband.fx|Deband|;Sharpen.fx|Sharpen|Amount=0.75
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
name=Dreamy
|
||||
description=Soft focus: the centre of the screen stays sharp while everything around it blurs, with a wide glow over the highlights. Good for slower games and cutscenes.
|
||||
chain=Blur.fx|Blur|Radius=6,Strength=0.85,FocusSize=0.28,FocusSoftness=0.5;Bloom.fx|Bloom|Radius=2.0,Amount=0.55
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
name=Retro
|
||||
description=Old television look: colour fringing at the edges of the screen, curved scanlines with a phosphor mask, and darkened corners. The scanline roll is switched off so it does not crawl.
|
||||
chain=ChromaticAberration.fx|ChromaticAberration|Strength=1.0;CRT.fx|CRT|RollSpeed=0,Bleed=1.2;Vignette.fx|Vignette|Strength=0.7
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
name=Vivid
|
||||
description=Punchier colour without stylising the image: warmer and more saturated, blacks and whites pushed to the ends of the range, and a light sharpen.
|
||||
chain=NaturalColors.fx|NaturalColors|Luma=1.15,Chroma=1.35;Levels.fx|Levels|InputBlack=0.02,InputWhite=0.98;Sharpen.fx|Sharpen|Amount=0.5
|
||||
Vendored
-70
@@ -1,70 +0,0 @@
|
||||
// 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(float(id & 2), float((id & 1) << 1));
|
||||
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);
|
||||
[unroll] for (int x = -3; x <= 3; x += 2)
|
||||
{
|
||||
[unroll] 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 <
|
||||
ui_label = "Bloom";
|
||||
ui_tooltip = "Bleeds light out of the brightest areas into a soft halo, the way a camera does when pointed at something too bright.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Bloom;
|
||||
}
|
||||
}
|
||||
Vendored
-87
@@ -1,87 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Radius <
|
||||
ui_type = "slider";
|
||||
ui_label = "Radius";
|
||||
ui_tooltip = "How far the blur reaches, in pixels.";
|
||||
ui_min = 1.0; ui_max = 12.0; ui_step = 0.5;
|
||||
> = 4.0;
|
||||
|
||||
uniform float Strength <
|
||||
ui_type = "slider";
|
||||
ui_label = "Strength";
|
||||
ui_tooltip = "Blend between the original image and the blurred one.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 1.0;
|
||||
|
||||
uniform float FocusSize <
|
||||
ui_type = "slider";
|
||||
ui_label = "Sharp Centre";
|
||||
ui_tooltip = "Size of the region left in focus at the centre of the screen. At zero the whole image is blurred evenly.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 0.0;
|
||||
|
||||
uniform float FocusSoftness <
|
||||
ui_type = "slider";
|
||||
ui_label = "Focus Falloff";
|
||||
ui_tooltip = "How gradually the sharp centre gives way to the blur.";
|
||||
ui_min = 0.05; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 0.4;
|
||||
|
||||
static const int TAPS = 17;
|
||||
|
||||
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_Blur(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
|
||||
float3 original = tex2D(BackBuffer, uv).rgb;
|
||||
|
||||
float2 centred = (uv - 0.5) * float2(BUFFER_WIDTH * BUFFER_RCP_HEIGHT, 1.0) * 2.0;
|
||||
float distance_from_centre = length(centred);
|
||||
float no_focus = 1.0 - step(0.001, FocusSize);
|
||||
float focus = max(smoothstep(FocusSize, FocusSize + FocusSoftness, distance_from_centre), no_focus);
|
||||
float amount = Strength * focus;
|
||||
|
||||
float angle = frac(sin(dot(pos.xy, float2(12.9898, 78.233))) * 43758.5453) * 6.2831853;
|
||||
float2 spoke = float2(cos(angle), sin(angle));
|
||||
|
||||
const float2 turn = float2(cos(2.39996323), sin(2.39996323));
|
||||
const float decay = exp(-2.0 / float(TAPS));
|
||||
float weight = exp(-1.0 / float(TAPS));
|
||||
|
||||
float3 sum = float3(0.0, 0.0, 0.0);
|
||||
float total = 0.0;
|
||||
[unroll] for (int i = 0; i < TAPS; ++i)
|
||||
{
|
||||
float reach = sqrt((float(i) + 0.5) / float(TAPS));
|
||||
sum += tex2D(BackBuffer, uv + spoke * reach * texel * Radius).rgb * weight;
|
||||
total += weight;
|
||||
weight *= decay;
|
||||
spoke = float2(spoke.x * turn.x - spoke.y * turn.y,
|
||||
spoke.x * turn.y + spoke.y * turn.x);
|
||||
}
|
||||
|
||||
float3 blurred = sum / total;
|
||||
return float4(lerp(original, blurred, amount), 1.0);
|
||||
}
|
||||
|
||||
technique Blur <
|
||||
ui_label = "Blur";
|
||||
ui_tooltip = "Gaussian blur with an optional sharp centre, for softening the picture or faking depth of field.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Blur;
|
||||
}
|
||||
}
|
||||
Vendored
-74
@@ -1,74 +0,0 @@
|
||||
// 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(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_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 <
|
||||
ui_label = "CRT";
|
||||
ui_tooltip = "Curved scanlines and the phosphor mask of a CRT television, for games that were drawn with that kind of screen in mind.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_CRT;
|
||||
}
|
||||
}
|
||||
Vendored
-79
@@ -1,79 +0,0 @@
|
||||
// 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(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_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 <
|
||||
ui_label = "Cartoon";
|
||||
ui_tooltip = "Ink outlines and flat colour bands. Faithful port of the PPSSPP shader; the outline gets heavy in dark scenes, so try Cartoon Soft or Cel Shading if the blacks smear.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Cartoon;
|
||||
}
|
||||
}
|
||||
Vendored
-100
@@ -1,100 +0,0 @@
|
||||
// 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
|
||||
|
||||
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.45;
|
||||
|
||||
uniform float ShadowGuard <
|
||||
ui_type = "slider";
|
||||
ui_label = "Shadow Guard";
|
||||
ui_tooltip = "Holds the outline back in dark areas. Raise it if shadows turn into black blobs.";
|
||||
ui_min = 0.1; ui_max = 2.0; ui_step = 0.05;
|
||||
> = 0.8;
|
||||
|
||||
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;
|
||||
> = 6.0;
|
||||
|
||||
uniform float Smoothing <
|
||||
ui_type = "slider";
|
||||
ui_label = "Banding";
|
||||
ui_tooltip = "How far the picture is pushed towards flat bands.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 0.75;
|
||||
|
||||
uniform float Saturation <
|
||||
ui_type = "slider";
|
||||
ui_label = "Saturation";
|
||||
ui_min = 0.0; ui_max = 2.0; ui_step = 0.05;
|
||||
> = 1.15;
|
||||
|
||||
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_CartoonSoft(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);
|
||||
const float3 luma_weights = float3(0.299, 0.587, 0.114);
|
||||
|
||||
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 luma = dot(c11, luma_weights);
|
||||
float response = (d1 + d2 + hl + vl) / (luma * 2.0 + ShadowGuard);
|
||||
float ink = 1.0 - saturate(response * EdgeStrength);
|
||||
|
||||
float scaled = luma * Levels;
|
||||
float step_position = frac(scaled);
|
||||
float eased = step_position * step_position * (3.0 - 2.0 * step_position);
|
||||
float banded = (floor(scaled) + eased) / Levels;
|
||||
float target = lerp(luma, banded, Smoothing);
|
||||
|
||||
float3 tinted = c11 * (target / max(luma, 0.001));
|
||||
float3 grey = dot(tinted, luma_weights);
|
||||
float3 color = lerp(grey, tinted, Saturation) * ink;
|
||||
|
||||
return float4(saturate(color), 1.0);
|
||||
}
|
||||
|
||||
technique CartoonSoft <
|
||||
ui_label = "Cartoon Soft";
|
||||
ui_tooltip = "Ink outlines and flat colour bands, with the outline held back in the shadows so dark scenes do not turn into black blobs.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_CartoonSoft;
|
||||
}
|
||||
}
|
||||
Vendored
-120
@@ -1,120 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Bands <
|
||||
ui_type = "slider";
|
||||
ui_label = "Shading Bands";
|
||||
ui_tooltip = "How many flat steps the lighting is collapsed into. Three or four gives the classic look.";
|
||||
ui_min = 2.0; ui_max = 8.0; ui_step = 1.0;
|
||||
> = 4.0;
|
||||
|
||||
uniform float BandContrast <
|
||||
ui_type = "slider";
|
||||
ui_label = "Band Contrast";
|
||||
ui_tooltip = "Spreads the bands towards pure black and white. Lower it if the shadows crush.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 0.5;
|
||||
|
||||
uniform float BandEdge <
|
||||
ui_type = "slider";
|
||||
ui_label = "Band Edge";
|
||||
ui_tooltip = "Width of the transition between bands. Low values give hard cel steps.";
|
||||
ui_min = 0.02; ui_max = 1.0; ui_step = 0.02;
|
||||
> = 0.15;
|
||||
|
||||
uniform float Detail <
|
||||
ui_type = "slider";
|
||||
ui_label = "Texture Detail";
|
||||
ui_tooltip = "How much surface texture survives the flattening. At zero the bands are completely flat.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 0.6;
|
||||
|
||||
uniform float OutlineStrength <
|
||||
ui_type = "slider";
|
||||
ui_label = "Outline Strength";
|
||||
ui_tooltip = "Opacity of the ink line drawn along detected edges.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 0.8;
|
||||
|
||||
uniform float OutlineThreshold <
|
||||
ui_type = "slider";
|
||||
ui_label = "Outline Threshold";
|
||||
ui_tooltip = "How strong an edge has to be before it is inked. Raise it to keep ink off textures and specular highlights.";
|
||||
ui_min = 0.01; ui_max = 0.4; ui_step = 0.01;
|
||||
> = 0.18;
|
||||
|
||||
uniform float Saturation <
|
||||
ui_type = "slider";
|
||||
ui_label = "Saturation";
|
||||
ui_tooltip = "Colour intensity of the flat regions.";
|
||||
ui_min = 0.0; ui_max = 2.0; ui_step = 0.05;
|
||||
> = 1.25;
|
||||
|
||||
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_CelShading(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
const float3 luma_weights = float3(0.2126, 0.7152, 0.0722);
|
||||
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
|
||||
|
||||
float3 centre = tex2D(BackBuffer, uv).rgb;
|
||||
float luma = dot(centre, luma_weights);
|
||||
|
||||
float2 near = texel * 1.5;
|
||||
float2 far = texel * 3.5;
|
||||
|
||||
float a00 = dot(tex2D(BackBuffer, uv + near * float2(-1.0, -1.0)).rgb, luma_weights);
|
||||
float a20 = dot(tex2D(BackBuffer, uv + near * float2( 1.0, -1.0)).rgb, luma_weights);
|
||||
float a02 = dot(tex2D(BackBuffer, uv + near * float2(-1.0, 1.0)).rgb, luma_weights);
|
||||
float a22 = dot(tex2D(BackBuffer, uv + near * float2( 1.0, 1.0)).rgb, luma_weights);
|
||||
|
||||
float b00 = dot(tex2D(BackBuffer, uv + far * float2(-1.0, -1.0)).rgb, luma_weights);
|
||||
float b20 = dot(tex2D(BackBuffer, uv + far * float2( 1.0, -1.0)).rgb, luma_weights);
|
||||
float b02 = dot(tex2D(BackBuffer, uv + far * float2(-1.0, 1.0)).rgb, luma_weights);
|
||||
float b22 = dot(tex2D(BackBuffer, uv + far * float2( 1.0, 1.0)).rgb, luma_weights);
|
||||
|
||||
float gx = (a00 + a02) - (a20 + a22);
|
||||
float gy = (a00 + a20) - (a02 + a22);
|
||||
float gradient = sqrt(gx * gx + gy * gy);
|
||||
float ink = smoothstep(OutlineThreshold, OutlineThreshold + 0.04, gradient);
|
||||
|
||||
float lighting = (a00 + a20 + a02 + a22 + b00 + b20 + b02 + b22) * 0.125;
|
||||
float detail = luma - lighting;
|
||||
|
||||
float softness = max(BandEdge, 0.001);
|
||||
float coord = lighting * Bands - softness * 0.5;
|
||||
float index = floor(coord) + smoothstep(1.0 - softness, 1.0, frac(coord));
|
||||
|
||||
float centred = (index + 0.5) / Bands;
|
||||
float stretched = index / max(Bands - 1.0, 1.0);
|
||||
float banded = saturate(lerp(centred, stretched, BandContrast));
|
||||
|
||||
float target = saturate(banded + detail * Detail);
|
||||
|
||||
float gain = min(target / max(luma, 0.001), 4.0);
|
||||
float3 shaded = centre * gain;
|
||||
float3 grey = dot(shaded, luma_weights);
|
||||
float3 color = lerp(grey, shaded, Saturation);
|
||||
color *= 1.0 - ink * OutlineStrength;
|
||||
|
||||
return float4(saturate(color), 1.0);
|
||||
}
|
||||
|
||||
technique CelShading <
|
||||
ui_label = "Cel Shading";
|
||||
ui_tooltip = "Collapses the lighting into a few hard steps and inks the edges, keeping texture detail and hue intact.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_CelShading;
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Strength <
|
||||
ui_type = "slider";
|
||||
ui_label = "Strength";
|
||||
ui_tooltip = "Channel separation in pixels, measured at the edge of the screen.";
|
||||
ui_min = 0.0; ui_max = 8.0; ui_step = 0.1;
|
||||
> = 1.5;
|
||||
|
||||
uniform float Falloff <
|
||||
ui_type = "slider";
|
||||
ui_label = "Falloff";
|
||||
ui_tooltip = "How fast the separation grows away from the centre. Higher keeps the middle clean.";
|
||||
ui_min = 1.0; ui_max = 4.0; ui_step = 0.1;
|
||||
> = 2.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_ChromaticAberration(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
|
||||
|
||||
float2 direction = uv - float2(0.5, 0.5);
|
||||
float radius = length(direction);
|
||||
float2 unit = direction / max(radius, 0.0001);
|
||||
float2 offset = unit * Strength * pow(radius * 2.0, Falloff) * texel;
|
||||
|
||||
float red = tex2D(BackBuffer, uv + offset).r;
|
||||
float green = tex2D(BackBuffer, uv).g;
|
||||
float blue = tex2D(BackBuffer, uv - offset).b;
|
||||
|
||||
return float4(red, green, blue, 1.0);
|
||||
}
|
||||
|
||||
technique ChromaticAberration <
|
||||
ui_label = "Chromatic Aberration";
|
||||
ui_tooltip = "Splits the colour channels apart towards the edges of the screen, the way a cheap lens fails to focus every colour on the same spot.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_ChromaticAberration;
|
||||
}
|
||||
}
|
||||
Vendored
-64
@@ -1,64 +0,0 @@
|
||||
// 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";
|
||||
ui_label = "Saturation";
|
||||
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
|
||||
> = 1.0;
|
||||
|
||||
uniform float Brightness <
|
||||
ui_type = "slider";
|
||||
ui_label = "Brightness";
|
||||
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
|
||||
> = 1.0;
|
||||
|
||||
uniform float Contrast <
|
||||
ui_type = "slider";
|
||||
ui_label = "Contrast";
|
||||
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
|
||||
> = 1.0;
|
||||
|
||||
uniform float Gamma <
|
||||
ui_type = "slider";
|
||||
ui_label = "Gamma";
|
||||
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
|
||||
> = 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_ColorGrade(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
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);
|
||||
rgb *= Brightness;
|
||||
rgb = (rgb - 0.5) * Contrast + 0.5;
|
||||
rgb = pow(max(rgb, 0.0), 1.0 / max(Gamma, 0.0001));
|
||||
|
||||
return float4(saturate(rgb), 1.0);
|
||||
}
|
||||
|
||||
technique ColorGrade <
|
||||
ui_label = "Colour Grade";
|
||||
ui_tooltip = "The four basic colour controls in one pass: saturation, brightness, contrast and gamma. Reach for this before anything more specialised.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_ColorGrade;
|
||||
}
|
||||
}
|
||||
Vendored
-91
@@ -1,91 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2015 Niklas Haas
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Threshold <
|
||||
ui_type = "slider";
|
||||
ui_label = "Threshold";
|
||||
ui_tooltip = "How flat a neighbourhood must be before it gets smoothed. Raise it to catch wider bands, lower it to keep more detail.";
|
||||
ui_min = 0.002; ui_max = 0.05; ui_step = 0.001;
|
||||
> = 0.012;
|
||||
|
||||
uniform float Radius <
|
||||
ui_type = "slider";
|
||||
ui_label = "Radius";
|
||||
ui_tooltip = "How far the sampling reaches, in pixels. Wider gradients need a larger radius.";
|
||||
ui_min = 1.0; ui_max = 32.0; ui_step = 1.0;
|
||||
> = 8.0;
|
||||
|
||||
uniform float Grain <
|
||||
ui_type = "slider";
|
||||
ui_label = "Dither";
|
||||
ui_tooltip = "Noise added to break up whatever banding survives the smoothing.";
|
||||
ui_min = 0.0; ui_max = 0.02; ui_step = 0.001;
|
||||
> = 0.004;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
float Hash(float2 p)
|
||||
{
|
||||
float3 scattered = frac(float3(p.x, p.y, p.x) * 0.1031);
|
||||
scattered += dot(scattered, scattered.yzx + 33.33);
|
||||
return frac((scattered.x + scattered.y) * scattered.z);
|
||||
}
|
||||
|
||||
float4 PS_Deband(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
|
||||
|
||||
float3 centre = tex2D(BackBuffer, uv).rgb;
|
||||
float base = Hash(pos.xy) * 6.2831853;
|
||||
|
||||
float3 total = float3(0.0, 0.0, 0.0);
|
||||
float3 deviation = float3(0.0, 0.0, 0.0);
|
||||
|
||||
[unroll] for (int ring = 1; ring <= 2; ++ring)
|
||||
{
|
||||
float angle = base + float(ring) * 2.3999632;
|
||||
float reach = Radius * float(ring) * 0.5;
|
||||
float2 along = float2(cos(angle), sin(angle)) * reach;
|
||||
float2 across = float2(-along.y, along.x);
|
||||
|
||||
float3 s0 = tex2D(BackBuffer, uv + along * texel).rgb;
|
||||
float3 s1 = tex2D(BackBuffer, uv - along * texel).rgb;
|
||||
float3 s2 = tex2D(BackBuffer, uv + across * texel).rgb;
|
||||
float3 s3 = tex2D(BackBuffer, uv - across * texel).rgb;
|
||||
|
||||
total += s0 + s1 + s2 + s3;
|
||||
deviation = max(deviation, max(max(abs(s0 - centre), abs(s1 - centre)),
|
||||
max(abs(s2 - centre), abs(s3 - centre))));
|
||||
}
|
||||
|
||||
float3 average = total * 0.125;
|
||||
float3 flatness = float3(1.0, 1.0, 1.0) -
|
||||
smoothstep(Threshold * 0.5, Threshold, deviation);
|
||||
float3 result = lerp(centre, average, flatness);
|
||||
|
||||
float dither = (Hash(pos.xy + float2(71.3, 41.7)) - 0.5) * Grain;
|
||||
|
||||
return float4(saturate(result + dither), 1.0);
|
||||
}
|
||||
|
||||
technique Deband <
|
||||
ui_label = "Deband";
|
||||
ui_tooltip = "Smooths the visible steps in gradients such as skies, then dithers whatever survives. Worth adding whenever large flat areas show rings.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Deband;
|
||||
}
|
||||
}
|
||||
Vendored
-83
@@ -1,83 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019-2021 bloc97
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Strength <
|
||||
ui_type = "slider";
|
||||
ui_label = "Strength";
|
||||
ui_min = 0.01; ui_max = 0.5; ui_step = 0.01;
|
||||
> = 0.1;
|
||||
|
||||
uniform float Radius <
|
||||
ui_type = "slider";
|
||||
ui_label = "Radius";
|
||||
ui_min = 0.3; ui_max = 2.0; ui_step = 0.05;
|
||||
> = 1.0;
|
||||
|
||||
uniform float Curve <
|
||||
ui_type = "slider";
|
||||
ui_label = "Shadow Bias";
|
||||
ui_min = 0.0; ui_max = 2.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(float(id & 2), float((id & 1) << 1));
|
||||
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
|
||||
}
|
||||
|
||||
float3 IntensityWeight(float3 value, float3 sigma, float3 centre)
|
||||
{
|
||||
float3 scaled = (value - centre) / sigma;
|
||||
return exp(-0.5 * scaled * scaled);
|
||||
}
|
||||
|
||||
float SpatialWeight(float distance, float sigma)
|
||||
{
|
||||
float scaled = distance / sigma;
|
||||
return exp(-0.5 * scaled * scaled);
|
||||
}
|
||||
|
||||
float4 PS_Denoise(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 intensity_sigma = max(pow(centre + 0.0001, Curve) * Strength, 0.0001);
|
||||
float spatial_sigma = max(Radius, 0.05);
|
||||
|
||||
float3 sum = float3(0.0, 0.0, 0.0);
|
||||
float3 total = float3(0.0, 0.0, 0.0);
|
||||
|
||||
[unroll] for (int y = -2; y <= 2; ++y)
|
||||
{
|
||||
[unroll] for (int x = -2; x <= 2; ++x)
|
||||
{
|
||||
float2 offset = float2(x, y);
|
||||
float3 tap = tex2D(BackBuffer, uv + offset * texel).rgb;
|
||||
float3 weight = IntensityWeight(tap, intensity_sigma, centre) *
|
||||
SpatialWeight(length(offset), spatial_sigma);
|
||||
sum += weight * tap;
|
||||
total += weight;
|
||||
}
|
||||
}
|
||||
|
||||
return float4(sum / total, 1.0);
|
||||
}
|
||||
|
||||
technique Denoise <
|
||||
ui_label = "Denoise";
|
||||
ui_tooltip = "Edge preserving blur that clears dithering and compression noise while leaving outlines sharp. Ported from Anime4K.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Denoise;
|
||||
}
|
||||
}
|
||||
Vendored
-65
@@ -1,65 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Intensity <
|
||||
ui_type = "slider";
|
||||
ui_label = "Intensity";
|
||||
ui_min = 0.0; ui_max = 0.2; ui_step = 0.005;
|
||||
> = 0.03;
|
||||
|
||||
uniform float Size <
|
||||
ui_type = "slider";
|
||||
ui_label = "Size";
|
||||
ui_tooltip = "Grain cell size in pixels.";
|
||||
ui_min = 1.0; ui_max = 4.0; ui_step = 1.0;
|
||||
> = 1.0;
|
||||
|
||||
uniform float Colored <
|
||||
ui_type = "slider";
|
||||
ui_label = "Colour";
|
||||
ui_tooltip = "Zero gives monochrome grain, one gives independent noise per channel.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
|
||||
> = 0.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);
|
||||
}
|
||||
|
||||
float Hash(float2 p)
|
||||
{
|
||||
float3 scattered = frac(float3(p.x, p.y, p.x) * 0.1031);
|
||||
scattered += dot(scattered, scattered.yzx + 33.33);
|
||||
return frac((scattered.x + scattered.y) * scattered.z);
|
||||
}
|
||||
|
||||
float4 PS_FilmGrain(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float3 rgb = tex2D(BackBuffer, uv).rgb;
|
||||
|
||||
float2 cell = floor(pos.xy / max(Size, 1.0));
|
||||
float mono = Hash(cell) - 0.5;
|
||||
float3 chroma = float3(Hash(cell + 11.7), Hash(cell + 23.1), Hash(cell + 37.5)) - 0.5;
|
||||
float3 noise = lerp(float3(mono, mono, mono), chroma, Colored);
|
||||
|
||||
float luma = dot(rgb, float3(0.2126, 0.7152, 0.0722));
|
||||
float response = 1.0 - abs(luma * 2.0 - 1.0);
|
||||
|
||||
return float4(saturate(rgb + noise * Intensity * response), 1.0);
|
||||
}
|
||||
|
||||
technique FilmGrain <
|
||||
ui_label = "Film Grain";
|
||||
ui_tooltip = "Adds photographic grain, strongest in the midtones and fading out in blacks and whites.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_FilmGrain;
|
||||
}
|
||||
}
|
||||
Vendored
-60
@@ -1,60 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Exposure <
|
||||
ui_type = "slider";
|
||||
ui_label = "Exposure";
|
||||
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
|
||||
> = 1.0;
|
||||
|
||||
uniform float Toe <
|
||||
ui_type = "slider";
|
||||
ui_label = "Toe";
|
||||
ui_tooltip = "Above 1.0 deepens the shadows, below 1.0 lifts them.";
|
||||
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
|
||||
> = 1.2;
|
||||
|
||||
uniform float Shoulder <
|
||||
ui_type = "slider";
|
||||
ui_label = "Shoulder";
|
||||
ui_tooltip = "Above 1.0 opens up the highlights, below 1.0 compresses them.";
|
||||
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
|
||||
> = 1.2;
|
||||
|
||||
uniform float Amount <
|
||||
ui_type = "slider";
|
||||
ui_label = "Amount";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
|
||||
> = 0.7;
|
||||
|
||||
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_FilmicCurve(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float3 rgb = tex2D(BackBuffer, uv).rgb;
|
||||
|
||||
float3 curved = saturate(rgb * Exposure);
|
||||
curved = pow(max(curved, 0.0), Toe);
|
||||
curved = 1.0 - pow(max(1.0 - curved, 0.0), Shoulder);
|
||||
|
||||
return float4(saturate(lerp(rgb, curved, Amount)), 1.0);
|
||||
}
|
||||
|
||||
technique FilmicCurve <
|
||||
ui_label = "Filmic Curve";
|
||||
ui_tooltip = "Filmic contrast curve. Deepens the shadows and opens the highlights without clipping either end.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_FilmicCurve;
|
||||
}
|
||||
}
|
||||
Vendored
-53
@@ -1,53 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Distortion <
|
||||
ui_type = "slider";
|
||||
ui_label = "Distortion";
|
||||
ui_tooltip = "Positive bulges the picture outwards, negative pinches it inwards.";
|
||||
ui_min = -0.5; ui_max = 0.5; ui_step = 0.01;
|
||||
> = 0.1;
|
||||
|
||||
uniform float Zoom <
|
||||
ui_type = "slider";
|
||||
ui_label = "Zoom";
|
||||
ui_tooltip = "Scales the picture to hide the edges the warp pulls in.";
|
||||
ui_min = 0.5; ui_max = 1.5; ui_step = 0.01;
|
||||
> = 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_LensDistortion(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float aspect = BUFFER_WIDTH * BUFFER_RCP_HEIGHT;
|
||||
float2 half_size = float2(aspect, 1.0);
|
||||
float2 unit = half_size / length(half_size);
|
||||
|
||||
float2 centred = (uv - 0.5) * 2.0 * unit;
|
||||
float r2 = dot(centred, centred);
|
||||
centred *= 1.0 + Distortion * r2;
|
||||
centred /= max(Zoom, 0.001);
|
||||
|
||||
float2 source = centred / (2.0 * unit) + 0.5;
|
||||
|
||||
return float4(tex2D(BackBuffer, source).rgb, 1.0);
|
||||
}
|
||||
|
||||
technique LensDistortion <
|
||||
ui_label = "Lens Distortion";
|
||||
ui_tooltip = "Barrel or pincushion warp, like looking through a wide angle lens.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_LensDistortion;
|
||||
}
|
||||
}
|
||||
Vendored
-66
@@ -1,66 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float InputBlack <
|
||||
ui_type = "slider";
|
||||
ui_label = "Input Black";
|
||||
ui_tooltip = "Input level mapped to black. Raise it to deepen washed out shadows.";
|
||||
ui_min = 0.0; ui_max = 0.5; ui_step = 0.005;
|
||||
> = 0.0;
|
||||
|
||||
uniform float InputWhite <
|
||||
ui_type = "slider";
|
||||
ui_label = "Input White";
|
||||
ui_min = 0.5; ui_max = 1.0; ui_step = 0.005;
|
||||
> = 1.0;
|
||||
|
||||
uniform float Gamma <
|
||||
ui_type = "slider";
|
||||
ui_label = "Gamma";
|
||||
ui_min = 0.2; ui_max = 3.0; ui_step = 0.01;
|
||||
> = 1.0;
|
||||
|
||||
uniform float OutputBlack <
|
||||
ui_type = "slider";
|
||||
ui_label = "Output Black";
|
||||
ui_tooltip = "Lifts crushed shadows so detail stops collapsing into one flat black.";
|
||||
ui_min = 0.0; ui_max = 0.5; ui_step = 0.005;
|
||||
> = 0.0;
|
||||
|
||||
uniform float OutputWhite <
|
||||
ui_type = "slider";
|
||||
ui_label = "Output White";
|
||||
ui_min = 0.5; ui_max = 1.0; ui_step = 0.005;
|
||||
> = 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_Levels(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float3 rgb = tex2D(BackBuffer, uv).rgb;
|
||||
|
||||
rgb = saturate((rgb - InputBlack) / max(InputWhite - InputBlack, 0.001));
|
||||
rgb = pow(max(rgb, 0.0), 1.0 / max(Gamma, 0.001));
|
||||
rgb = lerp(OutputBlack, OutputWhite, rgb);
|
||||
|
||||
return float4(saturate(rgb), 1.0);
|
||||
}
|
||||
|
||||
technique Levels <
|
||||
ui_label = "Levels";
|
||||
ui_tooltip = "Black point, white point, gamma and output range. Use it to fix crushed or washed out shadows.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Levels;
|
||||
}
|
||||
}
|
||||
Vendored
-88
@@ -1,88 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Length <
|
||||
ui_type = "slider";
|
||||
ui_label = "Length";
|
||||
ui_tooltip = "How far the streaks reach, as a percentage of screen height.";
|
||||
ui_min = 0.0; ui_max = 20.0; ui_step = 0.5;
|
||||
> = 5.0;
|
||||
|
||||
uniform float Zoom <
|
||||
ui_type = "slider";
|
||||
ui_label = "Zoom";
|
||||
ui_tooltip = "Streaks running out from the centre of the screen, as if rushing forward. The centre stays sharp.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 1.0;
|
||||
|
||||
uniform float Pan <
|
||||
ui_type = "slider";
|
||||
ui_label = "Pan";
|
||||
ui_tooltip = "Streaks running in one fixed direction, as if the camera were sweeping across.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 0.0;
|
||||
|
||||
uniform float Angle <
|
||||
ui_type = "slider";
|
||||
ui_label = "Pan Angle";
|
||||
ui_tooltip = "Direction of the sweep, in degrees.";
|
||||
ui_min = 0.0; ui_max = 360.0; ui_step = 5.0;
|
||||
> = 0.0;
|
||||
|
||||
uniform float Spin <
|
||||
ui_type = "slider";
|
||||
ui_label = "Spin";
|
||||
ui_tooltip = "Streaks curling around the centre, as if the camera were rolling. Negative turns the other way.";
|
||||
ui_min = -1.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 0.0;
|
||||
|
||||
static const int TAPS = 24;
|
||||
|
||||
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_MotionBlur(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float aspect = BUFFER_WIDTH * BUFFER_RCP_HEIGHT;
|
||||
float2 to_screen = float2(aspect, 1.0);
|
||||
|
||||
float2 centred = (uv - 0.5) * to_screen * 2.0;
|
||||
float2 outward = centred;
|
||||
float2 around = float2(-centred.y, centred.x);
|
||||
|
||||
float radians_angle = Angle * 0.01745329;
|
||||
float2 sweep = float2(cos(radians_angle), sin(radians_angle));
|
||||
|
||||
float2 velocity = sweep * Pan + outward * Zoom + around * Spin;
|
||||
velocity *= Length * 0.01;
|
||||
velocity /= to_screen;
|
||||
|
||||
float jitter = frac(sin(dot(pos.xy, float2(12.9898, 78.233))) * 43758.5453);
|
||||
|
||||
float3 sum = float3(0.0, 0.0, 0.0);
|
||||
[unroll] for (int i = 0; i < TAPS; ++i)
|
||||
{
|
||||
float t = (float(i) + jitter) / float(TAPS) - 0.5;
|
||||
sum += tex2D(BackBuffer, uv + velocity * t).rgb;
|
||||
}
|
||||
|
||||
return float4(sum / float(TAPS), 1.0);
|
||||
}
|
||||
|
||||
technique MotionBlur <
|
||||
ui_label = "Motion Blur";
|
||||
ui_tooltip = "Camera style motion blur: streaks the picture outwards, sideways or around, without touching still detail at the centre.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_MotionBlur;
|
||||
}
|
||||
}
|
||||
Vendored
-60
@@ -1,60 +0,0 @@
|
||||
// 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(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_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 <
|
||||
ui_label = "Natural Colours";
|
||||
ui_tooltip = "Warms the picture and lifts saturation for a less washed out look. Ported from the PPSSPP shader.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Natural;
|
||||
}
|
||||
}
|
||||
Vendored
-90
@@ -1,90 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Timer < source = "timer"; > = 0.0;
|
||||
|
||||
uniform float Horizon <
|
||||
ui_type = "slider";
|
||||
ui_label = "Reflection Line";
|
||||
ui_tooltip = "Height on screen where the reflective floor begins. Everything above it is left untouched.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
|
||||
> = 0.55;
|
||||
|
||||
uniform float Amount <
|
||||
ui_type = "slider";
|
||||
ui_label = "Amount";
|
||||
ui_tooltip = "How strongly the reflection shows through the floor.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 0.35;
|
||||
|
||||
uniform float Falloff <
|
||||
ui_type = "slider";
|
||||
ui_label = "Falloff";
|
||||
ui_tooltip = "How quickly the reflection fades as the floor comes towards the viewer. At zero it stays even.";
|
||||
ui_min = 0.0; ui_max = 4.0; ui_step = 0.1;
|
||||
> = 1.2;
|
||||
|
||||
uniform float Perspective <
|
||||
ui_type = "slider";
|
||||
ui_label = "Perspective";
|
||||
ui_tooltip = "Stretches or squashes the mirrored image. One is a true mirror, higher values pull it towards the line.";
|
||||
ui_min = 0.2; ui_max = 2.0; ui_step = 0.05;
|
||||
> = 1.0;
|
||||
|
||||
uniform float Ripple <
|
||||
ui_type = "slider";
|
||||
ui_label = "Ripple";
|
||||
ui_tooltip = "Amplitude of the waves disturbing the reflection. At zero the mirror is perfectly still.";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
|
||||
> = 0.0;
|
||||
|
||||
uniform float RippleSpeed <
|
||||
ui_type = "slider";
|
||||
ui_label = "Ripple Speed";
|
||||
ui_tooltip = "How fast the waves travel.";
|
||||
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(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_Reflections(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float3 color = tex2D(BackBuffer, uv).rgb;
|
||||
|
||||
float depth = uv.y - Horizon;
|
||||
float on_floor = step(0.0, depth);
|
||||
float span = max(1.0 - Horizon, 0.001);
|
||||
float distance_down = saturate(depth / span);
|
||||
|
||||
float seconds = Timer * 0.001;
|
||||
float wave = sin(uv.x * 38.0 + seconds * RippleSpeed * 2.0) *
|
||||
sin(uv.y * 21.0 - seconds * RippleSpeed * 1.3);
|
||||
float2 disturbance = float2(wave * 0.004, wave * 0.002) * Ripple * distance_down;
|
||||
|
||||
float2 mirrored = float2(uv.x, Horizon - depth * Perspective) + disturbance;
|
||||
float3 reflection = tex2D(BackBuffer, saturate(mirrored)).rgb;
|
||||
|
||||
float fade = pow(max(1.0 - distance_down, 0.0001), Falloff);
|
||||
float strength = Amount * fade * on_floor;
|
||||
|
||||
return float4(lerp(color, reflection, strength), 1.0);
|
||||
}
|
||||
|
||||
technique Reflections <
|
||||
ui_label = "Reflections";
|
||||
ui_tooltip = "Mirrors the picture into a reflective floor below a line you choose, with optional water ripples.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Reflections;
|
||||
}
|
||||
}
|
||||
Vendored
-66
@@ -1,66 +0,0 @@
|
||||
// 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(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_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 <
|
||||
ui_label = "Scanlines";
|
||||
ui_tooltip = "Darkens alternating rows of pixels to imitate the horizontal scanlines of a CRT. Cheaper than the full CRT effect when you only want the lines.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Scanlines;
|
||||
}
|
||||
}
|
||||
Vendored
-43
@@ -1,43 +0,0 @@
|
||||
// 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(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_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 <
|
||||
ui_label = "Sharpen";
|
||||
ui_tooltip = "Unsharp mask that brings back edge detail lost to scaling. Most useful after upscaling a low internal resolution.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Sharpen;
|
||||
}
|
||||
}
|
||||
Vendored
-75
@@ -1,75 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float ShadowHue <
|
||||
ui_type = "slider";
|
||||
ui_label = "Shadow Hue";
|
||||
ui_min = 0.0; ui_max = 360.0; ui_step = 1.0;
|
||||
> = 210.0;
|
||||
|
||||
uniform float ShadowStrength <
|
||||
ui_type = "slider";
|
||||
ui_label = "Shadow Strength";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
|
||||
> = 0.0;
|
||||
|
||||
uniform float HighlightHue <
|
||||
ui_type = "slider";
|
||||
ui_label = "Highlight Hue";
|
||||
ui_min = 0.0; ui_max = 360.0; ui_step = 1.0;
|
||||
> = 45.0;
|
||||
|
||||
uniform float HighlightStrength <
|
||||
ui_type = "slider";
|
||||
ui_label = "Highlight Strength";
|
||||
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
|
||||
> = 0.0;
|
||||
|
||||
uniform float Balance <
|
||||
ui_type = "slider";
|
||||
ui_label = "Balance";
|
||||
ui_tooltip = "Moves the split between what counts as shadow and what counts as highlight.";
|
||||
ui_min = -0.5; ui_max = 0.5; ui_step = 0.01;
|
||||
> = 0.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);
|
||||
}
|
||||
|
||||
float3 HueToRGB(float hue)
|
||||
{
|
||||
float h = frac(hue / 360.0) * 6.0;
|
||||
return saturate(float3(abs(h - 3.0) - 1.0, 2.0 - abs(h - 2.0), 2.0 - abs(h - 4.0)));
|
||||
}
|
||||
|
||||
float4 PS_SplitToning(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float3 rgb = tex2D(BackBuffer, uv).rgb;
|
||||
|
||||
float luma = saturate(dot(rgb, float3(0.2126, 0.7152, 0.0722)) + Balance);
|
||||
|
||||
float3 shadow_tint = HueToRGB(ShadowHue) - 0.5;
|
||||
float3 highlight_tint = HueToRGB(HighlightHue) - 0.5;
|
||||
|
||||
rgb += shadow_tint * ShadowStrength * 0.25 * (1.0 - luma);
|
||||
rgb += highlight_tint * HighlightStrength * 0.25 * luma;
|
||||
|
||||
return float4(saturate(rgb), 1.0);
|
||||
}
|
||||
|
||||
technique SplitToning <
|
||||
ui_label = "Split Toning";
|
||||
ui_tooltip = "Tints the shadows and the highlights towards two different hues.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_SplitToning;
|
||||
}
|
||||
}
|
||||
Vendored
-52
@@ -1,52 +0,0 @@
|
||||
// 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";
|
||||
ui_label = "Strength";
|
||||
ui_tooltip = "How dark the corners become.";
|
||||
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
|
||||
> = 0.6;
|
||||
|
||||
uniform float Aspect <
|
||||
ui_type = "slider";
|
||||
ui_label = "Aspect";
|
||||
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
|
||||
> = 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_Vignette(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float2 diff = uv - 0.5;
|
||||
diff.x *= Aspect;
|
||||
diff.y /= max(Aspect, 0.0001);
|
||||
|
||||
float falloff = 1.0 - min(1.0, Strength * dot(diff, diff) * 2.0);
|
||||
float3 rgb = tex2D(BackBuffer, uv).rgb;
|
||||
|
||||
return float4(rgb * falloff, 1.0);
|
||||
}
|
||||
|
||||
technique Vignette <
|
||||
ui_label = "Vignette";
|
||||
ui_tooltip = "Darkens the corners of the screen to pull the eye towards the middle, the way a camera lens falls off at its edges.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_Vignette;
|
||||
}
|
||||
}
|
||||
Vendored
-76
@@ -1,76 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
texture BackBufferTex : COLOR;
|
||||
sampler BackBuffer { Texture = BackBufferTex; };
|
||||
|
||||
uniform float Temperature <
|
||||
ui_type = "slider";
|
||||
ui_label = "Temperature";
|
||||
ui_tooltip = "Negative cools the picture towards blue, positive warms it towards orange.";
|
||||
ui_min = -100.0; ui_max = 100.0; ui_step = 1.0;
|
||||
> = 0.0;
|
||||
|
||||
uniform float Tint <
|
||||
ui_type = "slider";
|
||||
ui_label = "Tint";
|
||||
ui_tooltip = "Negative shifts towards green, positive towards magenta.";
|
||||
ui_min = -100.0; ui_max = 100.0; ui_step = 1.0;
|
||||
> = 0.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);
|
||||
}
|
||||
|
||||
float3 WhitePointLMS(float t1, float t2)
|
||||
{
|
||||
float shift = 0.05;
|
||||
if (t1 < 0.0)
|
||||
{
|
||||
shift = 0.10;
|
||||
}
|
||||
float x = 0.31271 - t1 * shift;
|
||||
float y = 2.87 * x - 3.0 * x * x - 0.27509507 + t2 * 0.05;
|
||||
|
||||
float big_y = 1.0;
|
||||
float big_x = big_y * x / y;
|
||||
float big_z = big_y * (1.0 - x - y) / y;
|
||||
|
||||
return float3( 0.7328 * big_x + 0.4296 * big_y - 0.1624 * big_z,
|
||||
-0.7036 * big_x + 1.6975 * big_y + 0.0061 * big_z,
|
||||
0.0030 * big_x + 0.0136 * big_y + 0.9834 * big_z);
|
||||
}
|
||||
|
||||
float4 PS_WhiteBalance(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
|
||||
{
|
||||
float3 rgb = pow(max(tex2D(BackBuffer, uv).rgb, 0.0), 2.2);
|
||||
|
||||
float3 balance = float3(0.949237, 1.03542, 1.08728) /
|
||||
WhitePointLMS(Temperature / 65.0, Tint / 65.0);
|
||||
|
||||
const float3x3 rgb_to_lms = float3x3(0.390405, 0.549941, 0.008926,
|
||||
0.070841, 0.963172, 0.001358,
|
||||
0.023108, 0.128021, 0.936245);
|
||||
const float3x3 lms_to_rgb = float3x3( 2.858470, -1.628790, -0.024891,
|
||||
-0.210182, 1.158200, 0.000324,
|
||||
-0.041812, -0.118169, 1.068670);
|
||||
|
||||
float3 lms = mul(rgb_to_lms, rgb) * balance;
|
||||
rgb = mul(lms_to_rgb, lms);
|
||||
|
||||
return float4(saturate(pow(max(rgb, 0.0), 1.0 / 2.2)), 1.0);
|
||||
}
|
||||
|
||||
technique WhiteBalance <
|
||||
ui_label = "White Balance";
|
||||
ui_tooltip = "Corrects a picture that looks too cool, too warm or tinted.";
|
||||
>
|
||||
{
|
||||
pass
|
||||
{
|
||||
VertexShader = VS_PostProcess;
|
||||
PixelShader = PS_WhiteBalance;
|
||||
}
|
||||
}
|
||||
Vendored
-44
@@ -195,50 +195,6 @@ else()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# reshadefx
|
||||
if (ENABLE_RESHADE)
|
||||
AddJsonPackage(NAME reshade DOWNLOAD_ONLY)
|
||||
|
||||
set(RESHADEFX_SHIM_DIR ${CMAKE_CURRENT_BINARY_DIR}/reshadefx_shim)
|
||||
file(WRITE ${RESHADEFX_SHIM_DIR}/spirv.hpp "#include <spirv/unified1/spirv.hpp>\n")
|
||||
file(WRITE ${RESHADEFX_SHIM_DIR}/GLSL.std.450.h "#include <spirv/unified1/GLSL.std.450.h>\n")
|
||||
if (APPLE)
|
||||
file(WRITE ${RESHADEFX_SHIM_DIR}/malloc.h "#include <stdlib.h>\n#include <alloca.h>\n")
|
||||
endif()
|
||||
|
||||
add_library(reshadefx STATIC
|
||||
${reshade_SOURCE_DIR}/source/effect_codegen_spirv.cpp
|
||||
${reshade_SOURCE_DIR}/source/effect_expression.cpp
|
||||
${reshade_SOURCE_DIR}/source/effect_lexer.cpp
|
||||
${reshade_SOURCE_DIR}/source/effect_parser_exp.cpp
|
||||
${reshade_SOURCE_DIR}/source/effect_parser_stmt.cpp
|
||||
${reshade_SOURCE_DIR}/source/effect_preprocessor.cpp
|
||||
${reshade_SOURCE_DIR}/source/effect_symbol_table.cpp
|
||||
)
|
||||
|
||||
target_include_directories(reshadefx SYSTEM PUBLIC ${reshade_SOURCE_DIR}/source)
|
||||
target_include_directories(reshadefx PRIVATE ${RESHADEFX_SHIM_DIR})
|
||||
target_link_libraries(reshadefx PUBLIC SPIRV-Headers::SPIRV-Headers)
|
||||
|
||||
if (NOT MSVC)
|
||||
target_compile_options(reshadefx PRIVATE -w -fno-char8_t)
|
||||
else()
|
||||
target_compile_options(reshadefx PRIVATE /w /Zc:char8_t-)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (NOT MSVC)
|
||||
target_compile_options(reshadefx PRIVATE -include share.h)
|
||||
else()
|
||||
target_compile_options(reshadefx PRIVATE /FIshare.h)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT TARGET reshadefx::reshadefx)
|
||||
add_library(reshadefx::reshadefx ALIAS reshadefx)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Catch2
|
||||
if (YUZU_TESTS OR DYNARMIC_TESTS)
|
||||
AddJsonPackage(catch2)
|
||||
|
||||
@@ -3,13 +3,10 @@
|
||||
|
||||
package org.yuzu.yuzu_emu.dialogs
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Rect
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RadioGroup
|
||||
import android.widget.TextView
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
@@ -21,28 +18,11 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
import org.yuzu.yuzu_emu.fragments.EmulationFragment
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
import org.yuzu.yuzu_emu.utils.NativePostProcessing
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
|
||||
|
||||
class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
private val expandedShaders = mutableSetOf<Int>()
|
||||
|
||||
private fun forgetShaderSlot(index: Int) {
|
||||
val shifted = mutableSetOf<Int>()
|
||||
for (slot in expandedShaders) {
|
||||
if (slot < index) {
|
||||
shifted.add(slot)
|
||||
}
|
||||
if (slot > index) {
|
||||
shifted.add(slot - 1)
|
||||
}
|
||||
}
|
||||
expandedShaders.clear()
|
||||
expandedShaders.addAll(shifted)
|
||||
}
|
||||
|
||||
private fun saveSettings() {
|
||||
if (emulationFragment.shouldUseCustom) {
|
||||
NativeConfig.savePerGameConfig()
|
||||
@@ -252,589 +232,6 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
||||
container.addView(itemView)
|
||||
}
|
||||
|
||||
fun addChoice(
|
||||
title: String,
|
||||
container: ViewGroup,
|
||||
choices: List<String>,
|
||||
selectedIndex: Int,
|
||||
onSelected: (Int) -> Unit
|
||||
) {
|
||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
|
||||
val headerView = itemView.findViewById<ViewGroup>(R.id.setting_header)
|
||||
val titleView = itemView.findViewById<TextView>(R.id.setting_title)
|
||||
val valueView = itemView.findViewById<TextView>(R.id.setting_value)
|
||||
val expandIcon = itemView.findViewById<android.widget.ImageView>(R.id.expand_icon)
|
||||
val radioGroup = itemView.findViewById<RadioGroup>(R.id.radio_group)
|
||||
|
||||
titleView.text = title
|
||||
|
||||
var current = ""
|
||||
if (selectedIndex in choices.indices) {
|
||||
current = choices[selectedIndex]
|
||||
}
|
||||
valueView.text = current
|
||||
headerView.visibility = View.VISIBLE
|
||||
|
||||
var isExpanded = false
|
||||
choices.forEachIndexed { index, name ->
|
||||
val radioButton = com.google.android.material.radiobutton.MaterialRadioButton(
|
||||
emulationFragment.requireContext()
|
||||
)
|
||||
radioButton.text = name
|
||||
radioButton.id = View.generateViewId()
|
||||
radioButton.isChecked = index == selectedIndex
|
||||
radioButton.setPadding(16, 8, 16, 8)
|
||||
|
||||
radioButton.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (isChecked) {
|
||||
valueView.text = name
|
||||
onSelected(index)
|
||||
}
|
||||
}
|
||||
radioGroup.addView(radioButton)
|
||||
}
|
||||
|
||||
headerView.setOnClickListener {
|
||||
isExpanded = !isExpanded
|
||||
if (isExpanded) {
|
||||
radioGroup.visibility = View.VISIBLE
|
||||
expandIcon.animate().rotation(180f).setDuration(200).start()
|
||||
} else {
|
||||
radioGroup.visibility = View.GONE
|
||||
expandIcon.animate().rotation(0f).setDuration(200).start()
|
||||
}
|
||||
}
|
||||
|
||||
container.addView(itemView)
|
||||
}
|
||||
|
||||
fun addStepSlider(
|
||||
title: String,
|
||||
container: ViewGroup,
|
||||
steps: Int,
|
||||
selectedStep: Int,
|
||||
describe: (Int) -> String,
|
||||
onCommitted: (Int) -> Unit,
|
||||
onChanged: (Int) -> Unit
|
||||
) {
|
||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
|
||||
|
||||
val sliderContainer = itemView.findViewById<ViewGroup>(R.id.slider_container)
|
||||
val titleView = itemView.findViewById<TextView>(R.id.slider_title)
|
||||
val valueDisplay = itemView.findViewById<TextView>(R.id.slider_value_display)
|
||||
val slider = itemView.findViewById<com.google.android.material.slider.Slider>(
|
||||
R.id.setting_slider
|
||||
)
|
||||
|
||||
titleView.text = title
|
||||
sliderContainer.visibility = View.VISIBLE
|
||||
|
||||
slider.valueFrom = 0f
|
||||
slider.valueTo = steps.toFloat()
|
||||
slider.stepSize = 1f
|
||||
slider.value = selectedStep.toFloat().coerceIn(0f, steps.toFloat())
|
||||
valueDisplay.text = describe(slider.value.toInt())
|
||||
|
||||
slider.addOnChangeListener { _, value, fromUser ->
|
||||
if (fromUser) {
|
||||
val step = value.toInt()
|
||||
onChanged(step)
|
||||
valueDisplay.text = describe(step)
|
||||
}
|
||||
}
|
||||
|
||||
var pressedValue = slider.value
|
||||
|
||||
slider.setOnTouchListener { _, event ->
|
||||
val drawer = emulationFragment.view?.findViewById<DrawerLayout>(R.id.drawer_layout)
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
drawer?.requestDisallowInterceptTouchEvent(true)
|
||||
pressedValue = slider.value
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
drawer?.requestDisallowInterceptTouchEvent(false)
|
||||
if (slider.value != pressedValue) {
|
||||
onCommitted(slider.value.toInt())
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
container.addView(itemView)
|
||||
}
|
||||
|
||||
fun addShaderCard(
|
||||
index: Int,
|
||||
title: String,
|
||||
summary: String,
|
||||
container: ViewGroup,
|
||||
onRemove: () -> Unit
|
||||
): ViewGroup {
|
||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||
val itemView = inflater.inflate(R.layout.item_quick_settings_shader, container, false)
|
||||
|
||||
val headerView = itemView.findViewById<ViewGroup>(R.id.shader_header)
|
||||
val titleView = itemView.findViewById<TextView>(R.id.shader_title)
|
||||
val summaryView = itemView.findViewById<TextView>(R.id.shader_summary)
|
||||
val removeView = itemView.findViewById<android.widget.ImageView>(R.id.shader_remove)
|
||||
val expandIcon = itemView.findViewById<android.widget.ImageView>(R.id.shader_expand)
|
||||
val bodyView = itemView.findViewById<ViewGroup>(R.id.shader_body)
|
||||
|
||||
titleView.text = title
|
||||
if (summary.isEmpty()) {
|
||||
summaryView.visibility = View.GONE
|
||||
} else {
|
||||
summaryView.text = summary
|
||||
}
|
||||
|
||||
var isExpanded = expandedShaders.contains(index)
|
||||
if (isExpanded) {
|
||||
bodyView.visibility = View.VISIBLE
|
||||
expandIcon.rotation = 180f
|
||||
}
|
||||
|
||||
headerView.setOnClickListener {
|
||||
isExpanded = !isExpanded
|
||||
if (isExpanded) {
|
||||
expandedShaders.add(index)
|
||||
bodyView.visibility = View.VISIBLE
|
||||
expandIcon.animate().rotation(180f).setDuration(200).start()
|
||||
} else {
|
||||
expandedShaders.remove(index)
|
||||
bodyView.visibility = View.GONE
|
||||
expandIcon.animate().rotation(0f).setDuration(200).start()
|
||||
}
|
||||
}
|
||||
|
||||
removeView.setOnClickListener {
|
||||
onRemove()
|
||||
}
|
||||
|
||||
container.addView(itemView)
|
||||
return bodyView
|
||||
}
|
||||
|
||||
fun addEffectPicker(
|
||||
container: ViewGroup,
|
||||
choices: List<String>,
|
||||
hasEffects: Boolean,
|
||||
onRemoveAll: () -> Unit,
|
||||
onPicked: (Int) -> Unit
|
||||
) {
|
||||
val context = emulationFragment.requireContext()
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val itemView = inflater.inflate(R.layout.item_quick_settings_add, container, false)
|
||||
|
||||
val button = itemView.findViewById<com.google.android.material.button.MaterialButton>(
|
||||
R.id.add_button
|
||||
)
|
||||
val removeButton =
|
||||
itemView.findViewById<com.google.android.material.button.MaterialButton>(
|
||||
R.id.remove_all_button
|
||||
)
|
||||
val choiceGroup = itemView.findViewById<RadioGroup>(R.id.add_choices)
|
||||
|
||||
choices.forEachIndexed { index, name ->
|
||||
val radioButton = com.google.android.material.radiobutton.MaterialRadioButton(context)
|
||||
radioButton.text = name
|
||||
radioButton.id = View.generateViewId()
|
||||
radioButton.setPadding(16, 8, 16, 8)
|
||||
radioButton.setOnCheckedChangeListener { _, isChecked ->
|
||||
if (isChecked) {
|
||||
onPicked(index)
|
||||
}
|
||||
}
|
||||
choiceGroup.addView(radioButton)
|
||||
}
|
||||
|
||||
var closedLabel = R.string.post_processing_add
|
||||
if (hasEffects) {
|
||||
closedLabel = R.string.post_processing_open_list
|
||||
removeButton.visibility = View.VISIBLE
|
||||
}
|
||||
button.setText(closedLabel)
|
||||
|
||||
val removeBackground = MaterialColors.getColor(
|
||||
removeButton,
|
||||
com.google.android.material.R.attr.colorErrorContainer
|
||||
)
|
||||
val removeForeground = MaterialColors.getColor(
|
||||
removeButton,
|
||||
com.google.android.material.R.attr.colorOnErrorContainer
|
||||
)
|
||||
removeButton.backgroundTintList = ColorStateList.valueOf(removeBackground)
|
||||
removeButton.setTextColor(removeForeground)
|
||||
removeButton.iconTint = ColorStateList.valueOf(removeForeground)
|
||||
removeButton.setOnClickListener {
|
||||
onRemoveAll()
|
||||
}
|
||||
|
||||
val slide = context.resources.displayMetrics.density * 24.0f
|
||||
|
||||
var isOpen = false
|
||||
button.setOnClickListener {
|
||||
isOpen = !isOpen
|
||||
if (isOpen) {
|
||||
choiceGroup.alpha = 0.0f
|
||||
choiceGroup.translationY = slide
|
||||
choiceGroup.visibility = View.VISIBLE
|
||||
choiceGroup.animate()
|
||||
.alpha(1.0f)
|
||||
.translationY(0.0f)
|
||||
.setDuration(220)
|
||||
.withEndAction {
|
||||
choiceGroup.requestRectangleOnScreen(
|
||||
Rect(0, 0, choiceGroup.width, choiceGroup.height),
|
||||
false
|
||||
)
|
||||
}
|
||||
.start()
|
||||
|
||||
button.setText(R.string.post_processing_close_list)
|
||||
button.setIconResource(R.drawable.ic_clear)
|
||||
} else {
|
||||
choiceGroup.animate()
|
||||
.alpha(0.0f)
|
||||
.translationY(slide)
|
||||
.setDuration(160)
|
||||
.withEndAction {
|
||||
choiceGroup.visibility = View.GONE
|
||||
}
|
||||
.start()
|
||||
|
||||
button.setText(closedLabel)
|
||||
button.setIconResource(R.drawable.ic_add)
|
||||
}
|
||||
}
|
||||
|
||||
container.addView(itemView)
|
||||
}
|
||||
|
||||
fun addPresetBand(container: ViewGroup, name: String, summary: String) {
|
||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||
val itemView = inflater.inflate(R.layout.item_quick_settings_preset, container, false)
|
||||
|
||||
val titleView = itemView.findViewById<TextView>(R.id.preset_title)
|
||||
val summaryView = itemView.findViewById<TextView>(R.id.preset_summary)
|
||||
val switchView = itemView.findViewById<MaterialSwitch>(R.id.preset_switch)
|
||||
|
||||
titleView.text = name
|
||||
if (summary.isEmpty()) {
|
||||
summaryView.visibility = View.GONE
|
||||
} else {
|
||||
summaryView.text = summary
|
||||
}
|
||||
|
||||
switchView.isChecked = NativePostProcessing.isEnabled()
|
||||
switchView.setOnCheckedChangeListener { _, checked ->
|
||||
emulationFragment.editPostProcessing {
|
||||
NativePostProcessing.setEnabled(checked)
|
||||
}
|
||||
}
|
||||
|
||||
container.addView(itemView)
|
||||
}
|
||||
|
||||
fun addPostProcessing(container: ViewGroup, onStructureChanged: () -> Unit) {
|
||||
val usable = NativePostProcessing.catalog().filter { it.valid }
|
||||
if (usable.isEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
val preset = NativePostProcessing.getActivePreset()
|
||||
if (preset.isNotEmpty()) {
|
||||
addDivider(container)
|
||||
|
||||
var summary = ""
|
||||
val described = NativePostProcessing.presets().firstOrNull { it.name == preset }
|
||||
if (described != null) {
|
||||
summary = described.description
|
||||
}
|
||||
if (summary.isEmpty()) {
|
||||
summary =
|
||||
YuzuApplication.appContext.getString(R.string.post_processing_preset_locked)
|
||||
}
|
||||
if (NativePostProcessing.isPresetModified()) {
|
||||
summary = summary + "\n" +
|
||||
YuzuApplication.appContext.getString(R.string.post_processing_preset_modified)
|
||||
}
|
||||
|
||||
addPresetBand(container, preset, summary)
|
||||
return
|
||||
}
|
||||
|
||||
val labels = mutableListOf<String>()
|
||||
val files = mutableListOf<String>()
|
||||
val techniques = mutableListOf<String>()
|
||||
|
||||
for (effect in usable) {
|
||||
for (technique in effect.techniques) {
|
||||
var label = effect.label
|
||||
if (effect.techniques.size > 1) {
|
||||
label = effect.label + " \u00b7 " + technique
|
||||
}
|
||||
labels.add(label)
|
||||
files.add(effect.file)
|
||||
techniques.add(technique)
|
||||
}
|
||||
}
|
||||
|
||||
addDivider(container)
|
||||
|
||||
val chain = NativePostProcessing.chain()
|
||||
chain.forEachIndexed { index, entry ->
|
||||
val effect = usable.firstOrNull { it.file == entry.file }
|
||||
|
||||
var title = entry.file
|
||||
var summary = ""
|
||||
if (effect != null) {
|
||||
title = effect.label
|
||||
if (effect.techniques.size > 1) {
|
||||
title = effect.label + " \u00b7 " + entry.technique
|
||||
}
|
||||
summary = effect.description
|
||||
}
|
||||
|
||||
val body = addShaderCard(index, title, summary, container) {
|
||||
emulationFragment.editPostProcessing {
|
||||
NativePostProcessing.remove(index)
|
||||
}
|
||||
forgetShaderSlot(index)
|
||||
onStructureChanged()
|
||||
}
|
||||
|
||||
if (effect != null) {
|
||||
for (uniform in effect.uniforms) {
|
||||
addUniformSliders(body, index, uniform)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addEffectPicker(
|
||||
container,
|
||||
labels,
|
||||
chain.isNotEmpty(),
|
||||
{
|
||||
emulationFragment.editPostProcessing {
|
||||
NativePostProcessing.clearChain()
|
||||
}
|
||||
expandedShaders.clear()
|
||||
onStructureChanged()
|
||||
}
|
||||
) { picked ->
|
||||
emulationFragment.editPostProcessing {
|
||||
NativePostProcessing.append(files[picked], techniques[picked])
|
||||
}
|
||||
onStructureChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addUniformSliders(
|
||||
container: ViewGroup,
|
||||
index: Int,
|
||||
uniform: NativePostProcessing.Uniform
|
||||
) {
|
||||
if (uniform.uiType == NativePostProcessing.UI_HIDDEN) {
|
||||
return
|
||||
}
|
||||
|
||||
for (component in 0 until uniform.components) {
|
||||
var title = uniform.label
|
||||
if (uniform.components > 1) {
|
||||
title = uniform.label + " [" + component + "]"
|
||||
}
|
||||
|
||||
var value = uniform.defaultAt(component)
|
||||
if (NativePostProcessing.hasValue(index, uniform.name)) {
|
||||
value = NativePostProcessing.getValue(index, uniform.name, component)
|
||||
}
|
||||
|
||||
val steps = uniform.steps
|
||||
val step = Math.round((value - uniform.min) / uniform.step)
|
||||
|
||||
addStepSlider(
|
||||
title,
|
||||
container,
|
||||
steps,
|
||||
step,
|
||||
{ position -> uniform.describe(position) },
|
||||
{ emulationFragment.persistPostProcessing() }
|
||||
) { position ->
|
||||
NativePostProcessing.setValue(
|
||||
index,
|
||||
uniform.name,
|
||||
component,
|
||||
uniform.min + position * uniform.step
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addFrameGenCell(inflater: LayoutInflater, row: ViewGroup): TextView {
|
||||
val cell = inflater.inflate(R.layout.item_quick_settings_frame_gen_cell, row, false)
|
||||
as TextView
|
||||
row.addView(cell)
|
||||
return cell
|
||||
}
|
||||
|
||||
fun addFrameGen(container: ViewGroup) {
|
||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||
val itemView = inflater.inflate(R.layout.item_quick_settings_frame_gen, container, false)
|
||||
|
||||
val multiplierRow = itemView.findViewById<LinearLayout>(R.id.frame_gen_multipliers)
|
||||
val targetRow = itemView.findViewById<LinearLayout>(R.id.frame_gen_targets)
|
||||
val targetSection = itemView.findViewById<ViewGroup>(R.id.frame_gen_target_section)
|
||||
val flowRow = itemView.findViewById<LinearLayout>(R.id.frame_gen_flow)
|
||||
val flowSection = itemView.findViewById<ViewGroup>(R.id.frame_gen_flow_section)
|
||||
|
||||
val multiplierNames =
|
||||
emulationFragment.resources.getStringArray(R.array.frameGenMultiplierNames)
|
||||
val multiplierValues =
|
||||
emulationFragment.resources.getIntArray(R.array.frameGenMultiplierValues)
|
||||
val targetValues =
|
||||
emulationFragment.resources.getIntArray(R.array.frameGenTargetRateValues)
|
||||
val flowValues =
|
||||
emulationFragment.resources.getIntArray(R.array.frameGenFlowScaleValues)
|
||||
|
||||
val columns = maxOf(
|
||||
multiplierValues.size + 1,
|
||||
targetValues.size,
|
||||
flowValues.size + 1
|
||||
).toFloat()
|
||||
multiplierRow.weightSum = columns
|
||||
targetRow.weightSum = columns
|
||||
flowRow.weightSum = columns
|
||||
|
||||
val powerCell = addFrameGenCell(inflater, multiplierRow)
|
||||
|
||||
val multiplierCells = mutableListOf<TextView>()
|
||||
for (name in multiplierNames) {
|
||||
val cell = addFrameGenCell(inflater, multiplierRow)
|
||||
cell.text = name
|
||||
multiplierCells.add(cell)
|
||||
}
|
||||
|
||||
val targetCells = mutableListOf<TextView>()
|
||||
for (value in targetValues) {
|
||||
val cell = addFrameGenCell(inflater, targetRow)
|
||||
if (value == 0) {
|
||||
cell.setText(R.string.frame_gen_fixed)
|
||||
} else {
|
||||
cell.text = value.toString()
|
||||
}
|
||||
targetCells.add(cell)
|
||||
}
|
||||
|
||||
val autoCell = addFrameGenCell(inflater, flowRow)
|
||||
autoCell.setText(R.string.frame_gen_flow_auto)
|
||||
|
||||
val flowCells = mutableListOf<TextView>()
|
||||
for (value in flowValues) {
|
||||
val cell = addFrameGenCell(inflater, flowRow)
|
||||
cell.text = "$value%"
|
||||
flowCells.add(cell)
|
||||
}
|
||||
|
||||
val inactiveAlpha = 0.38f
|
||||
|
||||
fun refresh() {
|
||||
val enabled = BooleanSetting.RENDERER_FRAME_GEN.getBoolean(needsGlobal = false)
|
||||
val multiplier = IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.getInt(needsGlobal = false)
|
||||
val target = IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.getInt(needsGlobal = false)
|
||||
val fixed = target == 0
|
||||
val flowAuto =
|
||||
BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.getBoolean(needsGlobal = false)
|
||||
val flowScale = IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.getInt(needsGlobal = false)
|
||||
|
||||
var powerLabel = R.string.frame_gen_off
|
||||
var rowAlpha = inactiveAlpha
|
||||
if (enabled) {
|
||||
powerLabel = R.string.frame_gen_on
|
||||
rowAlpha = 1.0f
|
||||
}
|
||||
|
||||
var multiplierAlpha = inactiveAlpha
|
||||
if (enabled && fixed) {
|
||||
multiplierAlpha = 1.0f
|
||||
}
|
||||
|
||||
powerCell.setText(powerLabel)
|
||||
powerCell.isSelected = enabled
|
||||
|
||||
multiplierCells.forEachIndexed { index, cell ->
|
||||
cell.isEnabled = enabled
|
||||
cell.isSelected = enabled && fixed && multiplierValues[index] == multiplier
|
||||
cell.alpha = multiplierAlpha
|
||||
}
|
||||
|
||||
targetSection.alpha = rowAlpha
|
||||
targetCells.forEachIndexed { index, cell ->
|
||||
cell.isEnabled = enabled
|
||||
cell.isSelected = enabled && targetValues[index] == target
|
||||
}
|
||||
|
||||
flowSection.alpha = rowAlpha
|
||||
autoCell.isEnabled = enabled
|
||||
autoCell.isSelected = enabled && flowAuto
|
||||
|
||||
flowCells.forEachIndexed { index, cell ->
|
||||
cell.isEnabled = enabled
|
||||
cell.isSelected = enabled && !flowAuto && flowValues[index] == flowScale
|
||||
}
|
||||
}
|
||||
|
||||
powerCell.setOnClickListener {
|
||||
if (BooleanSetting.RENDERER_FRAME_GEN.getBoolean(needsGlobal = false)) {
|
||||
BooleanSetting.RENDERER_FRAME_GEN.setBoolean(false)
|
||||
} else {
|
||||
IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.setInt(multiplierValues.first())
|
||||
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.setInt(0)
|
||||
BooleanSetting.RENDERER_FRAME_GEN.setBoolean(true)
|
||||
}
|
||||
saveSettings()
|
||||
refresh()
|
||||
}
|
||||
|
||||
multiplierCells.forEachIndexed { index, cell ->
|
||||
cell.setOnClickListener {
|
||||
IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.setInt(multiplierValues[index])
|
||||
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.setInt(0)
|
||||
saveSettings()
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
targetCells.forEachIndexed { index, cell ->
|
||||
cell.setOnClickListener {
|
||||
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.setInt(targetValues[index])
|
||||
saveSettings()
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
autoCell.setOnClickListener {
|
||||
BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.setBoolean(true)
|
||||
saveSettings()
|
||||
refresh()
|
||||
}
|
||||
|
||||
flowCells.forEachIndexed { index, cell ->
|
||||
cell.setOnClickListener {
|
||||
IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.setInt(flowValues[index])
|
||||
BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.setBoolean(false)
|
||||
saveSettings()
|
||||
refresh()
|
||||
}
|
||||
}
|
||||
|
||||
refresh()
|
||||
container.addView(itemView)
|
||||
}
|
||||
|
||||
fun addDivider(container: ViewGroup) {
|
||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||
val dividerView = inflater.inflate(R.layout.item_quick_settings_divider, container, false)
|
||||
|
||||
+2
@@ -38,7 +38,9 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
|
||||
RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"),
|
||||
RENDERER_SAMPLE_SHADING("sample_shading"),
|
||||
RENDERER_FRAME_GEN("frame_gen"),
|
||||
RENDERER_FRAME_GEN_FP16("frame_gen_fp16"),
|
||||
RENDERER_FRAME_GEN_FLOW_SCALE_AUTO("frame_gen_flow_scale_auto"),
|
||||
RENDERER_FRAME_GEN_DUMP_FLOW("frame_gen_dump_flow"),
|
||||
GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"),
|
||||
PICTURE_IN_PICTURE("picture_in_picture"),
|
||||
USE_CUSTOM_RTC("custom_rtc_enabled"),
|
||||
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model
|
||||
|
||||
class FxPresetNameSetting(private val onNamed: (String) -> Unit) : AbstractStringSetting {
|
||||
override val key: String
|
||||
get() = "fx_preset_name"
|
||||
|
||||
override val defaultValue: Any
|
||||
get() = ""
|
||||
|
||||
override val isRuntimeModifiable: Boolean
|
||||
get() = true
|
||||
|
||||
override val pairedSettingKey: String
|
||||
get() = ""
|
||||
|
||||
override val isSwitchable: Boolean
|
||||
get() = false
|
||||
|
||||
override val isSaveable: Boolean
|
||||
get() = true
|
||||
|
||||
override var global: Boolean
|
||||
get() = true
|
||||
set(_) {}
|
||||
|
||||
override fun getString(needsGlobal: Boolean): String = ""
|
||||
|
||||
override fun setString(value: String) = onNamed(value)
|
||||
|
||||
override fun getValueAsString(needsGlobal: Boolean): String = ""
|
||||
|
||||
override fun reset() {}
|
||||
}
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model
|
||||
|
||||
import org.yuzu.yuzu_emu.utils.NativePostProcessing
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
abstract class FxUniformSetting(
|
||||
protected val index: Int,
|
||||
protected val uniform: NativePostProcessing.Uniform,
|
||||
protected val component: Int
|
||||
) : AbstractSetting {
|
||||
override val key: String
|
||||
get() = "fx_${index}_${uniform.name}_$component"
|
||||
|
||||
override val isRuntimeModifiable: Boolean
|
||||
get() = true
|
||||
|
||||
override val pairedSettingKey: String
|
||||
get() = ""
|
||||
|
||||
override val isSwitchable: Boolean
|
||||
get() = false
|
||||
|
||||
override val isSaveable: Boolean
|
||||
get() = true
|
||||
|
||||
override var global: Boolean
|
||||
get() = true
|
||||
set(_) {}
|
||||
|
||||
protected fun currentValue(): Float {
|
||||
if (NativePostProcessing.hasValue(index, uniform.name)) {
|
||||
return NativePostProcessing.getValue(index, uniform.name, component)
|
||||
}
|
||||
return uniform.defaultAt(component)
|
||||
}
|
||||
|
||||
protected fun commit(value: Float) {
|
||||
NativePostProcessing.setValue(index, uniform.name, component, value)
|
||||
NativePostProcessing.store()
|
||||
}
|
||||
|
||||
override fun reset() = commit(uniform.defaultAt(component))
|
||||
}
|
||||
|
||||
class FxUniformSliderSetting(
|
||||
index: Int,
|
||||
uniform: NativePostProcessing.Uniform,
|
||||
component: Int
|
||||
) : FxUniformSetting(index, uniform, component), AbstractIntSetting {
|
||||
override val defaultValue: Any
|
||||
get() = ((uniform.defaultAt(component) - uniform.min) / uniform.step).roundToInt()
|
||||
|
||||
override fun getInt(needsGlobal: Boolean): Int =
|
||||
((currentValue() - uniform.min) / uniform.step).roundToInt()
|
||||
|
||||
override fun setInt(value: Int) = commit(uniform.min + value * uniform.step)
|
||||
|
||||
override fun getValueAsString(needsGlobal: Boolean): String {
|
||||
if (uniform.kind == NativePostProcessing.KIND_FLOAT) {
|
||||
return String.format("%.3f", currentValue())
|
||||
}
|
||||
return currentValue().roundToInt().toString()
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ object Settings {
|
||||
SECTION_SYSTEM(R.string.preferences_system),
|
||||
SECTION_RENDERER(R.string.preferences_graphics),
|
||||
SECTION_FRAME_GEN(R.string.frame_gen),
|
||||
SECTION_POST_PROCESSING(R.string.post_processing),
|
||||
SECTION_PERFORMANCE_STATS(R.string.stats_overlay_options),
|
||||
SECTION_INPUT_OVERLAY(R.string.input_overlay_options),
|
||||
SECTION_SOC_OVERLAY(R.string.soc_overlay_options),
|
||||
|
||||
-1
@@ -13,7 +13,6 @@ enum class StringSetting(override val key: String) : AbstractStringSetting {
|
||||
DEVICE_NAME("device_name"),
|
||||
LOG_FILTER("log_filter"),
|
||||
PROGRAM_ARGS("program_args"),
|
||||
POST_SHADER_CHAIN("post_shader_chain"),
|
||||
|
||||
WEB_TOKEN("eden_token"),
|
||||
WEB_USERNAME("eden_username")
|
||||
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
|
||||
class FxButtonSetting(
|
||||
@StringRes titleId: Int,
|
||||
val onClick: () -> Unit
|
||||
) : SettingsItem(emptySetting, titleId, "", 0, "") {
|
||||
override val type = TYPE_FX_BUTTON
|
||||
}
|
||||
-14
@@ -1,14 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
class FxPresetSetting(
|
||||
titleString: String,
|
||||
descriptionString: String = "",
|
||||
val deletable: Boolean = false,
|
||||
val onApply: () -> Unit,
|
||||
val onDelete: () -> Unit = {}
|
||||
) : SettingsItem(emptySetting, 0, titleString, 0, descriptionString) {
|
||||
override val type = TYPE_FX_PRESET
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
import org.yuzu.yuzu_emu.utils.NativePostProcessing
|
||||
|
||||
class FxShaderCardSetting(
|
||||
titleString: String,
|
||||
descriptionString: String,
|
||||
val index: Int,
|
||||
val expanded: Boolean,
|
||||
val uniforms: List<NativePostProcessing.Uniform>,
|
||||
val onToggle: () -> Unit,
|
||||
val onRemove: () -> Unit,
|
||||
val onReset: () -> Unit
|
||||
) : SettingsItem(emptySetting, 0, titleString, 0, descriptionString) {
|
||||
override val type = TYPE_FX_SHADER
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.model.view
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
|
||||
class FxToolbarSetting(
|
||||
@StringRes val addLabelId: Int,
|
||||
val listOpen: Boolean,
|
||||
val presetLabel: String,
|
||||
val hasEffects: Boolean,
|
||||
val createPreset: StringInputSetting,
|
||||
val onAdd: () -> Unit,
|
||||
val onPresets: () -> Unit,
|
||||
val onRemoveAll: () -> Unit
|
||||
) : SettingsItem(emptySetting, 0, "", 0, "") {
|
||||
override val type = TYPE_FX_TOOLBAR
|
||||
}
|
||||
+17
-5
@@ -123,7 +123,9 @@ abstract class SettingsItem(
|
||||
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key,
|
||||
IntSetting.RENDERER_FRAME_GEN_QUEUE_TARGET.key,
|
||||
BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.key,
|
||||
IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.key
|
||||
IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.key,
|
||||
BooleanSetting.RENDERER_FRAME_GEN_FP16.key,
|
||||
BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW.key
|
||||
)
|
||||
|
||||
const val TYPE_HEADER = 0
|
||||
@@ -142,10 +144,6 @@ abstract class SettingsItem(
|
||||
const val TYPE_LAUNCHABLE = 13
|
||||
const val TYPE_PATH = 14
|
||||
const val TYPE_GPU_UNSWIZZLE = 15
|
||||
const val TYPE_FX_TOOLBAR = 16
|
||||
const val TYPE_FX_PRESET = 17
|
||||
const val TYPE_FX_SHADER = 18
|
||||
const val TYPE_FX_BUTTON = 19
|
||||
|
||||
const val FASTMEM_COMBINED = "fastmem_combined"
|
||||
const val GPU_UNSWIZZLE_COMBINED = "gpu_unswizzle_combined"
|
||||
@@ -707,6 +705,20 @@ abstract class SettingsItem(
|
||||
units = "%"
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_FRAME_GEN_FP16,
|
||||
titleId = R.string.frame_gen_fp16,
|
||||
descriptionId = R.string.frame_gen_fp16_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SwitchSetting(
|
||||
BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW,
|
||||
titleId = R.string.frame_gen_dump_flow,
|
||||
descriptionId = R.string.frame_gen_dump_flow_description
|
||||
)
|
||||
)
|
||||
put(
|
||||
SingleChoiceSetting(
|
||||
IntSetting.RENDERER_SCREEN_LAYOUT,
|
||||
|
||||
-32
@@ -23,10 +23,6 @@ import com.google.android.material.timepicker.TimeFormat
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.SettingsNavigationDirections
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxButtonBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxPresetBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxShaderBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxToolbarBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingInputBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
|
||||
@@ -110,34 +106,6 @@ class SettingsAdapter(
|
||||
GpuUnswizzleViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_FX_TOOLBAR -> {
|
||||
FxToolbarViewHolder(
|
||||
ListItemSettingFxToolbarBinding.inflate(inflater, parent, false),
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_FX_PRESET -> {
|
||||
FxPresetViewHolder(
|
||||
ListItemSettingFxPresetBinding.inflate(inflater, parent, false),
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_FX_SHADER -> {
|
||||
FxShaderCardViewHolder(
|
||||
ListItemSettingFxShaderBinding.inflate(inflater, parent, false),
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
SettingsItem.TYPE_FX_BUTTON -> {
|
||||
FxButtonViewHolder(
|
||||
ListItemSettingFxButtonBinding.inflate(inflater, parent, false),
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
|
||||
}
|
||||
|
||||
+2
-214
@@ -18,7 +18,6 @@ import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.FxPresetNameSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
||||
@@ -31,7 +30,6 @@ import org.yuzu.yuzu_emu.features.settings.model.view.*
|
||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
import org.yuzu.yuzu_emu.utils.NativePostProcessing
|
||||
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
|
||||
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
||||
import androidx.core.content.edit
|
||||
@@ -46,14 +44,6 @@ class SettingsFragmentPresenter(
|
||||
) {
|
||||
private var settingsList = ArrayList<SettingsItem>()
|
||||
|
||||
private val expandedShaderSlots = mutableSetOf<Int>()
|
||||
|
||||
private var shaderPickerOpen = false
|
||||
|
||||
private var presetPickerOpen = false
|
||||
|
||||
private var postProcessingSynced = false
|
||||
|
||||
private val context get() = YuzuApplication.appContext
|
||||
|
||||
// Extension for altering settings list based on each setting's properties
|
||||
@@ -124,6 +114,7 @@ class SettingsFragmentPresenter(
|
||||
) {
|
||||
add(IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.key)
|
||||
}
|
||||
add(BooleanSetting.RENDERER_FRAME_GEN_FP16.key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,7 +163,6 @@ class SettingsFragmentPresenter(
|
||||
MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
||||
MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
||||
MenuTag.SECTION_FRAME_GEN -> addFrameGenSettings(sl)
|
||||
MenuTag.SECTION_POST_PROCESSING -> addPostProcessingSettings(sl)
|
||||
MenuTag.SECTION_PERFORMANCE_STATS -> addPerformanceOverlaySettings(sl)
|
||||
MenuTag.SECTION_SOC_OVERLAY -> addSocOverlaySettings(sl)
|
||||
MenuTag.SECTION_INPUT_OVERLAY -> addInputOverlaySettings(sl)
|
||||
@@ -200,209 +190,6 @@ class SettingsFragmentPresenter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun addPostProcessingSettings(sl: ArrayList<SettingsItem>) {
|
||||
if (!postProcessingSynced) {
|
||||
postProcessingSynced = true
|
||||
NativePostProcessing.reload()
|
||||
}
|
||||
|
||||
val usable = NativePostProcessing.catalog().filter { it.valid }
|
||||
|
||||
sl.apply {
|
||||
if (usable.isEmpty()) {
|
||||
add(
|
||||
RunnableSetting(
|
||||
titleId = R.string.post_processing_empty,
|
||||
descriptionString = NativePostProcessing.getShaderDirectory(),
|
||||
isRunnable = false
|
||||
) {}
|
||||
)
|
||||
return@apply
|
||||
}
|
||||
|
||||
val labels = mutableListOf<String>()
|
||||
val summaries = mutableListOf<String>()
|
||||
val files = mutableListOf<String>()
|
||||
val techniques = mutableListOf<String>()
|
||||
for (effect in usable) {
|
||||
for (technique in effect.techniques) {
|
||||
if (effect.techniques.size == 1) {
|
||||
labels.add(effect.label)
|
||||
} else {
|
||||
labels.add(effect.label + " \u00b7 " + technique)
|
||||
}
|
||||
summaries.add(effect.description)
|
||||
files.add(effect.file)
|
||||
techniques.add(technique)
|
||||
}
|
||||
}
|
||||
|
||||
val chain = NativePostProcessing.chain()
|
||||
val active = NativePostProcessing.getActivePreset()
|
||||
|
||||
var addLabel = R.string.post_processing_add
|
||||
if (chain.isNotEmpty()) {
|
||||
addLabel = R.string.post_processing_open_list
|
||||
}
|
||||
if (shaderPickerOpen) {
|
||||
addLabel = R.string.post_processing_close_list
|
||||
}
|
||||
|
||||
var presetLabel = context.getString(R.string.post_processing_presets)
|
||||
if (active.isNotEmpty()) {
|
||||
presetLabel = active
|
||||
}
|
||||
|
||||
val createPreset = StringInputSetting(
|
||||
setting = FxPresetNameSetting { name ->
|
||||
NativePostProcessing.savePreset(name, "")
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
},
|
||||
titleId = R.string.post_processing_preset_new,
|
||||
descriptionId = R.string.post_processing_preset_new_description,
|
||||
validator = { it != null && it.isNotBlank() && !it.contains('=') },
|
||||
errorId = R.string.post_processing_preset_name_invalid
|
||||
)
|
||||
|
||||
add(
|
||||
FxToolbarSetting(
|
||||
addLabelId = addLabel,
|
||||
listOpen = shaderPickerOpen,
|
||||
presetLabel = presetLabel,
|
||||
hasEffects = chain.isNotEmpty(),
|
||||
createPreset = createPreset,
|
||||
onAdd = {
|
||||
shaderPickerOpen = !shaderPickerOpen
|
||||
presetPickerOpen = false
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
},
|
||||
onPresets = {
|
||||
presetPickerOpen = !presetPickerOpen
|
||||
shaderPickerOpen = false
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
},
|
||||
onRemoveAll = {
|
||||
NativePostProcessing.clearChain()
|
||||
NativePostProcessing.clearPreset()
|
||||
NativePostProcessing.store()
|
||||
expandedShaderSlots.clear()
|
||||
shaderPickerOpen = false
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if (shaderPickerOpen) {
|
||||
for (choice in labels.indices) {
|
||||
add(
|
||||
RunnableSetting(
|
||||
titleString = labels[choice],
|
||||
descriptionString = summaries[choice],
|
||||
isRunnable = true
|
||||
) {
|
||||
NativePostProcessing.append(files[choice], techniques[choice])
|
||||
NativePostProcessing.store()
|
||||
shaderPickerOpen = false
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (presetPickerOpen) {
|
||||
add(
|
||||
FxPresetSetting(
|
||||
titleString = context.getString(R.string.post_processing_preset_none),
|
||||
onApply = {
|
||||
NativePostProcessing.clearPreset()
|
||||
presetPickerOpen = false
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
}
|
||||
)
|
||||
)
|
||||
for (preset in NativePostProcessing.presets()) {
|
||||
add(
|
||||
FxPresetSetting(
|
||||
titleString = preset.name,
|
||||
descriptionString = preset.description,
|
||||
deletable = !preset.bundled,
|
||||
onApply = {
|
||||
NativePostProcessing.applyPreset(preset.name)
|
||||
NativePostProcessing.store()
|
||||
presetPickerOpen = false
|
||||
expandedShaderSlots.clear()
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
},
|
||||
onDelete = {
|
||||
NativePostProcessing.deletePreset(preset.name)
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (active.isNotEmpty()) {
|
||||
add(
|
||||
FxButtonSetting(titleId = R.string.post_processing_preset_reset) {
|
||||
NativePostProcessing.applyPreset(active)
|
||||
NativePostProcessing.store()
|
||||
expandedShaderSlots.clear()
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
for (index in chain.indices) {
|
||||
val entry = chain[index]
|
||||
val effect = usable.firstOrNull { it.file == entry.file }
|
||||
|
||||
var header = entry.file
|
||||
var summary = ""
|
||||
var uniforms = emptyList<NativePostProcessing.Uniform>()
|
||||
if (effect != null) {
|
||||
header = effect.label
|
||||
if (effect.techniques.size > 1) {
|
||||
header = effect.label + " \u00b7 " + entry.technique
|
||||
}
|
||||
summary = effect.description
|
||||
uniforms = effect.uniforms
|
||||
}
|
||||
|
||||
val isOpen = expandedShaderSlots.contains(index)
|
||||
|
||||
add(
|
||||
FxShaderCardSetting(
|
||||
titleString = header,
|
||||
descriptionString = summary,
|
||||
index = index,
|
||||
expanded = isOpen,
|
||||
uniforms = uniforms,
|
||||
onToggle = {
|
||||
if (isOpen) {
|
||||
expandedShaderSlots.remove(index)
|
||||
} else {
|
||||
expandedShaderSlots.add(index)
|
||||
}
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
},
|
||||
onRemove = {
|
||||
NativePostProcessing.remove(index)
|
||||
NativePostProcessing.store()
|
||||
expandedShaderSlots.clear()
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
},
|
||||
onReset = {
|
||||
NativePostProcessing.resetValues(index)
|
||||
NativePostProcessing.store()
|
||||
settingsViewModel.setReloadListAndNotifyDataset(true)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
|
||||
sl.apply {
|
||||
add(
|
||||
@@ -1552,6 +1339,7 @@ class SettingsFragmentPresenter(
|
||||
add(BooleanSetting.DUMP_GUEST_SHADERS.key)
|
||||
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
|
||||
add(BooleanSetting.DUMP_MACROS.key)
|
||||
add(BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW.key)
|
||||
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
|
||||
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
|
||||
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxButtonBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.FxButtonSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||
|
||||
class FxButtonViewHolder(
|
||||
val binding: ListItemSettingFxButtonBinding,
|
||||
adapter: SettingsAdapter
|
||||
) : SettingViewHolder(binding.root, adapter) {
|
||||
private lateinit var setting: FxButtonSetting
|
||||
|
||||
override fun bind(item: SettingsItem) {
|
||||
setting = item as FxButtonSetting
|
||||
|
||||
binding.fxButton.text = item.title
|
||||
binding.fxButton.setOnClickListener { setting.onClick.invoke() }
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean = true
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.View
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxPresetBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.FxPresetSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||
|
||||
class FxPresetViewHolder(
|
||||
val binding: ListItemSettingFxPresetBinding,
|
||||
adapter: SettingsAdapter
|
||||
) : SettingViewHolder(binding.root, adapter) {
|
||||
private lateinit var setting: FxPresetSetting
|
||||
|
||||
override fun bind(item: SettingsItem) {
|
||||
setting = item as FxPresetSetting
|
||||
|
||||
binding.presetName.text = item.title
|
||||
binding.presetDescription.text = item.description
|
||||
binding.presetDescription.setVisible(item.description.isNotEmpty())
|
||||
|
||||
binding.presetRow.setOnClickListener { setting.onApply.invoke() }
|
||||
|
||||
binding.presetDelete.setVisible(setting.deletable)
|
||||
binding.presetDelete.setOnClickListener { setting.onDelete.invoke() }
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean = true
|
||||
}
|
||||
-100
@@ -1,100 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import org.yuzu.yuzu_emu.databinding.ItemSettingFxActionsBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ItemSettingFxSliderBinding
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxShaderBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.FxUniformSliderSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.FxShaderCardSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||
import org.yuzu.yuzu_emu.utils.NativePostProcessing
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||
|
||||
class FxShaderCardViewHolder(
|
||||
val binding: ListItemSettingFxShaderBinding,
|
||||
adapter: SettingsAdapter
|
||||
) : SettingViewHolder(binding.root, adapter) {
|
||||
private lateinit var setting: FxShaderCardSetting
|
||||
|
||||
override fun bind(item: SettingsItem) {
|
||||
setting = item as FxShaderCardSetting
|
||||
|
||||
binding.shaderTitle.text = item.title
|
||||
binding.shaderSummary.text = item.description
|
||||
binding.shaderSummary.setVisible(item.description.isNotEmpty())
|
||||
|
||||
var rotation = 0f
|
||||
if (setting.expanded) {
|
||||
rotation = 180f
|
||||
}
|
||||
binding.shaderExpand.rotation = rotation
|
||||
|
||||
binding.shaderHeader.setOnClickListener { setting.onToggle.invoke() }
|
||||
binding.shaderRemove.setOnClickListener { setting.onRemove.invoke() }
|
||||
|
||||
binding.shaderBody.removeAllViews()
|
||||
binding.shaderBody.setVisible(setting.expanded)
|
||||
if (!setting.expanded) {
|
||||
return
|
||||
}
|
||||
|
||||
val inflater = LayoutInflater.from(binding.root.context)
|
||||
for (uniform in setting.uniforms) {
|
||||
if (uniform.uiType == NativePostProcessing.UI_HIDDEN) {
|
||||
continue
|
||||
}
|
||||
for (component in 0 until uniform.components) {
|
||||
addSlider(inflater, uniform, component)
|
||||
}
|
||||
}
|
||||
addActions(inflater)
|
||||
}
|
||||
|
||||
private fun addSlider(
|
||||
inflater: LayoutInflater,
|
||||
uniform: NativePostProcessing.Uniform,
|
||||
component: Int
|
||||
) {
|
||||
val row = ItemSettingFxSliderBinding.inflate(inflater, binding.shaderBody, false)
|
||||
val value = FxUniformSliderSetting(setting.index, uniform, component)
|
||||
|
||||
var title = uniform.label
|
||||
if (uniform.components > 1) {
|
||||
title = uniform.label + " [" + component + "]"
|
||||
}
|
||||
row.fxSliderTitle.text = title
|
||||
|
||||
val steps = uniform.steps.toFloat()
|
||||
row.fxSlider.valueFrom = 0f
|
||||
row.fxSlider.valueTo = steps
|
||||
row.fxSlider.stepSize = 1f
|
||||
row.fxSlider.value = value.getInt(false).toFloat().coerceIn(0f, steps)
|
||||
row.fxSliderValue.text = uniform.describe(row.fxSlider.value.toInt())
|
||||
|
||||
row.fxSlider.addOnChangeListener { _, position, fromUser ->
|
||||
if (fromUser) {
|
||||
value.setInt(position.toInt())
|
||||
row.fxSliderValue.text = uniform.describe(position.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
binding.shaderBody.addView(row.root)
|
||||
}
|
||||
|
||||
private fun addActions(inflater: LayoutInflater) {
|
||||
val row = ItemSettingFxActionsBinding.inflate(inflater, binding.shaderBody, false)
|
||||
|
||||
row.fxReset.setOnClickListener { setting.onReset.invoke() }
|
||||
|
||||
binding.shaderBody.addView(row.root)
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean = true
|
||||
}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.view.View
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import org.yuzu.yuzu_emu.R
|
||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxToolbarBinding
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.FxToolbarSetting
|
||||
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
|
||||
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
|
||||
|
||||
class FxToolbarViewHolder(
|
||||
val binding: ListItemSettingFxToolbarBinding,
|
||||
adapter: SettingsAdapter
|
||||
) : SettingViewHolder(binding.root, adapter) {
|
||||
private lateinit var setting: FxToolbarSetting
|
||||
|
||||
override fun bind(item: SettingsItem) {
|
||||
setting = item as FxToolbarSetting
|
||||
|
||||
binding.fxAdd.setText(setting.addLabelId)
|
||||
var addIcon = R.drawable.ic_add
|
||||
if (setting.listOpen) {
|
||||
addIcon = R.drawable.ic_clear
|
||||
}
|
||||
binding.fxAdd.setIconResource(addIcon)
|
||||
binding.fxAdd.setOnClickListener { setting.onAdd.invoke() }
|
||||
|
||||
binding.fxPresets.text = setting.presetLabel
|
||||
binding.fxPresets.setOnClickListener { setting.onPresets.invoke() }
|
||||
|
||||
binding.fxCreatePreset.isEnabled = setting.hasEffects
|
||||
binding.fxCreatePreset.setOnClickListener {
|
||||
adapter.onStringInputClick(setting.createPreset, bindingAdapterPosition)
|
||||
}
|
||||
|
||||
val removeBackground = MaterialColors.getColor(
|
||||
binding.fxRemoveAll,
|
||||
com.google.android.material.R.attr.colorErrorContainer
|
||||
)
|
||||
val removeForeground = MaterialColors.getColor(
|
||||
binding.fxRemoveAll,
|
||||
com.google.android.material.R.attr.colorOnErrorContainer
|
||||
)
|
||||
binding.fxRemoveAll.backgroundTintList = ColorStateList.valueOf(removeBackground)
|
||||
binding.fxRemoveAll.iconTint = ColorStateList.valueOf(removeForeground)
|
||||
binding.fxRemoveAll.isEnabled = setting.hasEffects
|
||||
binding.fxRemoveAll.setOnClickListener { setting.onRemoveAll.invoke() }
|
||||
}
|
||||
|
||||
override fun onClick(clicked: View) {}
|
||||
|
||||
override fun onLongClick(clicked: View): Boolean = true
|
||||
}
|
||||
@@ -92,10 +92,8 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import org.yuzu.yuzu_emu.utils.InputHandler
|
||||
import org.yuzu.yuzu_emu.utils.Log
|
||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
|
||||
import org.yuzu.yuzu_emu.utils.NativePostProcessing
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils
|
||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||
import org.yuzu.yuzu_emu.utils.collect
|
||||
@@ -885,8 +883,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
if (shouldUseCustom) {
|
||||
SettingsFile.loadCustomConfig(game!!)
|
||||
}
|
||||
refreshPostProcessing()
|
||||
addQuickSettings()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1091,34 +1087,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
private fun withPerGameConfig(create: Boolean, action: () -> Unit) {
|
||||
val target = game
|
||||
var owned = false
|
||||
if (target != null && !NativeConfig.isPerGameConfigLoaded()) {
|
||||
if (create || SettingsFile.getCustomSettingsFile(target).exists()) {
|
||||
SettingsFile.loadCustomConfig(target)
|
||||
owned = true
|
||||
}
|
||||
}
|
||||
action()
|
||||
if (owned) {
|
||||
NativeConfig.unloadPerGameConfig()
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshPostProcessing() = withPerGameConfig(false) {
|
||||
NativePostProcessing.reload()
|
||||
}
|
||||
|
||||
fun persistPostProcessing() = withPerGameConfig(true) {
|
||||
NativePostProcessing.persist()
|
||||
}
|
||||
|
||||
fun editPostProcessing(action: () -> Unit) = withPerGameConfig(true) {
|
||||
action()
|
||||
NativePostProcessing.persist()
|
||||
}
|
||||
|
||||
private fun addQuickSettings() {
|
||||
binding.quickSettingsSheet.apply {
|
||||
val container = binding.quickSettingsSheet.findViewById<ViewGroup>(R.id.quick_settings_container)
|
||||
@@ -1189,11 +1157,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
|
||||
quickSettings.addDivider(container)
|
||||
|
||||
if (LosslessScalingHelper.isInstalled() && LosslessScalingHelper.isSupportedByGpu()) {
|
||||
quickSettings.addFrameGen(container)
|
||||
quickSettings.addDivider(container)
|
||||
}
|
||||
|
||||
quickSettings.addIntSetting(
|
||||
R.string.renderer_accuracy,
|
||||
container,
|
||||
@@ -1231,10 +1194,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||
R.array.rendererAntiAliasingNames,
|
||||
R.array.rendererAntiAliasingValues
|
||||
)
|
||||
|
||||
quickSettings.addPostProcessing(container) {
|
||||
addQuickSettings()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -383,20 +383,6 @@ class GamePropertiesFragment : Fragment() {
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
SubmenuProperty(
|
||||
R.string.post_processing,
|
||||
R.string.post_processing_per_game_description,
|
||||
R.drawable.ic_post_processing,
|
||||
action = {
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
|
||||
args.game,
|
||||
Settings.MenuTag.SECTION_POST_PROCESSING
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
if (GpuDriverHelper.isAdrenoGpu()) {
|
||||
add(
|
||||
|
||||
@@ -171,20 +171,6 @@ class HomeSettingsFragment : Fragment() {
|
||||
)
|
||||
)
|
||||
}
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.post_processing,
|
||||
R.string.post_processing_description,
|
||||
R.drawable.ic_post_processing,
|
||||
{
|
||||
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
|
||||
null,
|
||||
Settings.MenuTag.SECTION_POST_PROCESSING
|
||||
)
|
||||
binding.root.findNavController().navigate(action)
|
||||
}
|
||||
)
|
||||
)
|
||||
add(
|
||||
HomeSetting(
|
||||
R.string.lossless_scaling,
|
||||
|
||||
@@ -1,242 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package org.yuzu.yuzu_emu.utils
|
||||
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
object NativePostProcessing {
|
||||
const val KIND_BOOL = 0
|
||||
const val KIND_INT = 1
|
||||
const val KIND_FLOAT = 2
|
||||
|
||||
const val UI_HIDDEN = 0
|
||||
const val UI_SLIDER = 1
|
||||
const val UI_DRAG = 2
|
||||
const val UI_COMBO = 3
|
||||
const val UI_RADIO = 4
|
||||
const val UI_CHECKBOX = 5
|
||||
const val UI_COLOR = 6
|
||||
const val UI_INPUT_BOX = 7
|
||||
|
||||
external fun getCatalogJson(): String
|
||||
|
||||
external fun getChainJson(): String
|
||||
|
||||
external fun append(file: String, technique: String)
|
||||
|
||||
external fun replace(index: Int, file: String, technique: String)
|
||||
|
||||
external fun remove(index: Int)
|
||||
|
||||
external fun move(index: Int, delta: Int)
|
||||
|
||||
external fun resetValues(index: Int)
|
||||
|
||||
external fun getValue(index: Int, uniform: String, component: Int): Float
|
||||
|
||||
external fun hasValue(index: Int, uniform: String): Boolean
|
||||
|
||||
external fun setValue(index: Int, uniform: String, component: Int, value: Float)
|
||||
|
||||
external fun store()
|
||||
|
||||
external fun reload()
|
||||
|
||||
external fun clearChain()
|
||||
|
||||
external fun getPresetsJson(): String
|
||||
|
||||
external fun getActivePreset(): String
|
||||
|
||||
external fun isPresetModified(): Boolean
|
||||
|
||||
external fun applyPreset(name: String): Boolean
|
||||
|
||||
external fun savePreset(name: String, description: String): Boolean
|
||||
|
||||
external fun deletePreset(name: String): Boolean
|
||||
|
||||
external fun clearPreset()
|
||||
|
||||
external fun isEnabled(): Boolean
|
||||
|
||||
external fun setEnabled(enabled: Boolean)
|
||||
|
||||
external fun getPresetDirectory(): String
|
||||
|
||||
fun persist() {
|
||||
val perGame = NativeConfig.isPerGameConfigLoaded()
|
||||
store()
|
||||
if (perGame) {
|
||||
NativeConfig.savePerGameConfig()
|
||||
} else {
|
||||
NativeConfig.saveGlobalConfig()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
external fun getShaderDirectory(): String
|
||||
|
||||
data class Uniform(
|
||||
val name: String,
|
||||
val label: String,
|
||||
val tooltip: String,
|
||||
val category: String,
|
||||
val kind: Int,
|
||||
val uiType: Int,
|
||||
val components: Int,
|
||||
val min: Float,
|
||||
val max: Float,
|
||||
val step: Float,
|
||||
val items: List<String>,
|
||||
val defaults: List<Float>
|
||||
) {
|
||||
val steps: Int
|
||||
get() {
|
||||
val span = max - min
|
||||
if (step <= 0f) {
|
||||
return 1
|
||||
}
|
||||
val count = Math.round(span / step)
|
||||
if (count < 1) {
|
||||
return 1
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
fun describe(position: Int): String {
|
||||
val value = min + position * step
|
||||
if (kind == NativePostProcessing.KIND_FLOAT) {
|
||||
return String.format("%.3f", value)
|
||||
}
|
||||
return Math.round(value).toString()
|
||||
}
|
||||
|
||||
fun defaultAt(component: Int): Float {
|
||||
if (component < defaults.size) {
|
||||
return defaults[component]
|
||||
}
|
||||
return 0f
|
||||
}
|
||||
}
|
||||
|
||||
data class Effect(
|
||||
val file: String,
|
||||
val name: String,
|
||||
val label: String,
|
||||
val description: String,
|
||||
val error: String,
|
||||
val techniques: List<String>,
|
||||
val uniforms: List<Uniform>
|
||||
) {
|
||||
val valid: Boolean
|
||||
get() = error.isEmpty() && techniques.isNotEmpty()
|
||||
}
|
||||
|
||||
data class ChainEntry(val file: String, val technique: String)
|
||||
|
||||
data class Preset(
|
||||
val name: String,
|
||||
val description: String,
|
||||
val bundled: Boolean
|
||||
)
|
||||
|
||||
fun presets(): List<Preset> {
|
||||
val out = mutableListOf<Preset>()
|
||||
val array = JSONArray(getPresetsJson())
|
||||
for (i in 0 until array.length()) {
|
||||
val obj = array.getJSONObject(i)
|
||||
out.add(
|
||||
Preset(
|
||||
name = obj.optString("name"),
|
||||
description = obj.optString("description"),
|
||||
bundled = obj.optBoolean("bundled")
|
||||
)
|
||||
)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
fun catalog(): List<Effect> {
|
||||
val out = mutableListOf<Effect>()
|
||||
val array = JSONArray(getCatalogJson())
|
||||
for (i in 0 until array.length()) {
|
||||
val obj = array.getJSONObject(i)
|
||||
out.add(
|
||||
Effect(
|
||||
file = obj.optString("file"),
|
||||
name = obj.optString("name"),
|
||||
label = obj.optString("label"),
|
||||
description = obj.optString("description"),
|
||||
error = obj.optString("error"),
|
||||
techniques = obj.optJSONArray("techniques").toStringList(),
|
||||
uniforms = obj.optJSONArray("uniforms").toUniformList()
|
||||
)
|
||||
)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
fun chain(): List<ChainEntry> {
|
||||
val out = mutableListOf<ChainEntry>()
|
||||
val array = JSONArray(getChainJson())
|
||||
for (i in 0 until array.length()) {
|
||||
val obj = array.getJSONObject(i)
|
||||
out.add(ChainEntry(obj.optString("file"), obj.optString("technique")))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
fun findEffect(file: String): Effect? = catalog().firstOrNull { it.file == file }
|
||||
|
||||
private fun JSONArray?.toStringList(): List<String> {
|
||||
if (this == null) {
|
||||
return emptyList()
|
||||
}
|
||||
val out = mutableListOf<String>()
|
||||
for (i in 0 until length()) {
|
||||
out.add(optString(i))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
private fun JSONArray?.toFloatList(): List<Float> {
|
||||
if (this == null) {
|
||||
return emptyList()
|
||||
}
|
||||
val out = mutableListOf<Float>()
|
||||
for (i in 0 until length()) {
|
||||
out.add(optDouble(i, 0.0).toFloat())
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
private fun JSONArray?.toUniformList(): List<Uniform> {
|
||||
if (this == null) {
|
||||
return emptyList()
|
||||
}
|
||||
val out = mutableListOf<Uniform>()
|
||||
for (i in 0 until length()) {
|
||||
val obj: JSONObject = optJSONObject(i) ?: continue
|
||||
out.add(
|
||||
Uniform(
|
||||
name = obj.optString("name"),
|
||||
label = obj.optString("label"),
|
||||
tooltip = obj.optString("tooltip"),
|
||||
category = obj.optString("category"),
|
||||
kind = obj.optInt("kind", KIND_FLOAT),
|
||||
uiType = obj.optInt("uiType", UI_HIDDEN),
|
||||
components = obj.optInt("components", 1),
|
||||
min = obj.optDouble("min", 0.0).toFloat(),
|
||||
max = obj.optDouble("max", 1.0).toFloat(),
|
||||
step = obj.optDouble("step", 0.01).toFloat(),
|
||||
items = obj.optJSONArray("items").toStringList(),
|
||||
defaults = obj.optJSONArray("defaults").toFloatList()
|
||||
)
|
||||
)
|
||||
}
|
||||
return out
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@ add_library(yuzu-android SHARED
|
||||
android_config.cpp
|
||||
android_config.h
|
||||
native_input.cpp
|
||||
native_post_processing.cpp
|
||||
)
|
||||
|
||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
||||
|
||||
@@ -1168,7 +1168,7 @@ VkPhysicalDeviceProperties GetVulkanDeviceProperties() {
|
||||
return physical_device.GetProperties();
|
||||
}
|
||||
|
||||
bool GetFrameGenerationSupport() {
|
||||
bool GetVulkanMemoryModelSupport() {
|
||||
Common::DynamicLibrary library;
|
||||
if (!library.Open("libvulkan.so")) {
|
||||
return false;
|
||||
@@ -1183,13 +1183,9 @@ bool GetFrameGenerationSupport() {
|
||||
|
||||
const Vulkan::vk::PhysicalDevice physical_device(physical_devices[0], dld);
|
||||
|
||||
VkPhysicalDeviceShaderFloat16Int8Features float16_int8{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES,
|
||||
.pNext = nullptr,
|
||||
};
|
||||
VkPhysicalDeviceVulkanMemoryModelFeatures memory_model{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES,
|
||||
.pNext = &float16_int8,
|
||||
.pNext = nullptr,
|
||||
};
|
||||
VkPhysicalDeviceFeatures2 features{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
@@ -1197,7 +1193,7 @@ bool GetFrameGenerationSupport() {
|
||||
};
|
||||
physical_device.GetFeatures2(features);
|
||||
|
||||
return memory_model.vulkanMemoryModel == VK_TRUE && float16_int8.shaderFloat16 == VK_TRUE;
|
||||
return memory_model.vulkanMemoryModel == VK_TRUE;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -1276,7 +1272,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getVulkanApiVersion(JNIEnv* env, j
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_supportsFrameGeneration(JNIEnv* env, jobject jobj) {
|
||||
try {
|
||||
return static_cast<jboolean>(GetFrameGenerationSupport());
|
||||
return static_cast<jboolean>(GetVulkanMemoryModelSupport());
|
||||
} catch (...) {
|
||||
return static_cast<jboolean>(false);
|
||||
}
|
||||
|
||||
@@ -15,22 +15,10 @@
|
||||
#include "frontend_common/config.h"
|
||||
#include "frontend_common/settings_generator.h"
|
||||
#include "native.h"
|
||||
#ifdef HAS_RESHADE
|
||||
#include "video_core/post_processing/fx_chain.h"
|
||||
#endif
|
||||
|
||||
std::unique_ptr<AndroidConfig> global_config;
|
||||
std::unique_ptr<AndroidConfig> per_game_config;
|
||||
|
||||
#ifdef HAS_RESHADE
|
||||
static void ResetFxChainToGlobal() {
|
||||
VideoCore::UseGlobalFxSettings();
|
||||
VideoCore::FxChain::Instance().LoadFromSettings();
|
||||
}
|
||||
#else
|
||||
static void ResetFxChainToGlobal() {}
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
||||
auto key = Common::Android::GetJString(env, jkey);
|
||||
@@ -51,7 +39,6 @@ extern "C" {
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeGlobalConfig(JNIEnv* env, jobject obj) {
|
||||
global_config = std::make_unique<AndroidConfig>();
|
||||
FrontendCommon::GenerateSettings();
|
||||
ResetFxChainToGlobal();
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadGlobalConfig(JNIEnv* env, jobject obj) {
|
||||
@@ -60,7 +47,6 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadGlobalConfig(JNIEnv* env,
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadGlobalConfig(JNIEnv* env, jobject obj) {
|
||||
global_config->AndroidConfig::ReloadAllValues();
|
||||
ResetFxChainToGlobal();
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveGlobalConfig(JNIEnv* env, jobject obj) {
|
||||
|
||||
@@ -1,350 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include <jni.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "common/android/android_common.h"
|
||||
#ifdef HAS_RESHADE
|
||||
#include "android_config.h"
|
||||
#include "video_core/post_processing/fx_chain.h"
|
||||
#include "common/settings.h"
|
||||
#include "video_core/post_processing/fx_effect.h"
|
||||
#include "video_core/post_processing/fx_preset.h"
|
||||
|
||||
extern std::unique_ptr<AndroidConfig> per_game_config;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef HAS_RESHADE
|
||||
bool EditingPerGame() {
|
||||
return per_game_config != nullptr;
|
||||
}
|
||||
|
||||
void BeginFxEdit() {
|
||||
if (!EditingPerGame()) {
|
||||
return;
|
||||
}
|
||||
VideoCore::UsePerGameFxSettings();
|
||||
}
|
||||
|
||||
nlohmann::json SerializeUniform(const VideoCore::FxUniformDesc& uniform) {
|
||||
nlohmann::json out;
|
||||
out["name"] = uniform.name;
|
||||
out["label"] = uniform.label;
|
||||
out["tooltip"] = uniform.tooltip;
|
||||
out["category"] = uniform.category;
|
||||
out["kind"] = static_cast<int>(uniform.kind);
|
||||
out["uiType"] = static_cast<int>(uniform.ui_type);
|
||||
out["components"] = uniform.components;
|
||||
out["min"] = uniform.ui_min;
|
||||
out["max"] = uniform.ui_max;
|
||||
out["step"] = uniform.ui_step;
|
||||
out["items"] = uniform.items;
|
||||
|
||||
nlohmann::json defaults = nlohmann::json::array();
|
||||
for (u32 i = 0; i < uniform.components; ++i) {
|
||||
defaults.push_back(uniform.default_value[i]);
|
||||
}
|
||||
out["defaults"] = defaults;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::array<f32, 4> DefaultValueOf(size_t index, const std::string& uniform) {
|
||||
const auto entries = VideoCore::FxChain::Instance().Entries();
|
||||
if (index >= entries.size()) {
|
||||
return {};
|
||||
}
|
||||
const VideoCore::FxEffectDesc* effect = VideoCore::FindFxEffect(entries[index].file);
|
||||
if (effect == nullptr) {
|
||||
return {};
|
||||
}
|
||||
const VideoCore::FxUniformDesc* desc = VideoCore::FindFxUniform(*effect, uniform);
|
||||
if (desc == nullptr) {
|
||||
return {};
|
||||
}
|
||||
return desc->default_value;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getCatalogJson(JNIEnv* env,
|
||||
jobject obj) {
|
||||
nlohmann::json out = nlohmann::json::array();
|
||||
|
||||
#ifdef HAS_RESHADE
|
||||
VideoCore::FxChain::Instance().DropUnknownEntries();
|
||||
|
||||
for (const auto& effect : VideoCore::GetFxCatalog()) {
|
||||
nlohmann::json entry;
|
||||
entry["file"] = effect.file;
|
||||
entry["name"] = effect.name;
|
||||
entry["label"] = effect.label;
|
||||
entry["description"] = effect.description;
|
||||
entry["error"] = effect.error;
|
||||
entry["techniques"] = effect.techniques;
|
||||
|
||||
nlohmann::json uniforms = nlohmann::json::array();
|
||||
for (const auto& uniform : effect.uniforms) {
|
||||
uniforms.push_back(SerializeUniform(uniform));
|
||||
}
|
||||
entry["uniforms"] = uniforms;
|
||||
|
||||
out.push_back(entry);
|
||||
}
|
||||
#endif
|
||||
|
||||
return Common::Android::ToJString(env, out.dump());
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getChainJson(JNIEnv* env, jobject obj) {
|
||||
nlohmann::json out = nlohmann::json::array();
|
||||
|
||||
#ifdef HAS_RESHADE
|
||||
for (const auto& entry : VideoCore::FxChain::Instance().Entries()) {
|
||||
nlohmann::json item;
|
||||
item["file"] = entry.file;
|
||||
item["technique"] = entry.technique;
|
||||
|
||||
nlohmann::json values = nlohmann::json::object();
|
||||
for (const auto& [name, value] : entry.values) {
|
||||
values[name] = {value[0], value[1], value[2], value[3]};
|
||||
}
|
||||
item["values"] = values;
|
||||
|
||||
out.push_back(item);
|
||||
}
|
||||
#endif
|
||||
|
||||
return Common::Android::ToJString(env, out.dump());
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_append(JNIEnv* env, jobject obj,
|
||||
jstring jfile, jstring jtechnique) {
|
||||
#ifdef HAS_RESHADE
|
||||
VideoCore::FxChain::Instance().Append(Common::Android::GetJString(env, jfile),
|
||||
Common::Android::GetJString(env, jtechnique));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_replace(JNIEnv* env, jobject obj,
|
||||
jint index, jstring jfile,
|
||||
jstring jtechnique) {
|
||||
#ifdef HAS_RESHADE
|
||||
VideoCore::FxChain::Instance().Replace(static_cast<size_t>(index),
|
||||
Common::Android::GetJString(env, jfile),
|
||||
Common::Android::GetJString(env, jtechnique));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_remove(JNIEnv* env, jobject obj,
|
||||
jint index) {
|
||||
#ifdef HAS_RESHADE
|
||||
VideoCore::FxChain::Instance().Remove(static_cast<size_t>(index));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_move(JNIEnv* env, jobject obj, jint index,
|
||||
jint delta) {
|
||||
#ifdef HAS_RESHADE
|
||||
VideoCore::FxChain::Instance().Move(static_cast<size_t>(index), delta);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_resetValues(JNIEnv* env, jobject obj,
|
||||
jint index) {
|
||||
#ifdef HAS_RESHADE
|
||||
VideoCore::FxChain::Instance().ResetValues(static_cast<size_t>(index));
|
||||
#endif
|
||||
}
|
||||
|
||||
jfloat Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getValue(JNIEnv* env, jobject obj,
|
||||
jint index, jstring juniform,
|
||||
jint component) {
|
||||
#ifdef HAS_RESHADE
|
||||
if (component < 0 || component >= 4) {
|
||||
return 0.0f;
|
||||
}
|
||||
const auto value = VideoCore::FxChain::Instance().GetValue(
|
||||
static_cast<size_t>(index), Common::Android::GetJString(env, juniform));
|
||||
return value[static_cast<size_t>(component)];
|
||||
#else
|
||||
return 0.0f;
|
||||
#endif
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_hasValue(JNIEnv* env, jobject obj,
|
||||
jint index,
|
||||
jstring juniform) {
|
||||
#ifdef HAS_RESHADE
|
||||
return static_cast<jboolean>(VideoCore::FxChain::Instance().HasValue(
|
||||
static_cast<size_t>(index), Common::Android::GetJString(env, juniform)));
|
||||
#else
|
||||
return static_cast<jboolean>(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_setValue(JNIEnv* env, jobject obj,
|
||||
jint index, jstring juniform,
|
||||
jint component, jfloat value) {
|
||||
#ifdef HAS_RESHADE
|
||||
if (component < 0 || component >= 4) {
|
||||
return;
|
||||
}
|
||||
const std::string uniform = Common::Android::GetJString(env, juniform);
|
||||
auto& chain = VideoCore::FxChain::Instance();
|
||||
const auto slot = static_cast<size_t>(index);
|
||||
|
||||
auto current = chain.GetValue(slot, uniform);
|
||||
if (!chain.HasValue(slot, uniform)) {
|
||||
current = DefaultValueOf(slot, uniform);
|
||||
}
|
||||
|
||||
current[static_cast<size_t>(component)] = value;
|
||||
chain.SetValue(slot, uniform, current);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_store(JNIEnv* env, jobject obj) {
|
||||
#ifdef HAS_RESHADE
|
||||
BeginFxEdit();
|
||||
VideoCore::FxChain::Instance().StoreToSettings();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_reload(JNIEnv* env, jobject obj) {
|
||||
#ifdef HAS_RESHADE
|
||||
if (!EditingPerGame()) {
|
||||
VideoCore::UseGlobalFxSettings();
|
||||
}
|
||||
VideoCore::FxChain::Instance().LoadFromSettings();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_clearChain(JNIEnv* env, jobject obj) {
|
||||
#ifdef HAS_RESHADE
|
||||
VideoCore::FxChain::Instance().Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getShaderDirectory(JNIEnv* env,
|
||||
jobject obj) {
|
||||
#ifdef HAS_RESHADE
|
||||
return Common::Android::ToJString(env, VideoCore::GetFxRootDirectory().string());
|
||||
#else
|
||||
return Common::Android::ToJString(env, "");
|
||||
#endif
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getPresetsJson(JNIEnv* env,
|
||||
jobject obj) {
|
||||
nlohmann::json out = nlohmann::json::array();
|
||||
#ifdef HAS_RESHADE
|
||||
VideoCore::ReloadFxPresetCatalog();
|
||||
|
||||
for (const auto& preset : VideoCore::GetFxPresetCatalog()) {
|
||||
nlohmann::json entry;
|
||||
entry["name"] = preset.name;
|
||||
entry["description"] = preset.description;
|
||||
entry["bundled"] = preset.bundled;
|
||||
out.push_back(entry);
|
||||
}
|
||||
#endif
|
||||
return Common::Android::ToJString(env, out.dump());
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getActivePreset(JNIEnv* env,
|
||||
jobject obj) {
|
||||
#ifdef HAS_RESHADE
|
||||
return Common::Android::ToJString(env, VideoCore::GetActiveFxPreset());
|
||||
#else
|
||||
return Common::Android::ToJString(env, "");
|
||||
#endif
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_isPresetModified(JNIEnv* env,
|
||||
jobject obj) {
|
||||
#ifdef HAS_RESHADE
|
||||
return static_cast<jboolean>(VideoCore::IsActiveFxPresetModified());
|
||||
#else
|
||||
return static_cast<jboolean>(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_applyPreset(JNIEnv* env, jobject obj,
|
||||
jstring jname) {
|
||||
#ifdef HAS_RESHADE
|
||||
BeginFxEdit();
|
||||
return static_cast<jboolean>(
|
||||
VideoCore::ApplyFxPreset(Common::Android::GetJString(env, jname)));
|
||||
#else
|
||||
return static_cast<jboolean>(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_savePreset(JNIEnv* env, jobject obj,
|
||||
jstring jname,
|
||||
jstring jdescription) {
|
||||
#ifdef HAS_RESHADE
|
||||
BeginFxEdit();
|
||||
return static_cast<jboolean>(
|
||||
VideoCore::SaveFxPreset(Common::Android::GetJString(env, jname),
|
||||
Common::Android::GetJString(env, jdescription)));
|
||||
#else
|
||||
return static_cast<jboolean>(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_deletePreset(JNIEnv* env, jobject obj,
|
||||
jstring jname) {
|
||||
#ifdef HAS_RESHADE
|
||||
BeginFxEdit();
|
||||
return static_cast<jboolean>(
|
||||
VideoCore::DeleteFxPreset(Common::Android::GetJString(env, jname)));
|
||||
#else
|
||||
return static_cast<jboolean>(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_clearPreset(JNIEnv* env, jobject obj) {
|
||||
#ifdef HAS_RESHADE
|
||||
BeginFxEdit();
|
||||
VideoCore::SetActiveFxPreset(std::string_view());
|
||||
#endif
|
||||
}
|
||||
|
||||
jboolean Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_isEnabled(JNIEnv* env, jobject obj) {
|
||||
#ifdef HAS_RESHADE
|
||||
return static_cast<jboolean>(Settings::values.post_shader_enabled.GetValue());
|
||||
#else
|
||||
return static_cast<jboolean>(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_setEnabled(JNIEnv* env, jobject obj,
|
||||
jboolean enabled) {
|
||||
#ifdef HAS_RESHADE
|
||||
BeginFxEdit();
|
||||
Settings::values.post_shader_enabled.SetValue(enabled != JNI_FALSE);
|
||||
#endif
|
||||
}
|
||||
|
||||
jstring Java_org_yuzu_yuzu_1emu_utils_NativePostProcessing_getPresetDirectory(JNIEnv* env,
|
||||
jobject obj) {
|
||||
#ifdef HAS_RESHADE
|
||||
return Common::Android::ToJString(env, VideoCore::GetFxPresetDirectory().string());
|
||||
#else
|
||||
return Common::Android::ToJString(env, "");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?attr/colorOnPrimary" android:state_selected="true" />
|
||||
<item android:color="?attr/colorOnSurface" />
|
||||
</selector>
|
||||
@@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?attr/colorControlHighlight">
|
||||
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@android:color/white" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_selected="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="?attr/colorPrimary" />
|
||||
<corners android:radius="8dp" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="8dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="?attr/colorOutline" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
|
||||
</ripple>
|
||||
@@ -1,10 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M8.5,3 L19.4,3 Q21,3 21,4.6 L21,16.5 L19.4,16.5 L19.4,4.6 L8.5,4.6 Z M5,7 L15,7 Q17,7 17,9 L17,19 Q17,21 15,21 L5,21 Q3,21 3,19 L3,9 Q3,7 5,7 Z M5.2,8.6 L14.8,8.6 Q15.4,8.6 15.4,9.2 L15.4,18.8 Q15.4,19.4 14.8,19.4 L5.2,19.4 Q4.6,19.4 4.6,18.8 L4.6,9.2 Q4.6,8.6 5.2,8.6 Z M10,10.9 A3.1,3.1 0 0 1 10,17.1 Z"/>
|
||||
</vector>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="?attr/colorSurfaceVariant" />
|
||||
|
||||
<corners android:radius="16dp" />
|
||||
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="?attr/colorOutline" />
|
||||
|
||||
</shape>
|
||||
@@ -1,58 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:baselineAligned="false"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/add_button"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:minHeight="48dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/post_processing_add"
|
||||
app:icon="@drawable/ic_add"
|
||||
app:iconPadding="6dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/remove_all_button"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_weight="1"
|
||||
android:minHeight="48dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/post_processing_remove_all"
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_delete"
|
||||
app:iconPadding="6dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RadioGroup
|
||||
android:id="@+id/add_choices"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,111 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="18dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.TitleSmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/frame_gen" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/frame_gen_quick_description"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/frame_gen_multipliers"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="horizontal" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/frame_gen_target_section"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="18dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.TitleSmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/frame_gen_target_rate" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/frame_gen_target_rate_quick_description"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/frame_gen_targets"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="horizontal" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/frame_gen_flow_section"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="18dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.TitleSmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/frame_gen_flow_scale" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:text="@string/frame_gen_flow_scale_quick_description"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/frame_gen_flow"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:orientation="horizontal" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.textview.MaterialTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/TextAppearance.Material3.LabelLarge"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginEnd="6dp"
|
||||
android:background="@drawable/frame_gen_cell_background"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/frame_gen_cell_text" />
|
||||
@@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:background="@drawable/shader_card_background"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/preset_title"
|
||||
style="@style/TextAppearance.Material3.TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/preset_summary"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/preset_switch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="16dp"
|
||||
android:minHeight="48dp" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,82 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:background="@drawable/shader_card_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/shader_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/shader_title"
|
||||
style="@style/TextAppearance.Material3.TitleSmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/shader_summary"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/shader_remove"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="8dp"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:contentDescription="@string/post_processing_remove"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/shader_expand"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:src="@drawable/ic_dropdown_arrow"
|
||||
android:contentDescription=""
|
||||
app:tint="?attr/colorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/shader_body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fx_reset"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@string/post_processing_reset" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingBottom="4dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/fx_slider_title"
|
||||
style="@style/TextAppearance.Material3.TitleSmall"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/fx_slider_value"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
android:id="@+id/fx_slider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:stepSize="1"
|
||||
android:valueFrom="0"
|
||||
android:valueTo="100" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fx_button"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:background="@drawable/shader_card_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/preset_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/preset_name"
|
||||
style="@style/TextAppearance.Material3.TitleSmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/preset_description"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/preset_delete"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/post_processing_preset_delete"
|
||||
android:focusable="true"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:visibility="gone"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,82 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:background="@drawable/shader_card_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/shader_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingTop="14dp"
|
||||
android:paddingBottom="14dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/shader_title"
|
||||
style="@style/TextAppearance.Material3.TitleMedium"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/shader_summary"
|
||||
style="@style/TextAppearance.Material3.BodySmall"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/shader_remove"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/post_processing_remove"
|
||||
android:focusable="true"
|
||||
android:padding="8dp"
|
||||
android:src="@drawable/ic_delete"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/shader_expand"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="4dp"
|
||||
android:contentDescription=""
|
||||
android:src="@drawable/ic_dropdown_arrow"
|
||||
app:tint="?attr/colorPrimary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/shader_body"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginBottom="6dp"
|
||||
android:baselineAligned="false"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fx_add"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:minHeight="48dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/post_processing_add"
|
||||
app:icon="@drawable/ic_add"
|
||||
app:iconPadding="6dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fx_presets"
|
||||
style="@style/Widget.Material3.Button.TonalButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_weight="1.4"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:minHeight="48dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:text="@string/post_processing_presets"
|
||||
app:icon="@drawable/ic_dropdown_arrow"
|
||||
app:iconGravity="textEnd"
|
||||
app:iconPadding="6dp" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fx_create_preset"
|
||||
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:contentDescription="@string/post_processing_preset_new"
|
||||
app:icon="@drawable/ic_add" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/fx_remove_all"
|
||||
style="@style/Widget.Material3.Button.IconButton.Filled.Tonal"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:contentDescription="@string/post_processing_remove_all"
|
||||
app:icon="@drawable/ic_delete" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -305,6 +305,8 @@
|
||||
<string name="frame_gen_target_rate_60">60 إطارًا في الثانية</string>
|
||||
<string name="frame_gen_target_rate_90">90 إطارًا في الثانية</string>
|
||||
<string name="frame_gen_target_rate_120">120 إطارًا في الثانية</string>
|
||||
<string name="frame_gen_target_rate_144">144 إطارًا في الثانية</string>
|
||||
<string name="frame_gen_target_rate_165">165 إطارًا في الثانية</string>
|
||||
<string name="frame_gen_queue_target">هدف قائمة انتظار الإطارات</string>
|
||||
<string name="frame_gen_queue_target_description">كم عدد الإطارات المكتملة التي قد تنتظر قبل عرضها؟ تعمل قوائم الانتظار الأكبر حجمًا على امتصاص الارتفاعات المفاجئة في حمل وحدة معالجة الرسومات على حساب زمن انتقال الإدخال.</string>
|
||||
<string name="frame_gen_queue_target_0">أقل زمن انتقال (بدون تخزين مؤقت)</string>
|
||||
@@ -314,6 +316,10 @@
|
||||
<string name="frame_gen_flow_scale_auto_description">قم بتقدير الحركة بناءً على الدقة التي تعرضها اللعبة فعليًّا، بدلاً من الإخراج الذي تم رفع دقته. ولا يؤثر ذلك على الدقة بأي شكل، لأن رفع الدقة لا يضيف أي تفاصيل تتعلق بالحركة.</string>
|
||||
<string name="frame_gen_flow_scale">دقة تقدير الحركة</string>
|
||||
<string name="frame_gen_flow_scale_description">دقة مسار التدفق البصري، كجزء من الناتج. ويُعد خفض هذه القيمة أرخص طريقة لاستعادة الأداء.</string>
|
||||
<string name="frame_gen_fp16">مُظلِّلات نصف الدقة</string>
|
||||
<string name="frame_gen_fp16_description">استخدم نسخة التظليل 16 بت. يتم التراجع تلقائيًا إلى الخيار البديل في حالة عدم توفرها في برنامج التشغيل أو الملف.</string>
|
||||
<string name="frame_gen_dump_flow">إفراغ الإطار الذي تم إنشاؤه</string>
|
||||
<string name="frame_gen_dump_flow_description">قم بكتابة مستويات MIP للتدفق البصري والإطار المُستكمل إلى مجلد lossless/debug مرة واحدة، لغرض استكشاف الأخطاء وإصلاحها</string>
|
||||
<string name="frame_gen_unsupported">توليد الإطار غير متاح</string>
|
||||
<string name="frame_gen_unsupported_description">لا يدعم برنامج تشغيل وحدة معالجة الرسومات هذا نموذج ذاكرة Vulkan، الذي تتطلبه برامج التظليل الخاصة بـ«Lossless Scaling».</string>
|
||||
<string name="lossless_scaling_setup_description">اختياري. قم بتوفير ملف Lossless.dll الخاص بك لتمكين إنشاء الإطارات لاحقًا</string>
|
||||
|
||||
@@ -304,6 +304,8 @@
|
||||
<string name="frame_gen_target_rate_60">60 FPS</string>
|
||||
<string name="frame_gen_target_rate_90">90 FPS</string>
|
||||
<string name="frame_gen_target_rate_120">120 FPS</string>
|
||||
<string name="frame_gen_target_rate_144">144 FPS</string>
|
||||
<string name="frame_gen_target_rate_165">165 FPS</string>
|
||||
<string name="frame_gen_queue_target_1">Ausbalanciert (1 Bild)</string>
|
||||
<string name="frame_gen_queue_target_2">Flüssigste (2 Bilder)</string>
|
||||
<string name="frame_gen_unsupported">Frame-Generation nicht verfügbar</string>
|
||||
|
||||
@@ -301,9 +301,12 @@
|
||||
<string name="frame_gen_target_rate_60">60 FPS</string>
|
||||
<string name="frame_gen_target_rate_90">90 FPS</string>
|
||||
<string name="frame_gen_target_rate_120">120 FPS</string>
|
||||
<string name="frame_gen_target_rate_144">144 FPS</string>
|
||||
<string name="frame_gen_target_rate_165">165 FPS</string>
|
||||
<string name="frame_gen_queue_target_0">Latencia más baja (Sin búfer)</string>
|
||||
<string name="frame_gen_queue_target_1">Equilibrado (1 fotograma)</string>
|
||||
<string name="frame_gen_queue_target_2">Más suave (2 fotogramas)</string>
|
||||
<string name="frame_gen_fp16">Sombreadores de media precisión</string>
|
||||
<string name="frame_gen_unsupported">Generación de fotogramas no disponbile</string>
|
||||
<string name="lossless_scaling_install">Instalar Lossless.dll</string>
|
||||
<string name="lossless_scaling_replace_description">Seleccionar una copia diferente de Lossless.dll</string>
|
||||
|
||||
@@ -308,6 +308,10 @@
|
||||
<string name="frame_gen_flow_scale_auto_description">Оценивать движение в разрешении, которое игра действительно рендерит, вместо масштабированного вывода. Ничего не стоит в точности, так как масштабирование не добавляет деталей движения.</string>
|
||||
<string name="frame_gen_flow_scale">Разрешение оценки движения</string>
|
||||
<string name="frame_gen_flow_scale_description">Разрешение прохода оптического потока в долях от выходного разрешения. Его понижение — самый дешёвый способ вернуть производительность.</string>
|
||||
<string name="frame_gen_fp16">Шейдеры половинной точности</string>
|
||||
<string name="frame_gen_fp16_description">Использовать 16-битную версию шейдеров. Автоматически переключается на обычную, если драйвер или файл не поддерживают её.</string>
|
||||
<string name="frame_gen_dump_flow">Сохранить сгенерированный кадр</string>
|
||||
<string name="frame_gen_dump_flow_description">Однократно записать уровни мип-карт оптического потока и интерполированный кадр в папку lossless/debug для диагностики.</string>
|
||||
<string name="frame_gen_unsupported">Генерация кадров недоступна</string>
|
||||
<string name="frame_gen_unsupported_description">Драйвер ГПУ не поддерживает модель памяти Vulkan, требуемую шейдерами Lossless Scaling.</string>
|
||||
<string name="lossless_scaling_setup_description">Опционально. Укажите свой Lossless.dll для включения генерации кадров позже.</string>
|
||||
|
||||
@@ -305,6 +305,8 @@
|
||||
<string name="frame_gen_target_rate_60">60 FPS</string>
|
||||
<string name="frame_gen_target_rate_90">90 FPS</string>
|
||||
<string name="frame_gen_target_rate_120">120 FPS</string>
|
||||
<string name="frame_gen_target_rate_144">144 FPS</string>
|
||||
<string name="frame_gen_target_rate_165">165 FPS</string>
|
||||
<string name="frame_gen_queue_target">帧队列目标</string>
|
||||
<string name="frame_gen_queue_target_description">在显示之前有多少已完成渲染的帧正在等待。较大的队列可以缓解 GPU 突发的压力,但会增加输入延迟。</string>
|
||||
<string name="frame_gen_queue_target_0">最低延迟 (无缓冲)</string>
|
||||
@@ -314,6 +316,10 @@
|
||||
<string name="frame_gen_flow_scale_auto_description">在游戏实际渲染的分辨率下估算运动,而不是在放大后的输出上。不会影响精确性,因为放大并不会增加运动细节。</string>
|
||||
<string name="frame_gen_flow_scale">运动预估分辨率</string>
|
||||
<string name="frame_gen_flow_scale_description">光流通道的分辨率,以输出的比例表示。降低它是提升性能最经济的做法。</string>
|
||||
<string name="frame_gen_fp16">半精度着色器</string>
|
||||
<string name="frame_gen_fp16_description">使用 16 位着色器变体。如果驱动或文件不支持会自动回退。</string>
|
||||
<string name="frame_gen_dump_flow">转储已生成的帧</string>
|
||||
<string name="frame_gen_dump_flow_description">为了排查问题,把光流 mip 级别和插值帧写入 lossless/debug 文件夹一次</string>
|
||||
<string name="frame_gen_unsupported">帧生成不可用</string>
|
||||
<string name="frame_gen_unsupported_description">这个 GPU 驱动不支持无损缩放着色器所需的 Vulkan 内存模型。</string>
|
||||
<string name="lossless_scaling_setup_description">作为可选项。请提供您自己的 Lossless.dll 以便在之后可以启用帧生成。</string>
|
||||
|
||||
@@ -305,6 +305,8 @@
|
||||
<string name="frame_gen_target_rate_60">60 FPS</string>
|
||||
<string name="frame_gen_target_rate_90">90 FPS</string>
|
||||
<string name="frame_gen_target_rate_120">120 FPS</string>
|
||||
<string name="frame_gen_target_rate_144">144 FPS</string>
|
||||
<string name="frame_gen_target_rate_165">165 FPS</string>
|
||||
<string name="frame_gen_queue_target">影格佇列目標</string>
|
||||
<string name="frame_gen_queue_target_description">最多允許多少個已完成的影格在顯示器前等待,較大的佇列可以吸收 GPU 突發負載,但會增加輸入延遲</string>
|
||||
<string name="frame_gen_queue_target_0">最低延遲(無緩衝)</string>
|
||||
@@ -314,6 +316,10 @@
|
||||
<string name="frame_gen_flow_scale_auto_description">以遊戲實際渲染的解析度而非升頻後的輸出進行運動預測。由於升頻後不會增加任何動態細節,因此不會影響準確度</string>
|
||||
<string name="frame_gen_flow_scale">運動預測解析度</string>
|
||||
<string name="frame_gen_flow_scale_description">光流處理的解析度,以輸出解析度的比例表示。降低此設定值是減少效能負載最有效的方法</string>
|
||||
<string name="frame_gen_fp16">半準確著色器</string>
|
||||
<string name="frame_gen_fp16_description">使用16位元著色器。如果驅動程式或著色器檔案不支援則會自動切換成其它版本</string>
|
||||
<string name="frame_gen_dump_flow">傾印生成的著色器</string>
|
||||
<string name="frame_gen_dump_flow_description">將光流的 MIP 層級與補間影格寫入 Eden 資料夾中的 lossless\debug 資料夾以便進行疑難排解</string>
|
||||
<string name="frame_gen_unsupported">無法使用影格生成</string>
|
||||
<string name="frame_gen_unsupported_description">Lossless Scaling 的著色器需要 Vulkan 記憶體模型,所選的驅動程式不支援該功能</string>
|
||||
<string name="lossless_scaling_setup_description">可選擇安裝自己擁有的 Lossless.dll 以在之後啟用影格生成功能</string>
|
||||
|
||||
@@ -174,6 +174,8 @@
|
||||
<item>@string/frame_gen_target_rate_60</item>
|
||||
<item>@string/frame_gen_target_rate_90</item>
|
||||
<item>@string/frame_gen_target_rate_120</item>
|
||||
<item>@string/frame_gen_target_rate_144</item>
|
||||
<item>@string/frame_gen_target_rate_165</item>
|
||||
</string-array>
|
||||
|
||||
<integer-array name="frameGenTargetRateValues">
|
||||
@@ -181,12 +183,8 @@
|
||||
<item>60</item>
|
||||
<item>90</item>
|
||||
<item>120</item>
|
||||
</integer-array>
|
||||
|
||||
<integer-array name="frameGenFlowScaleValues">
|
||||
<item>50</item>
|
||||
<item>75</item>
|
||||
<item>100</item>
|
||||
<item>144</item>
|
||||
<item>165</item>
|
||||
</integer-array>
|
||||
|
||||
<string-array name="frameGenQueueTargetNames">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<!-- General application strings -->
|
||||
<string name="app_name" translatable="false">Eden</string>
|
||||
<string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.<br /><br />Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.<br /><br /><![CDATA[<a href=\"https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/user/QuickStart.md\">Learn more</a>]]></string>
|
||||
<string name="app_disclaimer">This software will run games for the Nintendo Switch game console. No game titles or keys are included.<br /><br />Before you begin, please locate your <![CDATA[<b> prod.keys </b>]]> file on your device storage.<br /><br /><![CDATA[<a href=\"https://yuzu-mirror.github.io/help/quickstart\">Learn more</a>]]></string>
|
||||
<string name="notice_notification_channel_name">Notices and errors</string>
|
||||
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
|
||||
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</string>
|
||||
@@ -298,25 +298,6 @@
|
||||
<string name="gpu_driver_fetcher">GPU driver fetcher</string>
|
||||
<string name="gpu_driver_manager">GPU driver manager</string>
|
||||
<string name="install_gpu_driver_description">Install alternative drivers for potentially better performance or accuracy</string>
|
||||
<string name="post_processing">Post-Processing Effects</string>
|
||||
<string name="post_processing_description">ReShade FX effects applied after rendering</string>
|
||||
<string name="post_processing_per_game_description">Configure the effect chain for this game</string>
|
||||
<string name="post_processing_add">Add effect</string>
|
||||
<string name="post_processing_remove">Remove</string>
|
||||
<string name="post_processing_open_list">Open list</string>
|
||||
<string name="post_processing_close_list">Close list</string>
|
||||
<string name="post_processing_remove_all">Remove effects</string>
|
||||
<string name="post_processing_preset_locked">Preset active. Edit it in Post-Processing Effects before starting a game.</string>
|
||||
<string name="post_processing_preset_modified">Values changed from the original preset.</string>
|
||||
<string name="post_processing_presets">Presets</string>
|
||||
<string name="post_processing_preset_new">New preset</string>
|
||||
<string name="post_processing_preset_new_description">Saves the effects you have loaded, with their current values, as a preset you can pick later.</string>
|
||||
<string name="post_processing_preset_name_invalid">Give the preset a name without an equals sign.</string>
|
||||
<string name="post_processing_preset_none">No preset</string>
|
||||
<string name="post_processing_preset_delete">Delete preset</string>
|
||||
<string name="post_processing_preset_reset">Reset preset values</string>
|
||||
<string name="post_processing_reset">Reset to default</string>
|
||||
<string name="post_processing_empty">No effects found. Place .fx files in this folder:</string>
|
||||
<string name="frame_gen">Frame generation</string>
|
||||
<string name="frame_gen_per_game_description">Configure frame generation for this game</string>
|
||||
<string name="frame_gen_description">Insert interpolated frames between rendered ones using Lossless Scaling. Forces FIFO presentation while enabled.</string>
|
||||
@@ -331,13 +312,8 @@
|
||||
<string name="frame_gen_target_rate_60">60 FPS</string>
|
||||
<string name="frame_gen_target_rate_90">90 FPS</string>
|
||||
<string name="frame_gen_target_rate_120">120 FPS</string>
|
||||
<string name="frame_gen_on">On</string>
|
||||
<string name="frame_gen_off">Off</string>
|
||||
<string name="frame_gen_fixed">Fixed</string>
|
||||
<string name="frame_gen_flow_auto">Auto</string>
|
||||
<string name="frame_gen_quick_description">Turn frame generation on or off and choose the frame multiplier.</string>
|
||||
<string name="frame_gen_target_rate_quick_description">Generate frames up to a target frame rate. The multiplier adjusts on its own.</string>
|
||||
<string name="frame_gen_flow_scale_quick_description">Resolution used to estimate motion between frames. Lower values save GPU time.</string>
|
||||
<string name="frame_gen_target_rate_144">144 FPS</string>
|
||||
<string name="frame_gen_target_rate_165">165 FPS</string>
|
||||
<string name="frame_gen_queue_target">Frame queue target</string>
|
||||
<string name="frame_gen_queue_target_description">How many finished frames may wait ahead of the display. Larger queues absorb GPU spikes at the cost of input latency.</string>
|
||||
<string name="frame_gen_queue_target_0">Lowest latency (Unbuffered)</string>
|
||||
@@ -347,15 +323,19 @@
|
||||
<string name="frame_gen_flow_scale_auto_description">Estimate motion at the resolution the game actually renders instead of the upscaled output. Costs nothing in accuracy, since upscaling adds no motion detail.</string>
|
||||
<string name="frame_gen_flow_scale">Motion estimation resolution</string>
|
||||
<string name="frame_gen_flow_scale_description">Resolution of the optical flow pass, as a fraction of the output. Lowering it is the cheapest way to reclaim performance.</string>
|
||||
<string name="frame_gen_fp16">Half precision shaders</string>
|
||||
<string name="frame_gen_fp16_description">Use the 16-bit shader variant. Falls back automatically if the driver or the file lacks it.</string>
|
||||
<string name="frame_gen_dump_flow">Dump generated frame</string>
|
||||
<string name="frame_gen_dump_flow_description">Write the optical flow mip levels and the interpolated frame to the lossless/debug folder once, for troubleshooting</string>
|
||||
<string name="frame_gen_unsupported">Frame generation unavailable</string>
|
||||
<string name="frame_gen_unsupported_description">This GPU driver lacks the Vulkan memory model or half precision (float16) support that the Lossless Scaling shaders require.</string>
|
||||
<string name="frame_gen_unsupported_description">This GPU driver does not support the Vulkan memory model, which the Lossless Scaling shaders require.</string>
|
||||
<string name="lossless_scaling_setup_description">Optional. Provide your own Lossless.dll to enable frame generation later</string>
|
||||
<string name="lossless_scaling_install">Install Lossless.dll</string>
|
||||
<string name="lossless_scaling_install_description">Frame generation needs your own legal copy of Lossless.dll from Lossless Scaling</string>
|
||||
<string name="lossless_scaling_replace_description">Select a different copy of Lossless.dll</string>
|
||||
<string name="frame_generation_support">Frame generation</string>
|
||||
<string name="frame_generation_supported">Supported</string>
|
||||
<string name="frame_generation_unsupported">Unsupported (no Vulkan memory model or float16)</string>
|
||||
<string name="frame_generation_unsupported">Unsupported (no Vulkan memory model)</string>
|
||||
<string name="lossless_scaling">Lossless Scaling</string>
|
||||
<string name="lossless_scaling_description">Provide your own copy of Lossless.dll to enable frame generation</string>
|
||||
<string name="lossless_scaling_installed">Installed</string>
|
||||
@@ -953,7 +933,7 @@
|
||||
<string name="loader_error_file_not_found">ROM file does not exist</string>
|
||||
|
||||
<string name="loader_requires_firmware">Game Requires Firmware</string>
|
||||
<string name="loader_requires_firmware_description"><![CDATA[This game may require firmware to function properly, and you don\'t have any installed. Please install firmware before launching, or press \"OK\" to launch anyways.]]></string>
|
||||
<string name="loader_requires_firmware_description"><![CDATA[The game you are trying to launch requires firmware to boot or to get past the opening menu. Please <a href=\"https://yuzu-mirror.github.io/help/quickstart\"> dump and install firmware</a>, or press \"OK\" to launch anyways.]]></string>
|
||||
|
||||
<!-- Intent Launch strings -->
|
||||
<string name="searching_for_game">Searching for game...</string>
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "audio_core/opus/hardware_opus.h"
|
||||
#include "audio_core/opus/parameters.h"
|
||||
#include "common/alignment.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/core.h"
|
||||
|
||||
@@ -29,18 +28,10 @@ OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
|
||||
OpusDecoder::~OpusDecoder() {
|
||||
if (decode_object_initialized) {
|
||||
hardware_opus.ShutdownDecodeObject(shared_buffer.data(), shared_buffer.size());
|
||||
hardware_opus.UnregisterDecoder(this);
|
||||
}
|
||||
}
|
||||
|
||||
Result OpusDecoder::Initialize(const OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
|
||||
R_TRY(hardware_opus.RegisterDecoder(this));
|
||||
SCOPE_EXIT {
|
||||
if (!decode_object_initialized) {
|
||||
hardware_opus.UnregisterDecoder(this);
|
||||
}
|
||||
};
|
||||
|
||||
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||
shared_buffer.resize(transfer_memory_size);
|
||||
shared_memory_mapped = true;
|
||||
@@ -70,13 +61,6 @@ Result OpusDecoder::Initialize(const OpusParametersEx& params, Kernel::KTransfer
|
||||
}
|
||||
|
||||
Result OpusDecoder::Initialize(const OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) {
|
||||
R_TRY(hardware_opus.RegisterDecoder(this));
|
||||
SCOPE_EXIT {
|
||||
if (!decode_object_initialized) {
|
||||
hardware_opus.UnregisterDecoder(this);
|
||||
}
|
||||
};
|
||||
|
||||
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||
shared_buffer.resize(transfer_memory_size, 0);
|
||||
shared_memory_mapped = true;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "audio_core/audio_core.h"
|
||||
@@ -46,25 +45,6 @@ HardwareOpus::HardwareOpus(Core::System& system_)
|
||||
opus_decoder.SetSharedMemory(shared_memory);
|
||||
}
|
||||
|
||||
Result HardwareOpus::RegisterDecoder(OpusDecoder* decoder) {
|
||||
std::scoped_lock l{mutex};
|
||||
const auto slot = std::ranges::find(decoders, nullptr);
|
||||
if (slot == decoders.end()) {
|
||||
R_THROW(ResultOutOfOpusDecoders);
|
||||
}
|
||||
*slot = decoder;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void HardwareOpus::UnregisterDecoder(OpusDecoder* decoder) {
|
||||
std::scoped_lock l{mutex};
|
||||
const auto slot = std::ranges::find(decoders, decoder);
|
||||
if (slot == decoders.end()) {
|
||||
return;
|
||||
}
|
||||
*slot = nullptr;
|
||||
}
|
||||
|
||||
u32 HardwareOpus::GetWorkBufferSize(u32 channel) {
|
||||
if (!opus_decoder.IsRunning()) {
|
||||
return 0;
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mutex>
|
||||
#include <opus.h>
|
||||
|
||||
@@ -16,12 +12,9 @@
|
||||
#include "core/hle/service/audio/errors.h"
|
||||
|
||||
namespace AudioCore::OpusDecoder {
|
||||
class OpusDecoder;
|
||||
class HardwareOpus {
|
||||
public:
|
||||
HardwareOpus(Core::System& system);
|
||||
Result RegisterDecoder(OpusDecoder* decoder);
|
||||
void UnregisterDecoder(OpusDecoder* decoder);
|
||||
|
||||
u32 GetWorkBufferSize(u32 channel);
|
||||
u32 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
|
||||
@@ -46,7 +39,6 @@ public:
|
||||
private:
|
||||
Core::System& system;
|
||||
std::mutex mutex;
|
||||
std::array<OpusDecoder*, 24> decoders{};
|
||||
ADSP::OpusDecoder::OpusDecoder& opus_decoder;
|
||||
ADSP::OpusDecoder::SharedMemory shared_memory;
|
||||
};
|
||||
|
||||
@@ -89,6 +89,7 @@ add_library(
|
||||
param_package.h
|
||||
parent_of_member.h
|
||||
point.h
|
||||
quaternion.h
|
||||
range_map.h
|
||||
range_mutex.h
|
||||
range_sets.h
|
||||
@@ -108,8 +109,6 @@ add_library(
|
||||
settings_setting.h
|
||||
slot_vector.h
|
||||
socket_types.h
|
||||
sparse_large_vector.cpp
|
||||
sparse_large_vector.h
|
||||
spin_lock.h
|
||||
stb.cpp
|
||||
stb.h
|
||||
@@ -139,6 +138,8 @@ add_library(
|
||||
uuid.cpp
|
||||
uuid.h
|
||||
vector_math.h
|
||||
virtual_buffer.cpp
|
||||
virtual_buffer.h
|
||||
zstd_compression.cpp
|
||||
zstd_compression.h
|
||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/fiber.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
#include <boost/context/detail/fcontext.hpp>
|
||||
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
#define LOSSLESS_DIR "lossless"
|
||||
#define NAND_DIR "nand"
|
||||
#define PLAY_TIME_DIR "play_time"
|
||||
#define POST_PRESET_DIR "post_presets"
|
||||
#define POST_SHADER_DIR "post_shaders"
|
||||
#define SCREENSHOTS_DIR "screenshots"
|
||||
#define SDMC_DIR "sdmc"
|
||||
#define SHADER_DIR "shader"
|
||||
|
||||
@@ -160,8 +160,6 @@ public:
|
||||
GenerateEdenPath(EdenPath::LosslessDir, eden_path / LOSSLESS_DIR);
|
||||
GenerateEdenPath(EdenPath::NANDDir, eden_path / NAND_DIR);
|
||||
GenerateEdenPath(EdenPath::PlayTimeDir, eden_path / PLAY_TIME_DIR);
|
||||
GenerateEdenPath(EdenPath::PostPresetDir, eden_path / POST_PRESET_DIR);
|
||||
GenerateEdenPath(EdenPath::PostShaderDir, eden_path / POST_SHADER_DIR);
|
||||
GenerateEdenPath(EdenPath::SaveDir, eden_path / NAND_DIR);
|
||||
GenerateEdenPath(EdenPath::ScreenshotsDir, eden_path / SCREENSHOTS_DIR);
|
||||
GenerateEdenPath(EdenPath::SDMCDir, eden_path / SDMC_DIR);
|
||||
|
||||
@@ -26,8 +26,6 @@ enum class EdenPath {
|
||||
LosslessDir, // Where the user-supplied Lossless Scaling library is stored.
|
||||
NANDDir, // Where the emulated NAND is stored.
|
||||
PlayTimeDir, // Where play time data is stored.
|
||||
PostPresetDir,
|
||||
PostShaderDir, // Where user post-processing shaders are stored.
|
||||
SaveDir, // Where save data is stored.
|
||||
ScreenshotsDir, // Where yuzu screenshots are stored.
|
||||
SDMCDir, // Where the emulated SDMC is stored.
|
||||
|
||||
+14
-41
@@ -117,6 +117,7 @@ template <typename T>
|
||||
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
|
||||
if (!dll.GetSymbol(name, &pfn)) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
|
||||
throw std::bad_alloc{};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,14 +179,6 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
void* Allocate(size_t size) {
|
||||
auto* ptr = VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
|
||||
if (ptr == nullptr) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, error {}", size, GetLastError());
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||
std::unique_lock lock{placeholder_mutex};
|
||||
if (!IsNiechePlaceholder(virtual_offset, length)) {
|
||||
@@ -406,10 +399,6 @@ private:
|
||||
// For managarm: see https://github.com/managarm/managarm/issues/1370
|
||||
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
||||
|
||||
#ifndef MAP_NOCORE
|
||||
#define MAP_NOCORE 0
|
||||
#endif
|
||||
|
||||
#ifdef ARCHITECTURE_arm64
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
@@ -434,7 +423,7 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
|
||||
void* map_pointer =
|
||||
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0);
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
|
||||
// If we successfully mapped, we're done.
|
||||
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
|
||||
@@ -454,11 +443,11 @@ static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
|
||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER | MAP_NOCORE, -1, 0);
|
||||
void* virtual_base = mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_ALIGNED_SUPER, -1, 0);
|
||||
if (virtual_base != MAP_FAILED)
|
||||
return virtual_base;
|
||||
#endif
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE | MAP_NOCORE, -1, 0);
|
||||
return mmap(nullptr, virtual_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -552,13 +541,13 @@ public:
|
||||
}
|
||||
if (use_anon) {
|
||||
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NOCORE, -1, 0));
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0));
|
||||
if (fd > 0) {
|
||||
fd = -1;
|
||||
close(fd);
|
||||
}
|
||||
} else {
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_NOCORE, fd, 0));
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
|
||||
}
|
||||
if (backing_base == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||
@@ -582,14 +571,6 @@ public:
|
||||
Release();
|
||||
}
|
||||
|
||||
void* Allocate(size_t size) {
|
||||
auto* ptr = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (ptr == MAP_FAILED) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to allocate fallback buffer with size {:#x}, {}", size, strerror(errno));
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||
// Intersect the range with our address space.
|
||||
AdjustMap(&virtual_offset, &length);
|
||||
@@ -710,10 +691,12 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
{
|
||||
#if defined(__OPENORBIS__) || defined(__managarm__)
|
||||
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
|
||||
backing_base = static_cast<u8*>(mmap(nullptr, backing_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
fallback_buffer.emplace(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
virtual_base = nullptr;
|
||||
#else
|
||||
// Try to allocate a fastmem arena.
|
||||
// The implementation will fail with std::bad_alloc on errors.
|
||||
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
||||
if (impl->Init()) {
|
||||
backing_base = impl->backing_base;
|
||||
@@ -724,26 +707,16 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
||||
virtual_base_offset = virtual_base - impl->virtual_base;
|
||||
}
|
||||
} else {
|
||||
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
|
||||
fallback_buffer = true;
|
||||
backing_base = static_cast<u8*>(impl->Allocate(backing_size));
|
||||
virtual_base = nullptr;
|
||||
impl.reset();
|
||||
LOG_WARNING(HW_Memory, "Platform can support fastmem, but can't create it");
|
||||
fallback_buffer.emplace(backing_size);
|
||||
backing_base = fallback_buffer->data();
|
||||
virtual_base = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
HostMemory::~HostMemory() {
|
||||
#ifdef _WIN32
|
||||
if (fallback_buffer) {
|
||||
VirtualFree(backing_base, backing_size, MEM_RELEASE);
|
||||
}
|
||||
#else
|
||||
if (fallback_buffer) {
|
||||
munmap(backing_base, backing_size);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
HostMemory::~HostMemory() = default;
|
||||
|
||||
HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <optional>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -85,7 +86,7 @@ private:
|
||||
u8* virtual_base{};
|
||||
size_t virtual_base_offset{};
|
||||
// Windows requires it for kernels whom lack proper support for some functions!
|
||||
bool fallback_buffer{false};
|
||||
std::optional<Common::VirtualBuffer<u8>> fallback_buffer;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||
@@ -13,11 +13,39 @@ PageTable::PageTable() = default;
|
||||
|
||||
PageTable::~PageTable() noexcept = default;
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_bits) {
|
||||
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_bits);
|
||||
entries.ResizeAndClear(num_page_table_entries);
|
||||
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const {
|
||||
out_context->next_offset = GetInteger(address);
|
||||
out_context->next_page = address / page_size;
|
||||
|
||||
return this->ContinueTraversal(out_entry, out_context);
|
||||
}
|
||||
|
||||
bool PageTable::ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const {
|
||||
// Setup invalid defaults.
|
||||
out_entry->phys_addr = 0;
|
||||
out_entry->block_size = page_size;
|
||||
// Validate that we can read the actual entry.
|
||||
if (auto const page = context->next_page; page < entries.size()) {
|
||||
// Validate that the entry is mapped.
|
||||
if (auto const paddr = entries[page].addr; paddr != 0) {
|
||||
// Populate the results.
|
||||
out_entry->phys_addr = paddr + context->next_offset;
|
||||
context->next_page += 1;
|
||||
context->next_offset += page_size;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
context->next_page += 1;
|
||||
context->next_offset += page_size;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits) {
|
||||
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_size_in_bits);
|
||||
entries.resize(num_page_table_entries);
|
||||
current_address_space_width_in_bits = address_space_width_in_bits;
|
||||
current_page_bits = page_bits;
|
||||
page_size = 1ULL << page_size_in_bits;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
+56
-64
@@ -9,22 +9,22 @@
|
||||
#include <atomic>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
#include "common/typed_address.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
enum class PageType : u8 {
|
||||
/// Page is unmapped and should cause an access error.
|
||||
Unmapped = 0b00,
|
||||
Unmapped,
|
||||
/// Page is mapped to regular memory. This is the only type you can get pointers to.
|
||||
Memory = 0b01,
|
||||
Memory,
|
||||
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
|
||||
/// the callbacks.
|
||||
DebugMemory = 0b10,
|
||||
DebugMemory,
|
||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||
/// invalidation
|
||||
RasterizerCachedMemory = 0b11,
|
||||
RasterizerCachedMemory,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -42,86 +42,57 @@ struct PageTable {
|
||||
u64 next_offset{};
|
||||
};
|
||||
|
||||
/// Masks out bits reserved for attribute tagging.
|
||||
static constexpr u64 ATTRIBUTE_MASK = ((1ULL << 44) - 1) << 12;
|
||||
|
||||
/// Specifies sign bit for page table entries.
|
||||
static constexpr u64 SIGN_BIT = 45 + 12; // 44 bits of data + page offset
|
||||
/// Number of bits reserved for attribute tagging.
|
||||
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||
static constexpr int ATTRIBUTE_BITS = 2;
|
||||
|
||||
/**
|
||||
* Atomic tuple of host pointer, page type, and block id.
|
||||
* This uses the lower bits of a given pointer to store the attributes.
|
||||
* Pair of host pointer and page type attribute.
|
||||
* This uses the lower bits of a given pointer to store the attribute tag.
|
||||
* Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
|
||||
* call. In other words, they are guaranteed to be synchronized at all times.
|
||||
*/
|
||||
class PageEntryData {
|
||||
class PageInfo {
|
||||
public:
|
||||
struct Data {
|
||||
Data(bool marked_, PageType type_, u16 block_, u64 page_)
|
||||
: marked(static_cast<u64>(marked_) & 0b1)
|
||||
, type(static_cast<u64>(type_) & ((1ULL << 2) - 1))
|
||||
, block(static_cast<u64>(block_) & ((1ULL << 9) - 1))
|
||||
, page((page_ >> 12) & ((1ULL << 45) - 1))
|
||||
, block2((static_cast<u64>(block_) >> 9) & ((1ULL << 7) - 1)) {}
|
||||
u64 marked : 1;
|
||||
u64 type : 2;
|
||||
u64 block : 9;
|
||||
u64 page : 45; // 44 bits of actual data (64 - page offset (12) - reserved (8)) + a sign bit
|
||||
u64 block2 : 7;
|
||||
};
|
||||
|
||||
[[nodiscard]] Data Raw() const noexcept {
|
||||
return std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Returns the page pointer
|
||||
[[nodiscard]] uintptr_t Pointer(bool ignored_marked = false) const noexcept {
|
||||
return ExtractPointer(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)), ignored_marked);
|
||||
[[nodiscard]] uintptr_t Pointer() const noexcept {
|
||||
return ExtractPointer(raw.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Returns the page type attribute
|
||||
[[nodiscard]] PageType Type() const noexcept {
|
||||
return static_cast<PageType>(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)).type);
|
||||
}
|
||||
|
||||
/// Returns the block identifier.
|
||||
[[nodiscard]] u16 Block() const noexcept {
|
||||
return ExtractBlock(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)));
|
||||
return ExtractType(raw.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
||||
[[nodiscard]] std::tuple<uintptr_t, PageType, u16> PointerTypeBlock(bool ignore_marked = false) const noexcept {
|
||||
const auto non_atomic_raw = std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
||||
return {ExtractPointer(non_atomic_raw, ignore_marked), static_cast<PageType>(non_atomic_raw.type), ExtractBlock(non_atomic_raw)};
|
||||
[[nodiscard]] std::pair<uintptr_t, PageType> PointerType() const noexcept {
|
||||
const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
|
||||
return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
|
||||
}
|
||||
|
||||
/// Write page info atomically
|
||||
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
||||
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
||||
/// Returns the raw representation of the page information.
|
||||
/// Use ExtractPointer and ExtractType to unpack the value.
|
||||
[[nodiscard]] uintptr_t Raw() const noexcept {
|
||||
return raw.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
constexpr void MarkRasterizerCached() noexcept {
|
||||
data_raw.fetch_or(0b111);
|
||||
}
|
||||
|
||||
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
||||
Store(true, PageType::DebugMemory, block, ptr);
|
||||
/// Write a page pointer and type pair atomically
|
||||
void Store(uintptr_t pointer, PageType type) noexcept {
|
||||
raw.store(pointer | uintptr_t(type));
|
||||
}
|
||||
|
||||
/// Unpack a pointer from a page info raw representation
|
||||
[[nodiscard]] static uintptr_t ExtractPointer(Data raw, bool ignore_marked = false) noexcept {
|
||||
return raw.marked && !ignore_marked ? 0
|
||||
// shift raw.page's fake sign bit to the actual sign bit, then sign extend
|
||||
: ((s64)(raw.page << (64 - 44))) >> (64 - 44 - 12);
|
||||
[[nodiscard]] static uintptr_t ExtractPointer(uintptr_t raw) noexcept {
|
||||
return raw & (~uintptr_t{0} << ATTRIBUTE_BITS);
|
||||
}
|
||||
|
||||
[[nodiscard]] static u16 ExtractBlock(Data raw) noexcept {
|
||||
return static_cast<u16>(raw.block | (raw.block2 << 9));
|
||||
/// Unpack a page type from a page info raw representation
|
||||
[[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
|
||||
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
|
||||
}
|
||||
|
||||
private:
|
||||
std::atomic<u64> data_raw;
|
||||
static_assert(sizeof(Data) == sizeof(std::atomic<u64>));
|
||||
std::atomic<uintptr_t> raw;
|
||||
};
|
||||
|
||||
PageTable();
|
||||
@@ -129,8 +100,13 @@ struct PageTable {
|
||||
|
||||
PageTable(const PageTable&) = delete;
|
||||
PageTable& operator=(const PageTable&) = delete;
|
||||
PageTable(PageTable&&) noexcept = delete;
|
||||
PageTable& operator=(PageTable&&) noexcept = delete;
|
||||
|
||||
PageTable(PageTable&&) noexcept = default;
|
||||
PageTable& operator=(PageTable&&) noexcept = default;
|
||||
|
||||
bool BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||
Common::ProcessAddress address) const;
|
||||
bool ContinueTraversal(TraversalEntry* out_entry, TraversalContext* context) const;
|
||||
|
||||
/**
|
||||
* Resizes the page table to be able to accommodate enough pages within
|
||||
@@ -145,14 +121,30 @@ struct PageTable {
|
||||
return current_address_space_width_in_bits;
|
||||
}
|
||||
|
||||
bool GetPhysicalAddress(Common::PhysicalAddress* out_phys_addr,
|
||||
Common::ProcessAddress virt_addr) const {
|
||||
if (virt_addr > (1ULL << this->GetAddressSpaceBits())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*out_phys_addr = entries[virt_addr / page_size].addr + GetInteger(virt_addr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||
/// corresponding attribute element is of type `Memory`.
|
||||
SparseLargeVector<PageEntryData> entries;
|
||||
static_assert(sizeof(PageEntryData) == 8);
|
||||
struct PageEntryData {
|
||||
PageInfo ptr;
|
||||
u64 block;
|
||||
u64 addr;
|
||||
u64 padding;
|
||||
};
|
||||
VirtualBuffer<PageEntryData> entries;
|
||||
static_assert(sizeof(PageEntryData) == 32);
|
||||
|
||||
u8* fastmem_arena{};
|
||||
std::size_t current_address_space_width_in_bits{};
|
||||
std::size_t current_page_bits{};
|
||||
std::size_t page_size{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/vector_math.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
class Quaternion {
|
||||
public:
|
||||
Vec3<T> xyz;
|
||||
T w{};
|
||||
|
||||
[[nodiscard]] Quaternion<decltype(-T{})> Inverse() const {
|
||||
return {-xyz, w};
|
||||
}
|
||||
|
||||
[[nodiscard]] Quaternion<decltype(T{} + T{})> operator+(const Quaternion& other) const {
|
||||
return {xyz + other.xyz, w + other.w};
|
||||
}
|
||||
|
||||
[[nodiscard]] Quaternion<decltype(T{} - T{})> operator-(const Quaternion& other) const {
|
||||
return {xyz - other.xyz, w - other.w};
|
||||
}
|
||||
|
||||
[[nodiscard]] Quaternion<decltype(T{} * T{} - T{} * T{})> operator*(
|
||||
const Quaternion& other) const {
|
||||
return {xyz * other.w + other.xyz * w + Cross(xyz, other.xyz),
|
||||
w * other.w - Dot(xyz, other.xyz)};
|
||||
}
|
||||
|
||||
[[nodiscard]] Quaternion<T> Normalized() const {
|
||||
T length = std::sqrt(xyz.Length2() + w * w);
|
||||
return {xyz / length, w / length};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
|
||||
const T x2 = xyz[0] * xyz[0];
|
||||
const T y2 = xyz[1] * xyz[1];
|
||||
const T z2 = xyz[2] * xyz[2];
|
||||
|
||||
const T xy = xyz[0] * xyz[1];
|
||||
const T wz = w * xyz[2];
|
||||
const T xz = xyz[0] * xyz[2];
|
||||
const T wy = w * xyz[1];
|
||||
const T yz = xyz[1] * xyz[2];
|
||||
const T wx = w * xyz[0];
|
||||
|
||||
return {1.0f - 2.0f * (y2 + z2),
|
||||
2.0f * (xy + wz),
|
||||
2.0f * (xz - wy),
|
||||
0.0f,
|
||||
2.0f * (xy - wz),
|
||||
1.0f - 2.0f * (x2 + z2),
|
||||
2.0f * (yz + wx),
|
||||
0.0f,
|
||||
2.0f * (xz + wy),
|
||||
2.0f * (yz - wx),
|
||||
1.0f - 2.0f * (x2 + y2),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] auto QuaternionRotate(const Quaternion<T>& q, const Vec3<T>& v) {
|
||||
return v + 2 * Cross(q.xyz, Cross(q.xyz, v) + v * q.w);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline Quaternion<float> MakeQuaternion(const Vec3<float>& axis, float angle) {
|
||||
return {axis * std::sin(angle / 2), std::cos(angle / 2)};
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
+6
-27
@@ -388,30 +388,6 @@ struct Values {
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<std::string> post_shader_chain{linkage,
|
||||
std::string(),
|
||||
"post_shader_chain",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<std::string> post_shader_preset{linkage,
|
||||
std::string(),
|
||||
"post_shader_preset",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<bool> post_shader_enabled{linkage,
|
||||
true,
|
||||
"post_shader_enabled",
|
||||
Category::Renderer,
|
||||
Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer,
|
||||
Specialization::Default, true, false};
|
||||
|
||||
@@ -459,7 +435,7 @@ struct Values {
|
||||
&frame_gen};
|
||||
|
||||
SwitchableSetting<u32, true> frame_gen_queue_target{linkage,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
"frame_gen_queue_target",
|
||||
@@ -469,6 +445,9 @@ struct Values {
|
||||
false,
|
||||
&frame_gen};
|
||||
|
||||
SwitchableSetting<bool> frame_gen_fp16{linkage, true, "frame_gen_fp16", Category::Renderer,
|
||||
Specialization::Default, true, false, &frame_gen};
|
||||
|
||||
SwitchableSetting<bool> frame_gen_dump_flow{linkage, false, "frame_gen_dump_flow",
|
||||
Category::Renderer};
|
||||
|
||||
@@ -879,7 +858,7 @@ struct Values {
|
||||
SwitchableSetting<std::string> program_args{linkage,
|
||||
std::string(),
|
||||
"program_args",
|
||||
Category::Debugging,
|
||||
Category::System,
|
||||
Specialization::Default,
|
||||
true, // save_ - persist in config file
|
||||
false}; // runtime_modifiable_ - startup-only
|
||||
@@ -925,7 +904,7 @@ struct Values {
|
||||
0,
|
||||
65535,
|
||||
"debug_knobs",
|
||||
Category::Debugging,
|
||||
Category::System,
|
||||
Specialization::Countable,
|
||||
true,
|
||||
true};
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* virtual_buffer.cpp */
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <mutex>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/sparse_large_vector.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
static std::vector<std::pair<u64, u64>> vector_regions {};
|
||||
|
||||
// Workaround for handling non-commited memory accessed by Dynarmic; usually result of an error
|
||||
static LONG WINAPI FakePageFaultHandler(PEXCEPTION_POINTERS info) {
|
||||
DWORD code = info->ExceptionRecord->ExceptionCode;
|
||||
u64 exception_addr = reinterpret_cast<u64>(info->ExceptionRecord->ExceptionAddress);
|
||||
|
||||
if (code != EXCEPTION_ACCESS_VIOLATION) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
u64 addr = 0, addr2 = 0;
|
||||
|
||||
for (auto region: vector_regions) {
|
||||
auto addr_shifted = exception_addr >> HostPageBits;
|
||||
if (region.first <= addr_shifted && addr_shifted <= region.second) {
|
||||
addr = addr_shifted;
|
||||
}
|
||||
|
||||
// Page-boundary accesses
|
||||
if (auto addr_ = (exception_addr + 0x40) >> HostPageBits; addr_ != addr_shifted && region.first <= addr_ && addr_ <= region.second) {
|
||||
addr2 = addr_;
|
||||
}
|
||||
|
||||
if (addr != 0 || addr2 != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (addr == 0 && addr2 == 0) {
|
||||
// Not our problem
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
LOG_ERROR(HW_Memory, "Accessing an unallocated region of a SparseLargeVector at {:#x}; this shouldn't happen and is likely a Dynarmic error!", exception_addr);
|
||||
|
||||
// Commit this region
|
||||
if (addr != 0) {
|
||||
if (!CommitVectorPage(addr << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
// Commit next region if needed
|
||||
if (addr2 != 0) {
|
||||
if (!CommitVectorPage(addr2 << HostPageBits, false)) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept {
|
||||
MEMORY_BASIC_INFORMATION info {};
|
||||
auto res = VirtualQuery(reinterpret_cast<void*>(addr), &info, sizeof(info));
|
||||
if (res == 0) {
|
||||
LOG_CRITICAL(HW_Memory, "Failed to query large buffer region at {:#x} with error {}, will try committing anyway", addr, GetLastError());
|
||||
} else if (info.State != MEM_RESERVE) {
|
||||
LOG_ERROR(HW_Memory, "Tried to commit an unreserved large buffer region at {:#x} that is not mapped or is already committed (state {:#x})", addr, info.State);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto perm = write ? PAGE_READWRITE : PAGE_READONLY;
|
||||
void* res2 = VirtualAlloc(reinterpret_cast<LPVOID>(addr), HostPageSize, MEM_COMMIT, perm);
|
||||
if (res2 == nullptr) {
|
||||
LOG_ERROR(HW_Memory, "Failed to commit large buffer region at {:#x}, error {}", addr, GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef MAP_NOCORE
|
||||
#define MAP_NOCORE 0
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
LOG_WARNING(HW_Memory, "Allocating unaligned large vector with size {:#x}; aligning to {} page size", size, page);
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// We will never use this memory entirely so instead of committing it up front let's just reserve it and commit each page individually
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_READWRITE);
|
||||
|
||||
if (base != nullptr) {
|
||||
vector_regions.emplace_back(reinterpret_cast<u64>(base), reinterpret_cast<u64>(base) + size);
|
||||
|
||||
static std::once_flag flag;
|
||||
std::call_once(flag, []() { AddVectoredExceptionHandler(1, FakePageFaultHandler); });
|
||||
} else {
|
||||
// Try committing everything instead??
|
||||
LOG_WARNING(HW_Memory, "Failed to reserve large vector region with error {}, trying to commit instead..", GetLastError());
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
ASSERT_MSG(base, "Failed to reserve {:#x} sized region with error {}", size, GetLastError());
|
||||
#else
|
||||
void* base = mmap(nullptr, size, PROT_READ, MAP_ANON | MAP_PRIVATE | MAP_NOCORE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
ASSERT_MSG(base, "Failed to allocate {:#x} sized region with error {}", size, strerror(errno));
|
||||
#endif
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (auto page = HostPageSize; size % page != 0) {
|
||||
size = AlignUp(size, page);
|
||||
}
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -1,194 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
/* virtual_buffer.h */
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <bit>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef _WIN32
|
||||
constexpr u64 HostPageSize = 0x1000;
|
||||
constexpr u64 HostPageBits = 12;
|
||||
constexpr u64 HostPageMask = ~(HostPageSize - 1);
|
||||
bool CommitVectorPage(uintptr_t addr, bool write) noexcept;
|
||||
#else
|
||||
const u64 HostPageSize = sysconf(_SC_PAGESIZE);
|
||||
const u64 HostPageBits = std::countr_zero(HostPageSize);
|
||||
const u64 HostPageMask = ~(HostPageSize - 1);
|
||||
#endif
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
|
||||
/// A large page-aligned buffer that has optimized memory usage for zero-writes.
|
||||
template <typename T>
|
||||
requires std::is_trivially_copyable_v<T>
|
||||
class SparseLargeVector final {
|
||||
public:
|
||||
constexpr SparseLargeVector() = default;
|
||||
|
||||
explicit SparseLargeVector(std::size_t count) noexcept
|
||||
: alloc_size{count * sizeof(T)}
|
||||
{
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
// each item in vector holds information for 64 pages
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
}
|
||||
|
||||
~SparseLargeVector() noexcept {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
}
|
||||
|
||||
SparseLargeVector(const SparseLargeVector&) = delete;
|
||||
SparseLargeVector& operator=(const SparseLargeVector&) = delete;
|
||||
SparseLargeVector(SparseLargeVector&& other) = delete;
|
||||
SparseLargeVector& operator=(SparseLargeVector&& other) = delete;
|
||||
|
||||
void ResizeAndClear(std::size_t count) noexcept {
|
||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
alloc_size = new_size;
|
||||
base_ptr = static_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
|
||||
auto denom = HostPageSize * 64;
|
||||
committed_pages = std::vector<std::atomic<u64>>((alloc_size + denom - 1) / denom);
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index and allocates memory if needed.
|
||||
T& GetAndFault(std::size_t index) noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
UNREACHABLE_MSG("Out of bounds RW access on SparseLargeVector @ {}", index);
|
||||
}
|
||||
|
||||
if (!IsCommittedPage(index)) {
|
||||
CommitPage(index);
|
||||
}
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
/// Returns a reference to the value of the requested index if initialized, or will otherwise return a zero-initialized object.
|
||||
const T& GetOrDefault(std::size_t index) const {
|
||||
#ifdef _WIN32
|
||||
if (!IsCommittedPage(index)) {
|
||||
return *reinterpret_cast<const T*>(&default_val);
|
||||
}
|
||||
#endif
|
||||
// On non-Windows, OS page table should optimize this by pointing to a zero page if unallocated.
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
void Set(std::size_t index, const T& value) noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds write on SparseLargeVector @ {}", index);
|
||||
return;
|
||||
}
|
||||
if (!IsCommittedPage(index))
|
||||
CommitPage(index);
|
||||
base_ptr[index] = value;
|
||||
}
|
||||
|
||||
void ZeroRegion(std::size_t start, std::size_t end_) noexcept {
|
||||
u64 base = reinterpret_cast<u64>(&base_ptr[start]);
|
||||
const u64 end = reinterpret_cast<u64>(&base_ptr[end_]);
|
||||
|
||||
const u64 end_page = AlignUp(base, HostPageSize);
|
||||
const u64 first_size = (std::min)(end_page, end) - base;
|
||||
|
||||
if (IsCommittedPage(start / sizeof(T))) {
|
||||
std::memset(reinterpret_cast<void*>(base), 0, first_size);
|
||||
}
|
||||
|
||||
if (end <= end_page)
|
||||
return;
|
||||
|
||||
base = end_page;
|
||||
|
||||
for (u64 page = base; page < end; page += HostPageSize) {
|
||||
if (!IsCommittedPage((page - reinterpret_cast<u64>(base_ptr)) / sizeof(T))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::memset(reinterpret_cast<void*>(page), 0, (std::min)( HostPageSize, end - page));
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void CommitRegion(size_t index, size_t end_) {
|
||||
const u64 base = static_cast<u64>(index) * sizeof(T);
|
||||
const u64 end = static_cast<u64>(end_) * sizeof(T);
|
||||
|
||||
for (u64 page = AlignDown(base, HostPageSize); page < end; page += HostPageSize) {
|
||||
if (!IsCommittedPage(page / sizeof(T))) {
|
||||
CommitPage(page / sizeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr T& GetUnchecked(size_t index) {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||
return GetOrDefault(index);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||
return alloc_size / sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] constexpr bool IsCommittedPage(std::size_t index) const noexcept {
|
||||
if (index > alloc_size / sizeof(T)) {
|
||||
LOG_CRITICAL(Common_Memory, "Out of bounds access on large vector @ {}", index);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto page = (index * sizeof(T)) >> HostPageBits;
|
||||
auto val = committed_pages[page >> 6].load(std::memory_order_acquire);
|
||||
return (val >> (page & 63)) & 1;
|
||||
}
|
||||
|
||||
constexpr void CommitPage(std::size_t index) noexcept {
|
||||
auto page_index = (index * sizeof(T)) >> HostPageBits;
|
||||
auto page = reinterpret_cast<uintptr_t>(base_ptr + index) & HostPageMask;
|
||||
#if defined(_WIN32)
|
||||
CommitVectorPage(page, true);
|
||||
#else
|
||||
mprotect(reinterpret_cast<void*>(page), HostPageSize, PROT_READ | PROT_WRITE);
|
||||
#endif
|
||||
|
||||
committed_pages[page_index >> 6].fetch_or(1ULL << (page_index & 63), std::memory_order_release);
|
||||
}
|
||||
|
||||
std::size_t alloc_size{};
|
||||
T* base_ptr{};
|
||||
|
||||
std::vector<std::atomic<u64>> committed_pages{};
|
||||
#ifdef _WIN32
|
||||
const std::array<u8, sizeof(T)> default_val{};
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
+717
-93
@@ -1,4 +1,4 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2014 Tony Wasserka
|
||||
@@ -7,128 +7,752 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __ARM_NEON
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename T, size_t N>
|
||||
class Vec {
|
||||
template <typename T>
|
||||
class Vec2;
|
||||
template <typename T>
|
||||
class Vec3;
|
||||
template <typename T>
|
||||
class Vec4;
|
||||
|
||||
template <typename T>
|
||||
class Vec2 {
|
||||
public:
|
||||
std::array<T, N> elems{};
|
||||
T x{};
|
||||
T y{};
|
||||
|
||||
constexpr Vec() = default;
|
||||
constexpr Vec(T e0) noexcept : elems{e0} {}
|
||||
constexpr Vec(T e0, T e1) noexcept : elems{e0, e1} {}
|
||||
constexpr Vec(T e0, T e1, T e2) noexcept : elems{e0, e1, e2} {}
|
||||
constexpr Vec(T e0, T e1, T e2, T e4) noexcept : elems{e0, e1, e2, e4} {}
|
||||
//explicit constexpr Vec(const std::initializer_list<T> elems_) noexcept : elems{elems_} {}
|
||||
constexpr Vec2() = default;
|
||||
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
|
||||
|
||||
[[nodiscard]] constexpr Vec<decltype(T{} + T{}), N> operator+(const Vec o) const noexcept {
|
||||
Vec<decltype(T{} + T{}), N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = elems[i] + o.elems[i];
|
||||
return r;
|
||||
template <typename T2>
|
||||
[[nodiscard]] constexpr Vec2<T2> Cast() const {
|
||||
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
|
||||
}
|
||||
constexpr Vec<T, N> operator+=(const Vec<T, N> o) noexcept { return *this = *this + o; }
|
||||
|
||||
[[nodiscard]] constexpr Vec<decltype(T{} - T{}), N> operator-(const Vec o) const noexcept {
|
||||
Vec<decltype(T{} - T{}), N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = elems[i] - o.elems[i];
|
||||
return r;
|
||||
[[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
|
||||
return Vec2{f, f};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec2<decltype(T{} + T{})> operator+(const Vec2& other) const {
|
||||
return {x + other.x, y + other.y};
|
||||
}
|
||||
constexpr Vec2& operator+=(const Vec2& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
return *this;
|
||||
}
|
||||
[[nodiscard]] constexpr Vec2<decltype(T{} - T{})> operator-(const Vec2& other) const {
|
||||
return {x - other.x, y - other.y};
|
||||
}
|
||||
constexpr Vec2& operator-=(const Vec2& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
return *this;
|
||||
}
|
||||
constexpr Vec<T, N> operator-=(const Vec<T, N> o) noexcept { return *this = *this - o; }
|
||||
|
||||
template <typename U = T>
|
||||
[[nodiscard]] constexpr Vec<std::enable_if_t<std::is_signed_v<U>, U>, N> operator-() const noexcept {
|
||||
Vec<U, N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = -elems[i];
|
||||
return r;
|
||||
[[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y};
|
||||
}
|
||||
[[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
|
||||
return {x * other.x, y * other.y};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec<decltype(T{} * T{}), N> operator*(const Vec o) const noexcept {
|
||||
Vec<decltype(T{} * T{}), N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = elems[i] * o.elems[i];
|
||||
return r;
|
||||
}
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec<decltype(T{} * V{}), N> operator*(const V f) const noexcept {
|
||||
[[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
using TV = decltype(T{} * V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
Vec<TV, N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = TV(C(elems[i]) * C(f));
|
||||
return r;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
||||
};
|
||||
}
|
||||
template <typename V>
|
||||
constexpr Vec<T, N> operator*=(const V f) noexcept { return *this = *this * f; }
|
||||
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec<decltype(T{} / V{}), N> operator/(const V f) const noexcept {
|
||||
constexpr Vec2& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
using TV = decltype(T{} / V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
Vec<TV, N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = TV(C(elems[i]) / C(f));
|
||||
return r;
|
||||
}
|
||||
template <typename V>
|
||||
constexpr Vec<T, N> operator/=(const V f) noexcept { return *this = *this / f; }
|
||||
|
||||
[[nodiscard]] constexpr T Length2() const noexcept {
|
||||
T r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r += elems[i] * elems[i];
|
||||
return r;
|
||||
}
|
||||
// Only implemented for T=float
|
||||
[[nodiscard]] T Length() const { return T(std::sqrt(float(Length2()))); }
|
||||
[[nodiscard]] Vec<T, N> Normalized() const { return *this / Length(); }
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t i) noexcept { return elems[i]; }
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const noexcept { return elems[i]; }
|
||||
|
||||
[[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
|
||||
const T x2 = elems[0] * elems[0];
|
||||
const T y2 = elems[1] * elems[1];
|
||||
const T z2 = elems[2] * elems[2];
|
||||
|
||||
const T xy = elems[0] * elems[1];
|
||||
const T wz = elems[3] * elems[2];
|
||||
const T xz = elems[0] * elems[2];
|
||||
const T wy = elems[3] * elems[1];
|
||||
const T yz = elems[1] * elems[2];
|
||||
const T wx = elems[3] * elems[0];
|
||||
return {
|
||||
1.0f - 2.0f * (y2 + z2),
|
||||
2.0f * (xy + wz),
|
||||
2.0f * (xz - wy),
|
||||
0.0f,
|
||||
2.0f * (xy - wz),
|
||||
1.0f - 2.0f * (x2 + z2),
|
||||
2.0f * (yz + wx),
|
||||
0.0f,
|
||||
2.0f * (xz + wy),
|
||||
2.0f * (yz - wx),
|
||||
1.0f - 2.0f * (x2 + y2),
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
0.0f,
|
||||
1.0f
|
||||
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec2& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T Length2() const {
|
||||
return x * x + y * y;
|
||||
}
|
||||
|
||||
// Only implemented for T=float
|
||||
[[nodiscard]] float Length() const;
|
||||
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
|
||||
// Common aliases: UV (texel coordinates), ST (texture coordinates)
|
||||
[[nodiscard]] constexpr T& u() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& v() {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr T& s() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& t() {
|
||||
return y;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& u() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& v() const {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& s() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& t() const {
|
||||
return y;
|
||||
}
|
||||
|
||||
// swizzlers - create a subvector of specific components
|
||||
[[nodiscard]] constexpr Vec2 yx() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
[[nodiscard]] constexpr Vec2 vu() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
[[nodiscard]] constexpr Vec2 ts() const {
|
||||
return Vec2(y, x);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, size_t N, typename V>
|
||||
[[nodiscard]] constexpr Vec<T, N> operator*(const V f, const Vec<T, N> v) noexcept {
|
||||
template <typename T, typename V>
|
||||
[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
|
||||
using C = std::common_type_t<T, V>;
|
||||
Vec<T, N> r{};
|
||||
for (size_t i = 0; i < N; ++i)
|
||||
r.elems[i] = T(C(f) * C(v.elems[i]));
|
||||
return r;
|
||||
|
||||
return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
||||
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)));
|
||||
}
|
||||
|
||||
using Vec2f = Vec2<float>;
|
||||
|
||||
template <>
|
||||
inline float Vec2<float>::Length() const {
|
||||
return std::sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float Vec2<float>::Normalize() {
|
||||
float length = Length();
|
||||
*this /= length;
|
||||
return length;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class Vec3 {
|
||||
public:
|
||||
T x{};
|
||||
T y{};
|
||||
T z{};
|
||||
|
||||
constexpr Vec3() = default;
|
||||
constexpr Vec3(const T& x_, const T& y_, const T& z_) : x(x_), y(y_), z(z_) {}
|
||||
|
||||
template <typename T2>
|
||||
[[nodiscard]] constexpr Vec3<T2> Cast() const {
|
||||
return Vec3<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z));
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr Vec3 AssignToAll(const T& f) {
|
||||
return Vec3(f, f, f);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} + T{})> operator+(const Vec3& other) const {
|
||||
return {x + other.x, y + other.y, z + other.z};
|
||||
}
|
||||
|
||||
constexpr Vec3& operator+=(const Vec3& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} - T{})> operator-(const Vec3& other) const {
|
||||
return {x - other.x, y - other.y, z - other.z};
|
||||
}
|
||||
|
||||
constexpr Vec3& operator-=(const Vec3& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
[[nodiscard]] constexpr Vec3<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y, -z};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} * T{})> operator*(const Vec3& other) const {
|
||||
return {x * other.x, y * other.y, z * other.z};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
using TV = decltype(T{} * V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec3& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
using TV = decltype(T{} / V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec3& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void RotateFromOrigin(float roll, float pitch, float yaw) {
|
||||
float temp = y;
|
||||
y = std::cos(roll) * y - std::sin(roll) * z;
|
||||
z = std::sin(roll) * temp + std::cos(roll) * z;
|
||||
|
||||
temp = x;
|
||||
x = std::cos(pitch) * x + std::sin(pitch) * z;
|
||||
z = -std::sin(pitch) * temp + std::cos(pitch) * z;
|
||||
|
||||
temp = x;
|
||||
x = std::cos(yaw) * x - std::sin(yaw) * y;
|
||||
y = std::sin(yaw) * temp + std::cos(yaw) * y;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T Length2() const {
|
||||
return x * x + y * y + z * z;
|
||||
}
|
||||
|
||||
// Only implemented for T=float
|
||||
[[nodiscard]] float Length() const;
|
||||
[[nodiscard]] Vec3 Normalized() const;
|
||||
[[nodiscard]] float Normalize(); // returns the previous length, which is often useful
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
}
|
||||
|
||||
// Common aliases: UVW (texel coordinates), RGB (colors), STQ (texture coordinates)
|
||||
[[nodiscard]] constexpr T& u() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& v() {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr T& w() {
|
||||
return z;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& r() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& g() {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr T& b() {
|
||||
return z;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& s() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& t() {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr T& q() {
|
||||
return z;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& u() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& v() const {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& w() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& r() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& g() const {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& b() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& s() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& t() const {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& q() const {
|
||||
return z;
|
||||
}
|
||||
|
||||
// swizzlers - create a subvector of specific components
|
||||
// e.g. Vec2 uv() { return Vec2(x,y); }
|
||||
// _DEFINE_SWIZZLER2 defines a single such function, DEFINE_SWIZZLER2 defines all of them for all
|
||||
// component names (x<->r) and permutations (xy<->yx)
|
||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
|
||||
#define DEFINE_SWIZZLER2(a, b, a2, b2, a3, b3, a4, b4) \
|
||||
_DEFINE_SWIZZLER2(a, b, a##b); \
|
||||
_DEFINE_SWIZZLER2(a, b, a2##b2); \
|
||||
_DEFINE_SWIZZLER2(a, b, a3##b3); \
|
||||
_DEFINE_SWIZZLER2(a, b, a4##b4); \
|
||||
_DEFINE_SWIZZLER2(b, a, b##a); \
|
||||
_DEFINE_SWIZZLER2(b, a, b2##a2); \
|
||||
_DEFINE_SWIZZLER2(b, a, b3##a3); \
|
||||
_DEFINE_SWIZZLER2(b, a, b4##a4)
|
||||
|
||||
DEFINE_SWIZZLER2(x, y, r, g, u, v, s, t);
|
||||
DEFINE_SWIZZLER2(x, z, r, b, u, w, s, q);
|
||||
DEFINE_SWIZZLER2(y, z, g, b, v, w, t, q);
|
||||
#undef DEFINE_SWIZZLER2
|
||||
#undef _DEFINE_SWIZZLER2
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return Vec3<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
||||
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)),
|
||||
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.z)));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float Vec3<float>::Length() const {
|
||||
return std::sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Vec3<float> Vec3<float>::Normalized() const {
|
||||
return *this / Length();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float Vec3<float>::Normalize() {
|
||||
float length = Length();
|
||||
*this /= length;
|
||||
return length;
|
||||
}
|
||||
|
||||
using Vec3f = Vec3<float>;
|
||||
|
||||
template <typename T>
|
||||
class Vec4 {
|
||||
public:
|
||||
T x{};
|
||||
T y{};
|
||||
T z{};
|
||||
T w{};
|
||||
|
||||
constexpr Vec4() = default;
|
||||
constexpr Vec4(const T& x_, const T& y_, const T& z_, const T& w_)
|
||||
: x(x_), y(y_), z(z_), w(w_) {}
|
||||
|
||||
template <typename T2>
|
||||
[[nodiscard]] constexpr Vec4<T2> Cast() const {
|
||||
return Vec4<T2>(static_cast<T2>(x), static_cast<T2>(y), static_cast<T2>(z),
|
||||
static_cast<T2>(w));
|
||||
}
|
||||
|
||||
[[nodiscard]] static constexpr Vec4 AssignToAll(const T& f) {
|
||||
return Vec4(f, f, f, f);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec4<decltype(T{} + T{})> operator+(const Vec4& other) const {
|
||||
return {x + other.x, y + other.y, z + other.z, w + other.w};
|
||||
}
|
||||
|
||||
constexpr Vec4& operator+=(const Vec4& other) {
|
||||
x += other.x;
|
||||
y += other.y;
|
||||
z += other.z;
|
||||
w += other.w;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec4<decltype(T{} - T{})> operator-(const Vec4& other) const {
|
||||
return {x - other.x, y - other.y, z - other.z, w - other.w};
|
||||
}
|
||||
|
||||
constexpr Vec4& operator-=(const Vec4& other) {
|
||||
x -= other.x;
|
||||
y -= other.y;
|
||||
z -= other.z;
|
||||
w -= other.w;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
[[nodiscard]] constexpr Vec4<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||
return {-x, -y, -z, -w};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Vec4<decltype(T{} * T{})> operator*(const Vec4& other) const {
|
||||
return {x * other.x, y * other.y, z * other.z, w * other.w};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
|
||||
using TV = decltype(T{} * V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(w) * static_cast<C>(f)),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4& operator*=(const V& f) {
|
||||
*this = *this * f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
[[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
|
||||
using TV = decltype(T{} / V{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
|
||||
static_cast<TV>(static_cast<C>(w) / static_cast<C>(f)),
|
||||
};
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
constexpr Vec4& operator/=(const V& f) {
|
||||
*this = *this / f;
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T Length2() const {
|
||||
return x * x + y * y + z * z + w * w;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t i) {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t i) const {
|
||||
return *((&x) + i);
|
||||
}
|
||||
|
||||
constexpr void SetZero() {
|
||||
x = 0;
|
||||
y = 0;
|
||||
z = 0;
|
||||
w = 0;
|
||||
}
|
||||
|
||||
// Common alias: RGBA (colors)
|
||||
[[nodiscard]] constexpr T& r() {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr T& g() {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr T& b() {
|
||||
return z;
|
||||
}
|
||||
[[nodiscard]] constexpr T& a() {
|
||||
return w;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& r() const {
|
||||
return x;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& g() const {
|
||||
return y;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& b() const {
|
||||
return z;
|
||||
}
|
||||
[[nodiscard]] constexpr const T& a() const {
|
||||
return w;
|
||||
}
|
||||
|
||||
// Swizzlers - Create a subvector of specific components
|
||||
// e.g. Vec2 uv() { return Vec2(x,y); }
|
||||
|
||||
// _DEFINE_SWIZZLER2 defines a single such function
|
||||
// DEFINE_SWIZZLER2_COMP1 defines one-component functions for all component names (x<->r)
|
||||
// DEFINE_SWIZZLER2_COMP2 defines two component functions for all component names (x<->r) and
|
||||
// permutations (xy<->yx)
|
||||
#define _DEFINE_SWIZZLER2(a, b, name) \
|
||||
[[nodiscard]] constexpr Vec2<T> name() const { return Vec2<T>(a, b); }
|
||||
#define DEFINE_SWIZZLER2_COMP1(a, a2) \
|
||||
_DEFINE_SWIZZLER2(a, a, a##a); \
|
||||
_DEFINE_SWIZZLER2(a, a, a2##a2)
|
||||
#define DEFINE_SWIZZLER2_COMP2(a, b, a2, b2) \
|
||||
_DEFINE_SWIZZLER2(a, b, a##b); \
|
||||
_DEFINE_SWIZZLER2(a, b, a2##b2); \
|
||||
_DEFINE_SWIZZLER2(b, a, b##a); \
|
||||
_DEFINE_SWIZZLER2(b, a, b2##a2)
|
||||
|
||||
DEFINE_SWIZZLER2_COMP2(x, y, r, g);
|
||||
DEFINE_SWIZZLER2_COMP2(x, z, r, b);
|
||||
DEFINE_SWIZZLER2_COMP2(x, w, r, a);
|
||||
DEFINE_SWIZZLER2_COMP2(y, z, g, b);
|
||||
DEFINE_SWIZZLER2_COMP2(y, w, g, a);
|
||||
DEFINE_SWIZZLER2_COMP2(z, w, b, a);
|
||||
DEFINE_SWIZZLER2_COMP1(x, r);
|
||||
DEFINE_SWIZZLER2_COMP1(y, g);
|
||||
DEFINE_SWIZZLER2_COMP1(z, b);
|
||||
DEFINE_SWIZZLER2_COMP1(w, a);
|
||||
#undef DEFINE_SWIZZLER2_COMP1
|
||||
#undef DEFINE_SWIZZLER2_COMP2
|
||||
#undef _DEFINE_SWIZZLER2
|
||||
|
||||
#define _DEFINE_SWIZZLER3(a, b, c, name) \
|
||||
[[nodiscard]] constexpr Vec3<T> name() const { return Vec3<T>(a, b, c); }
|
||||
#define DEFINE_SWIZZLER3_COMP1(a, a2) \
|
||||
_DEFINE_SWIZZLER3(a, a, a, a##a##a); \
|
||||
_DEFINE_SWIZZLER3(a, a, a, a2##a2##a2)
|
||||
#define DEFINE_SWIZZLER3_COMP3(a, b, c, a2, b2, c2) \
|
||||
_DEFINE_SWIZZLER3(a, b, c, a##b##c); \
|
||||
_DEFINE_SWIZZLER3(a, c, b, a##c##b); \
|
||||
_DEFINE_SWIZZLER3(b, a, c, b##a##c); \
|
||||
_DEFINE_SWIZZLER3(b, c, a, b##c##a); \
|
||||
_DEFINE_SWIZZLER3(c, a, b, c##a##b); \
|
||||
_DEFINE_SWIZZLER3(c, b, a, c##b##a); \
|
||||
_DEFINE_SWIZZLER3(a, b, c, a2##b2##c2); \
|
||||
_DEFINE_SWIZZLER3(a, c, b, a2##c2##b2); \
|
||||
_DEFINE_SWIZZLER3(b, a, c, b2##a2##c2); \
|
||||
_DEFINE_SWIZZLER3(b, c, a, b2##c2##a2); \
|
||||
_DEFINE_SWIZZLER3(c, a, b, c2##a2##b2); \
|
||||
_DEFINE_SWIZZLER3(c, b, a, c2##b2##a2)
|
||||
|
||||
DEFINE_SWIZZLER3_COMP3(x, y, z, r, g, b);
|
||||
DEFINE_SWIZZLER3_COMP3(x, y, w, r, g, a);
|
||||
DEFINE_SWIZZLER3_COMP3(x, z, w, r, b, a);
|
||||
DEFINE_SWIZZLER3_COMP3(y, z, w, g, b, a);
|
||||
DEFINE_SWIZZLER3_COMP1(x, r);
|
||||
DEFINE_SWIZZLER3_COMP1(y, g);
|
||||
DEFINE_SWIZZLER3_COMP1(z, b);
|
||||
DEFINE_SWIZZLER3_COMP1(w, a);
|
||||
#undef DEFINE_SWIZZLER3_COMP1
|
||||
#undef DEFINE_SWIZZLER3_COMP3
|
||||
#undef _DEFINE_SWIZZLER3
|
||||
};
|
||||
|
||||
template <typename T, typename V>
|
||||
[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
|
||||
using TV = decltype(V{} * T{});
|
||||
using C = std::common_type_t<T, V>;
|
||||
|
||||
return {
|
||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.y)),
|
||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.z)),
|
||||
static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.w)),
|
||||
};
|
||||
}
|
||||
|
||||
using Vec4f = Vec4<float>;
|
||||
|
||||
template <typename T>
|
||||
constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec2<T>& a, const Vec2<T>& b) {
|
||||
return a.x * b.x + a.y * b.y;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec3<T>& a, const Vec3<T>& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr decltype(T{} * T{} + T{} * T{}) Dot(const Vec4<T>& a, const Vec4<T>& b) {
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
}
|
||||
|
||||
template <>
|
||||
[[nodiscard]] inline float Dot(const Vec4<float>& a, const Vec4<float>& b) {
|
||||
#ifdef __ARM_NEON
|
||||
float32x4_t va = vld1q_f32(&a.x);
|
||||
float32x4_t vb = vld1q_f32(&b.x);
|
||||
float32x4_t result = vmulq_f32(va, vb);
|
||||
#if defined(__aarch64__) // Use vaddvq_f32 in ARMv8 architectures
|
||||
return vaddvq_f32(result);
|
||||
#else // Use manual addition for older architectures
|
||||
float32x2_t sum2 = vadd_f32(vget_high_f32(result), vget_low_f32(result));
|
||||
return vget_lane_f32(vpadd_f32(sum2, sum2), 0);
|
||||
#endif
|
||||
#else
|
||||
return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec3<decltype(T{} * T{} - T{} * T{})> Cross(const Vec3<T>& a,
|
||||
const Vec3<T>& b) {
|
||||
return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
|
||||
}
|
||||
|
||||
// linear interpolation via float: 0.0=begin, 1.0=end
|
||||
template <typename X>
|
||||
[[nodiscard]] constexpr decltype(X{} * float{} + X{} * float{}) Lerp(const X& begin, const X& end,
|
||||
const float t) {
|
||||
return begin * (1.f - t) + end * t;
|
||||
}
|
||||
|
||||
// linear interpolation via int: 0=begin, base=end
|
||||
template <typename X, int base>
|
||||
[[nodiscard]] constexpr decltype((X{} * int{} + X{} * int{}) / base) LerpInt(const X& begin,
|
||||
const X& end,
|
||||
const int t) {
|
||||
return (begin * (base - t) + end * t) / base;
|
||||
}
|
||||
|
||||
// bilinear interpolation. s is for interpolating x00-x01 and x10-x11, and t is for the second
|
||||
// interpolation.
|
||||
template <typename X>
|
||||
[[nodiscard]] constexpr auto BilinearInterp(const X& x00, const X& x01, const X& x10, const X& x11,
|
||||
const float s, const float t) {
|
||||
auto y0 = Lerp(x00, x01, s);
|
||||
auto y1 = Lerp(x10, x11, s);
|
||||
return Lerp(y0, y1, t);
|
||||
}
|
||||
|
||||
// Utility vector factories
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec2<T> MakeVec(const T& x, const T& y) {
|
||||
return Vec2<T>{x, y};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const T& y, const T& z) {
|
||||
return Vec3<T>{x, y, z};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const Vec2<T>& zw) {
|
||||
return MakeVec(x, y, zw[0], zw[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec3<T> MakeVec(const Vec2<T>& xy, const T& z) {
|
||||
return MakeVec(xy[0], xy[1], z);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec3<T> MakeVec(const T& x, const Vec2<T>& yz) {
|
||||
return MakeVec(x, yz[0], yz[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const T& y, const T& z, const T& w) {
|
||||
return Vec4<T>{x, y, z, w};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const T& z, const T& w) {
|
||||
return MakeVec(xy[0], xy[1], z, w);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec2<T>& yz, const T& w) {
|
||||
return MakeVec(x, yz[0], yz[1], w);
|
||||
}
|
||||
|
||||
// NOTE: This has priority over "Vec2<Vec2<T>> MakeVec(const Vec2<T>& x, const Vec2<T>& y)".
|
||||
// Even if someone wanted to use an odd object like Vec2<Vec2<T>>, the compiler would error
|
||||
// out soon enough due to misuse of the returned structure.
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec2<T>& xy, const Vec2<T>& zw) {
|
||||
return MakeVec(xy[0], xy[1], zw[0], zw[1]);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const Vec3<T>& xyz, const T& w) {
|
||||
return MakeVec(xyz[0], xyz[1], xyz[2], w);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr Vec4<T> MakeVec(const T& x, const Vec3<T>& yzw) {
|
||||
return MakeVec(x, yzw[0], yzw[1], yzw[2]);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/virtual_buffer.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept {
|
||||
#ifdef _WIN32
|
||||
void* base = VirtualAlloc(nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
||||
if (base == nullptr) {
|
||||
// Probably failing to reserve is less likely than failing to commit
|
||||
base = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE);
|
||||
}
|
||||
#else
|
||||
void* base = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
if (base == MAP_FAILED)
|
||||
base = nullptr;
|
||||
#endif
|
||||
ASSERT(base);
|
||||
return base;
|
||||
}
|
||||
|
||||
void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
|
||||
if (!base)
|
||||
return;
|
||||
#ifdef _WIN32
|
||||
ASSERT(VirtualFree(base, 0, MEM_RELEASE));
|
||||
#else
|
||||
ASSERT(munmap(base, size) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
@@ -0,0 +1,84 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
void* AllocateMemoryPages(std::size_t size) noexcept;
|
||||
void FreeMemoryPages(void* base, std::size_t size) noexcept;
|
||||
|
||||
template <typename T>
|
||||
class VirtualBuffer final {
|
||||
public:
|
||||
// TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
|
||||
// using std::atomic_ref once libc++ has support for it
|
||||
// static_assert(
|
||||
// std::is_trivially_constructible_v<T>,
|
||||
// "T must be trivially constructible, as non-trivial constructors will not be executed "
|
||||
// "with the current allocator");
|
||||
|
||||
constexpr VirtualBuffer() = default;
|
||||
explicit VirtualBuffer(std::size_t count) noexcept
|
||||
: alloc_size{count * sizeof(T)}
|
||||
{
|
||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
}
|
||||
|
||||
~VirtualBuffer() noexcept {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
}
|
||||
|
||||
VirtualBuffer(const VirtualBuffer&) = delete;
|
||||
VirtualBuffer& operator=(const VirtualBuffer&) = delete;
|
||||
|
||||
VirtualBuffer(VirtualBuffer&& other) noexcept
|
||||
: alloc_size{std::exchange(other.alloc_size, 0)}
|
||||
, base_ptr{std::exchange(other.base_ptr, nullptr)}
|
||||
{}
|
||||
|
||||
VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
|
||||
alloc_size = std::exchange(other.alloc_size, 0);
|
||||
base_ptr = std::exchange(other.base_ptr, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void resize(std::size_t count) noexcept {
|
||||
if (auto const new_size = count * sizeof(T); new_size != alloc_size) {
|
||||
FreeMemoryPages(base_ptr, alloc_size);
|
||||
alloc_size = new_size;
|
||||
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T& operator[](std::size_t index) const noexcept {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T& operator[](std::size_t index) noexcept {
|
||||
return base_ptr[index];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr T* data() noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const T* data() const noexcept {
|
||||
return base_ptr;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t size() const noexcept {
|
||||
return alloc_size / sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
std::size_t alloc_size{};
|
||||
T* base_ptr{};
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -711,6 +711,8 @@ add_library(core STATIC
|
||||
hle/service/kernel_helpers.h
|
||||
hle/service/lbl/lbl.cpp
|
||||
hle/service/lbl/lbl.h
|
||||
hle/service/ldn/client_process_monitor.cpp
|
||||
hle/service/ldn/client_process_monitor.h
|
||||
hle/service/ldn/lan_discovery.cpp
|
||||
hle/service/ldn/lan_discovery.h
|
||||
hle/service/ldn/ldn.cpp
|
||||
@@ -830,6 +832,8 @@ add_library(core STATIC
|
||||
hle/service/ns/system_update_control.h
|
||||
hle/service/ns/system_update_interface.cpp
|
||||
hle/service/ns/system_update_interface.h
|
||||
hle/service/ns/vulnerability_manager_interface.cpp
|
||||
hle/service/ns/vulnerability_manager_interface.h
|
||||
hle/service/nvdrv/core/container.cpp
|
||||
hle/service/nvdrv/core/container.h
|
||||
hle/service/nvdrv/core/heap_mapper.cpp
|
||||
@@ -1063,6 +1067,8 @@ add_library(core STATIC
|
||||
hle/service/sockets/sockets.h
|
||||
hle/service/sockets/sockets_translate.cpp
|
||||
hle/service/sockets/sockets_translate.h
|
||||
hle/service/spl/csrng.cpp
|
||||
hle/service/spl/csrng.h
|
||||
hle/service/spl/spl.cpp
|
||||
hle/service/spl/spl.h
|
||||
hle/service/spl/spl_module.cpp
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user