mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-10-29 02:41:51 +00:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f158b22b1 | |||
| 7275857304 | |||
| 7c035d5df0 | |||
| c2a3c1fdf3 | |||
| 78c46ddd25 | |||
| 68df3e6dae | |||
| 628ec8640b | |||
| f91b07f7d4 | |||
| aac96f6f3c | |||
| 353cb1aab5 | |||
| 32d425e196 | |||
| 928961a615 | |||
| 78f0d35f8c | |||
| 9f9849c156 |
@@ -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.
|
# Lossless Scaling frame generation. Only Android.
|
||||||
cmake_dependent_option(ENABLE_LSFG "Enable Lossless Scaling frame generation" ON "ANDROID" OFF)
|
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
|
# non-linux bundled qt are static
|
||||||
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
|
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
|
||||||
set(YUZU_STATIC_BUILD ON)
|
set(YUZU_STATIC_BUILD ON)
|
||||||
|
|||||||
@@ -238,14 +238,6 @@
|
|||||||
"repo": "stachenov/quazip",
|
"repo": "stachenov/quazip",
|
||||||
"version": "2e95c9001b"
|
"version": "2e95c9001b"
|
||||||
},
|
},
|
||||||
"reshade": {
|
|
||||||
"hash": "6fc9b39821ca6e2d91160f05846b3a42159fec759f5d6fba91d1a57801a1bdaf6047740578c136f0b1a60a9c29ad160d87dbfe0dbfbcdf6ea879a8a078377b8c",
|
|
||||||
"patches": [
|
|
||||||
"0001-from-chars-fallback.patch"
|
|
||||||
],
|
|
||||||
"repo": "crosire/reshade",
|
|
||||||
"version": "v6.8.0"
|
|
||||||
},
|
|
||||||
"sdl3": {
|
"sdl3": {
|
||||||
"hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3",
|
"hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3",
|
||||||
"min_version": "3.2.10",
|
"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()
|
||||||
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
|
# Catch2
|
||||||
if (YUZU_TESTS OR DYNARMIC_TESTS)
|
if (YUZU_TESTS OR DYNARMIC_TESTS)
|
||||||
AddJsonPackage(catch2)
|
AddJsonPackage(catch2)
|
||||||
|
|||||||
@@ -3,8 +3,6 @@
|
|||||||
|
|
||||||
package org.yuzu.yuzu_emu.dialogs
|
package org.yuzu.yuzu_emu.dialogs
|
||||||
|
|
||||||
import android.content.res.ColorStateList
|
|
||||||
import android.graphics.Rect
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@@ -20,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.features.settings.model.IntSetting
|
||||||
import org.yuzu.yuzu_emu.fragments.EmulationFragment
|
import org.yuzu.yuzu_emu.fragments.EmulationFragment
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
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.AbstractSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
|
import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
|
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
|
||||||
|
|
||||||
class QuickSettings(val emulationFragment: EmulationFragment) {
|
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() {
|
private fun saveSettings() {
|
||||||
if (emulationFragment.shouldUseCustom) {
|
if (emulationFragment.shouldUseCustom) {
|
||||||
NativeConfig.savePerGameConfig()
|
NativeConfig.savePerGameConfig()
|
||||||
@@ -251,428 +232,6 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
|
|||||||
container.addView(itemView)
|
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addDivider(container: ViewGroup) {
|
fun addDivider(container: ViewGroup) {
|
||||||
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
val inflater = LayoutInflater.from(emulationFragment.requireContext())
|
||||||
val dividerView = inflater.inflate(R.layout.item_quick_settings_divider, container, false)
|
val dividerView = inflater.inflate(R.layout.item_quick_settings_divider, container, false)
|
||||||
|
|||||||
-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_SYSTEM(R.string.preferences_system),
|
||||||
SECTION_RENDERER(R.string.preferences_graphics),
|
SECTION_RENDERER(R.string.preferences_graphics),
|
||||||
SECTION_FRAME_GEN(R.string.frame_gen),
|
SECTION_FRAME_GEN(R.string.frame_gen),
|
||||||
SECTION_POST_PROCESSING(R.string.post_processing),
|
|
||||||
SECTION_PERFORMANCE_STATS(R.string.stats_overlay_options),
|
SECTION_PERFORMANCE_STATS(R.string.stats_overlay_options),
|
||||||
SECTION_INPUT_OVERLAY(R.string.input_overlay_options),
|
SECTION_INPUT_OVERLAY(R.string.input_overlay_options),
|
||||||
SECTION_SOC_OVERLAY(R.string.soc_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"),
|
DEVICE_NAME("device_name"),
|
||||||
LOG_FILTER("log_filter"),
|
LOG_FILTER("log_filter"),
|
||||||
PROGRAM_ARGS("program_args"),
|
PROGRAM_ARGS("program_args"),
|
||||||
POST_SHADER_CHAIN("post_shader_chain"),
|
|
||||||
|
|
||||||
WEB_TOKEN("eden_token"),
|
WEB_TOKEN("eden_token"),
|
||||||
WEB_USERNAME("eden_username")
|
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
|
|
||||||
}
|
|
||||||
-4
@@ -144,10 +144,6 @@ abstract class SettingsItem(
|
|||||||
const val TYPE_LAUNCHABLE = 13
|
const val TYPE_LAUNCHABLE = 13
|
||||||
const val TYPE_PATH = 14
|
const val TYPE_PATH = 14
|
||||||
const val TYPE_GPU_UNSWIZZLE = 15
|
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 FASTMEM_COMBINED = "fastmem_combined"
|
||||||
const val GPU_UNSWIZZLE_COMBINED = "gpu_unswizzle_combined"
|
const val GPU_UNSWIZZLE_COMBINED = "gpu_unswizzle_combined"
|
||||||
|
|||||||
-32
@@ -23,10 +23,6 @@ import com.google.android.material.timepicker.TimeFormat
|
|||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.SettingsNavigationDirections
|
import org.yuzu.yuzu_emu.SettingsNavigationDirections
|
||||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
|
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.ListItemSettingInputBinding
|
||||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
|
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
|
||||||
import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
|
import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
|
||||||
@@ -110,34 +106,6 @@ class SettingsAdapter(
|
|||||||
GpuUnswizzleViewHolder(ListItemSettingBinding.inflate(inflater), this)
|
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 -> {
|
else -> {
|
||||||
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
|
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
|
||||||
}
|
}
|
||||||
|
|||||||
-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.AbstractBooleanSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
|
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.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.ByteSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
|
||||||
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
|
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.InputHandler
|
||||||
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
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.DirectoryInitialization
|
||||||
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
import org.yuzu.yuzu_emu.utils.FullscreenHelper
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
@@ -46,14 +44,6 @@ class SettingsFragmentPresenter(
|
|||||||
) {
|
) {
|
||||||
private var settingsList = ArrayList<SettingsItem>()
|
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
|
private val context get() = YuzuApplication.appContext
|
||||||
|
|
||||||
// Extension for altering settings list based on each setting's properties
|
// Extension for altering settings list based on each setting's properties
|
||||||
@@ -173,7 +163,6 @@ class SettingsFragmentPresenter(
|
|||||||
MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
|
||||||
MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
|
||||||
MenuTag.SECTION_FRAME_GEN -> addFrameGenSettings(sl)
|
MenuTag.SECTION_FRAME_GEN -> addFrameGenSettings(sl)
|
||||||
MenuTag.SECTION_POST_PROCESSING -> addPostProcessingSettings(sl)
|
|
||||||
MenuTag.SECTION_PERFORMANCE_STATS -> addPerformanceOverlaySettings(sl)
|
MenuTag.SECTION_PERFORMANCE_STATS -> addPerformanceOverlaySettings(sl)
|
||||||
MenuTag.SECTION_SOC_OVERLAY -> addSocOverlaySettings(sl)
|
MenuTag.SECTION_SOC_OVERLAY -> addSocOverlaySettings(sl)
|
||||||
MenuTag.SECTION_INPUT_OVERLAY -> addInputOverlaySettings(sl)
|
MenuTag.SECTION_INPUT_OVERLAY -> addInputOverlaySettings(sl)
|
||||||
@@ -201,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>) {
|
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
|
||||||
sl.apply {
|
sl.apply {
|
||||||
add(
|
add(
|
||||||
|
|||||||
-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
|
|
||||||
}
|
|
||||||
@@ -94,7 +94,6 @@ import org.yuzu.yuzu_emu.utils.InputHandler
|
|||||||
import org.yuzu.yuzu_emu.utils.Log
|
import org.yuzu.yuzu_emu.utils.Log
|
||||||
import org.yuzu.yuzu_emu.utils.NativeConfig
|
import org.yuzu.yuzu_emu.utils.NativeConfig
|
||||||
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
|
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
|
||||||
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
|
||||||
import org.yuzu.yuzu_emu.utils.collect
|
import org.yuzu.yuzu_emu.utils.collect
|
||||||
@@ -884,8 +883,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
if (shouldUseCustom) {
|
if (shouldUseCustom) {
|
||||||
SettingsFile.loadCustomConfig(game!!)
|
SettingsFile.loadCustomConfig(game!!)
|
||||||
}
|
}
|
||||||
refreshPostProcessing()
|
|
||||||
addQuickSettings()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1090,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() {
|
private fun addQuickSettings() {
|
||||||
binding.quickSettingsSheet.apply {
|
binding.quickSettingsSheet.apply {
|
||||||
val container = binding.quickSettingsSheet.findViewById<ViewGroup>(R.id.quick_settings_container)
|
val container = binding.quickSettingsSheet.findViewById<ViewGroup>(R.id.quick_settings_container)
|
||||||
@@ -1225,10 +1194,6 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
|||||||
R.array.rendererAntiAliasingNames,
|
R.array.rendererAntiAliasingNames,
|
||||||
R.array.rendererAntiAliasingValues
|
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()) {
|
if (GpuDriverHelper.isAdrenoGpu()) {
|
||||||
add(
|
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(
|
add(
|
||||||
HomeSetting(
|
HomeSetting(
|
||||||
R.string.lossless_scaling,
|
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.cpp
|
||||||
android_config.h
|
android_config.h
|
||||||
native_input.cpp
|
native_input.cpp
|
||||||
native_post_processing.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
|
||||||
|
|||||||
@@ -15,22 +15,10 @@
|
|||||||
#include "frontend_common/config.h"
|
#include "frontend_common/config.h"
|
||||||
#include "frontend_common/settings_generator.h"
|
#include "frontend_common/settings_generator.h"
|
||||||
#include "native.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> global_config;
|
||||||
std::unique_ptr<AndroidConfig> per_game_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>
|
template <typename T>
|
||||||
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
|
||||||
auto key = Common::Android::GetJString(env, 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) {
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeGlobalConfig(JNIEnv* env, jobject obj) {
|
||||||
global_config = std::make_unique<AndroidConfig>();
|
global_config = std::make_unique<AndroidConfig>();
|
||||||
FrontendCommon::GenerateSettings();
|
FrontendCommon::GenerateSettings();
|
||||||
ResetFxChainToGlobal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadGlobalConfig(JNIEnv* env, jobject obj) {
|
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) {
|
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadGlobalConfig(JNIEnv* env, jobject obj) {
|
||||||
global_config->AndroidConfig::ReloadAllValues();
|
global_config->AndroidConfig::ReloadAllValues();
|
||||||
ResetFxChainToGlobal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveGlobalConfig(JNIEnv* env, jobject obj) {
|
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,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,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>
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<!-- General application strings -->
|
<!-- General application strings -->
|
||||||
<string name="app_name" translatable="false">Eden</string>
|
<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_name">Notices and errors</string>
|
||||||
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
|
<string name="notice_notification_channel_id" translatable="false">noticesAndErrors</string>
|
||||||
<string name="notice_notification_channel_description">Shows notifications when something goes wrong.</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_fetcher">GPU driver fetcher</string>
|
||||||
<string name="gpu_driver_manager">GPU driver manager</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="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">Frame generation</string>
|
||||||
<string name="frame_gen_per_game_description">Configure frame generation for this game</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>
|
<string name="frame_gen_description">Insert interpolated frames between rendered ones using Lossless Scaling. Forces FIFO presentation while enabled.</string>
|
||||||
@@ -952,7 +933,7 @@
|
|||||||
<string name="loader_error_file_not_found">ROM file does not exist</string>
|
<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">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 -->
|
<!-- Intent Launch strings -->
|
||||||
<string name="searching_for_game">Searching for game...</string>
|
<string name="searching_for_game">Searching for game...</string>
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -12,9 +9,7 @@
|
|||||||
namespace AudioCore::Renderer {
|
namespace AudioCore::Renderer {
|
||||||
|
|
||||||
Manager::Manager(Core::System& system_)
|
Manager::Manager(Core::System& system_)
|
||||||
: system{system_}
|
: system{system_}, system_manager{std::make_unique<SystemManager>(system)} {
|
||||||
, system_manager{std::make_unique<SystemManager>(system)}
|
|
||||||
{
|
|
||||||
std::iota(session_ids.begin(), session_ids.end(), 0);
|
std::iota(session_ids.begin(), session_ids.end(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,12 +38,14 @@ Result Manager::GetWorkBufferSize(const AudioRendererParameterInternal& params,
|
|||||||
|
|
||||||
s32 Manager::GetSessionId() {
|
s32 Manager::GetSessionId() {
|
||||||
std::scoped_lock l{session_lock};
|
std::scoped_lock l{session_lock};
|
||||||
ASSERT(session_count <= session_ids.size());
|
auto session_id{session_ids[session_count]};
|
||||||
auto const session_id = session_ids[session_count];
|
|
||||||
if (session_id >= 0) {
|
if (session_id == -1) {
|
||||||
session_ids[session_count] = -1;
|
return -1;
|
||||||
session_count++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session_ids[session_count] = -1;
|
||||||
|
session_count++;
|
||||||
return session_id;
|
return session_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include "audio_core/opus/hardware_opus.h"
|
#include "audio_core/opus/hardware_opus.h"
|
||||||
#include "audio_core/opus/parameters.h"
|
#include "audio_core/opus/parameters.h"
|
||||||
#include "common/alignment.h"
|
#include "common/alignment.h"
|
||||||
#include "common/scope_exit.h"
|
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
|
|
||||||
@@ -29,18 +28,10 @@ OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_)
|
|||||||
OpusDecoder::~OpusDecoder() {
|
OpusDecoder::~OpusDecoder() {
|
||||||
if (decode_object_initialized) {
|
if (decode_object_initialized) {
|
||||||
hardware_opus.ShutdownDecodeObject(shared_buffer.data(), shared_buffer.size());
|
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) {
|
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};
|
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||||
shared_buffer.resize(transfer_memory_size);
|
shared_buffer.resize(transfer_memory_size);
|
||||||
shared_memory_mapped = true;
|
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) {
|
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};
|
auto frame_size{params.use_large_frame_size ? 5760 : 1920};
|
||||||
shared_buffer.resize(transfer_memory_size, 0);
|
shared_buffer.resize(transfer_memory_size, 0);
|
||||||
shared_memory_mapped = true;
|
shared_memory_mapped = true;
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "audio_core/audio_core.h"
|
#include "audio_core/audio_core.h"
|
||||||
@@ -46,25 +45,6 @@ HardwareOpus::HardwareOpus(Core::System& system_)
|
|||||||
opus_decoder.SetSharedMemory(shared_memory);
|
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) {
|
u32 HardwareOpus::GetWorkBufferSize(u32 channel) {
|
||||||
if (!opus_decoder.IsRunning()) {
|
if (!opus_decoder.IsRunning()) {
|
||||||
return 0;
|
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-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <opus.h>
|
#include <opus.h>
|
||||||
|
|
||||||
@@ -16,12 +12,9 @@
|
|||||||
#include "core/hle/service/audio/errors.h"
|
#include "core/hle/service/audio/errors.h"
|
||||||
|
|
||||||
namespace AudioCore::OpusDecoder {
|
namespace AudioCore::OpusDecoder {
|
||||||
class OpusDecoder;
|
|
||||||
class HardwareOpus {
|
class HardwareOpus {
|
||||||
public:
|
public:
|
||||||
HardwareOpus(Core::System& system);
|
HardwareOpus(Core::System& system);
|
||||||
Result RegisterDecoder(OpusDecoder* decoder);
|
|
||||||
void UnregisterDecoder(OpusDecoder* decoder);
|
|
||||||
|
|
||||||
u32 GetWorkBufferSize(u32 channel);
|
u32 GetWorkBufferSize(u32 channel);
|
||||||
u32 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
|
u32 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count);
|
||||||
@@ -46,7 +39,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
std::mutex mutex;
|
std::mutex mutex;
|
||||||
std::array<OpusDecoder*, 24> decoders{};
|
|
||||||
ADSP::OpusDecoder::OpusDecoder& opus_decoder;
|
ADSP::OpusDecoder::OpusDecoder& opus_decoder;
|
||||||
ADSP::OpusDecoder::SharedMemory shared_memory;
|
ADSP::OpusDecoder::SharedMemory shared_memory;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,18 +28,10 @@ namespace {
|
|||||||
//
|
//
|
||||||
// Keep in sync with cubeb_sink.cpp name.
|
// Keep in sync with cubeb_sink.cpp name.
|
||||||
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
|
SDL_SetHint("SDL_AUDIO_DEVICE_APP_NAME", "yuzu Latency Getter");
|
||||||
#ifdef __ANDROID__
|
|
||||||
SDL_SetHintWithPriority(SDL_HINT_AUDIO_DRIVER, "openslES", SDL_HINT_OVERRIDE);
|
|
||||||
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
if (!SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
||||||
LOG_WARNING(Audio_Sink, "OpenSL ES audio initialization failed: {}; retrying default drivers", SDL_GetError());
|
|
||||||
SDL_ResetHint(SDL_HINT_AUDIO_DRIVER);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (!SDL_WasInit(SDL_INIT_AUDIO) && !SDL_InitSubSystem(SDL_INIT_AUDIO)) {
|
|
||||||
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
LOG_CRITICAL(Audio_Sink, "SDL_InitSubSystem audio failed: {}", SDL_GetError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
LOG_INFO(Audio_Sink, "SDL audio driver: {}", SDL_GetCurrentAudioDriver());
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ add_library(
|
|||||||
param_package.h
|
param_package.h
|
||||||
parent_of_member.h
|
parent_of_member.h
|
||||||
point.h
|
point.h
|
||||||
|
quaternion.h
|
||||||
range_map.h
|
range_map.h
|
||||||
range_mutex.h
|
range_mutex.h
|
||||||
range_sets.h
|
range_sets.h
|
||||||
@@ -108,8 +109,6 @@ add_library(
|
|||||||
settings_setting.h
|
settings_setting.h
|
||||||
slot_vector.h
|
slot_vector.h
|
||||||
socket_types.h
|
socket_types.h
|
||||||
sparse_large_vector.cpp
|
|
||||||
sparse_large_vector.h
|
|
||||||
spin_lock.h
|
spin_lock.h
|
||||||
stb.cpp
|
stb.cpp
|
||||||
stb.h
|
stb.h
|
||||||
@@ -139,6 +138,8 @@ add_library(
|
|||||||
uuid.cpp
|
uuid.cpp
|
||||||
uuid.h
|
uuid.h
|
||||||
vector_math.h
|
vector_math.h
|
||||||
|
virtual_buffer.cpp
|
||||||
|
virtual_buffer.h
|
||||||
zstd_compression.cpp
|
zstd_compression.cpp
|
||||||
zstd_compression.h
|
zstd_compression.h
|
||||||
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
fs/ryujinx_compat.h fs/ryujinx_compat.cpp
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "common/fiber.h"
|
#include "common/fiber.h"
|
||||||
|
#include "common/virtual_buffer.h"
|
||||||
|
|
||||||
#include <boost/context/detail/fcontext.hpp>
|
#include <boost/context/detail/fcontext.hpp>
|
||||||
|
|
||||||
|
|||||||
@@ -23,8 +23,6 @@
|
|||||||
#define LOSSLESS_DIR "lossless"
|
#define LOSSLESS_DIR "lossless"
|
||||||
#define NAND_DIR "nand"
|
#define NAND_DIR "nand"
|
||||||
#define PLAY_TIME_DIR "play_time"
|
#define PLAY_TIME_DIR "play_time"
|
||||||
#define POST_PRESET_DIR "post_presets"
|
|
||||||
#define POST_SHADER_DIR "post_shaders"
|
|
||||||
#define SCREENSHOTS_DIR "screenshots"
|
#define SCREENSHOTS_DIR "screenshots"
|
||||||
#define SDMC_DIR "sdmc"
|
#define SDMC_DIR "sdmc"
|
||||||
#define SHADER_DIR "shader"
|
#define SHADER_DIR "shader"
|
||||||
|
|||||||
@@ -160,8 +160,6 @@ public:
|
|||||||
GenerateEdenPath(EdenPath::LosslessDir, eden_path / LOSSLESS_DIR);
|
GenerateEdenPath(EdenPath::LosslessDir, eden_path / LOSSLESS_DIR);
|
||||||
GenerateEdenPath(EdenPath::NANDDir, eden_path / NAND_DIR);
|
GenerateEdenPath(EdenPath::NANDDir, eden_path / NAND_DIR);
|
||||||
GenerateEdenPath(EdenPath::PlayTimeDir, eden_path / PLAY_TIME_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::SaveDir, eden_path / NAND_DIR);
|
||||||
GenerateEdenPath(EdenPath::ScreenshotsDir, eden_path / SCREENSHOTS_DIR);
|
GenerateEdenPath(EdenPath::ScreenshotsDir, eden_path / SCREENSHOTS_DIR);
|
||||||
GenerateEdenPath(EdenPath::SDMCDir, eden_path / SDMC_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.
|
LosslessDir, // Where the user-supplied Lossless Scaling library is stored.
|
||||||
NANDDir, // Where the emulated NAND is stored.
|
NANDDir, // Where the emulated NAND is stored.
|
||||||
PlayTimeDir, // Where play time data is stored.
|
PlayTimeDir, // Where play time data is stored.
|
||||||
PostPresetDir,
|
|
||||||
PostShaderDir, // Where user post-processing shaders are stored.
|
|
||||||
SaveDir, // Where save data is stored.
|
SaveDir, // Where save data is stored.
|
||||||
ScreenshotsDir, // Where yuzu screenshots are stored.
|
ScreenshotsDir, // Where yuzu screenshots are stored.
|
||||||
SDMCDir, // Where the emulated SDMC is 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) {
|
static void GetFuncAddress(Common::DynamicLibrary& dll, const char* name, T& pfn) {
|
||||||
if (!dll.GetSymbol(name, &pfn)) {
|
if (!dll.GetSymbol(name, &pfn)) {
|
||||||
LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
|
LOG_CRITICAL(HW_Memory, "Failed to load {}", name);
|
||||||
|
throw std::bad_alloc{};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,14 +179,6 @@ public:
|
|||||||
Release();
|
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) {
|
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||||
std::unique_lock lock{placeholder_mutex};
|
std::unique_lock lock{placeholder_mutex};
|
||||||
if (!IsNiechePlaceholder(virtual_offset, length)) {
|
if (!IsNiechePlaceholder(virtual_offset, length)) {
|
||||||
@@ -406,10 +399,6 @@ private:
|
|||||||
// For managarm: see https://github.com/managarm/managarm/issues/1370
|
// For managarm: see https://github.com/managarm/managarm/issues/1370
|
||||||
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
#else // ^^^ Windows ^^^ vvv POSIX vvv
|
||||||
|
|
||||||
#ifndef MAP_NOCORE
|
|
||||||
#define MAP_NOCORE 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ARCHITECTURE_arm64
|
#ifdef ARCHITECTURE_arm64
|
||||||
|
|
||||||
static void* ChooseVirtualBase(size_t virtual_size) {
|
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.
|
// Note: we may be able to take advantage of MAP_FIXED_NOREPLACE here.
|
||||||
void* map_pointer =
|
void* map_pointer =
|
||||||
mmap(reinterpret_cast<void*>(hint_address), virtual_size, PROT_READ | PROT_WRITE,
|
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 we successfully mapped, we're done.
|
||||||
if (reinterpret_cast<uintptr_t>(map_pointer) == hint_address) {
|
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) {
|
static void* ChooseVirtualBase(size_t virtual_size) {
|
||||||
#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__OpenBSD__) || defined(__sun__) || defined(__HAIKU__) || defined(__managarm__) || defined(__AIX__)
|
#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)
|
if (virtual_base != MAP_FAILED)
|
||||||
return virtual_base;
|
return virtual_base;
|
||||||
#endif
|
#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
|
#endif
|
||||||
@@ -552,13 +541,13 @@ public:
|
|||||||
}
|
}
|
||||||
if (use_anon) {
|
if (use_anon) {
|
||||||
LOG_WARNING(Common_Memory, "Using private mappings instead of shared ones");
|
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) {
|
if (fd > 0) {
|
||||||
fd = -1;
|
fd = -1;
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if (backing_base == MAP_FAILED) {
|
||||||
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
LOG_CRITICAL(HW_Memory, "mmap failed: {}", strerror(errno));
|
||||||
@@ -582,14 +571,6 @@ public:
|
|||||||
Release();
|
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) {
|
void Map(size_t virtual_offset, size_t host_offset, size_t length, MemoryPermission perms) {
|
||||||
// Intersect the range with our address space.
|
// Intersect the range with our address space.
|
||||||
AdjustMap(&virtual_offset, &length);
|
AdjustMap(&virtual_offset, &length);
|
||||||
@@ -710,10 +691,12 @@ HostMemory::HostMemory(size_t backing_size_, size_t virtual_size_)
|
|||||||
{
|
{
|
||||||
#if defined(__OPENORBIS__) || defined(__managarm__)
|
#if defined(__OPENORBIS__) || defined(__managarm__)
|
||||||
LOG_WARNING(HW_Memory, "Platform doesn't support fastmem");
|
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;
|
virtual_base = nullptr;
|
||||||
#else
|
#else
|
||||||
// Try to allocate a fastmem arena.
|
// 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);
|
impl = std::make_unique<HostMemory::Impl>(AlignUp(backing_size, PageAlignment), AlignUp(virtual_size, PageAlignment) + HugePageSize);
|
||||||
if (impl->Init()) {
|
if (impl->Init()) {
|
||||||
backing_base = impl->backing_base;
|
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;
|
virtual_base_offset = virtual_base - impl->virtual_base;
|
||||||
}
|
}
|
||||||
} else {
|
} 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();
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
HostMemory::~HostMemory() {
|
HostMemory::~HostMemory() = default;
|
||||||
#ifdef _WIN32
|
|
||||||
if (fallback_buffer) {
|
|
||||||
VirtualFree(backing_base, backing_size, MEM_RELEASE);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (fallback_buffer) {
|
|
||||||
munmap(backing_base, backing_size);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
HostMemory::HostMemory(HostMemory&&) noexcept = default;
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
|
#include "common/virtual_buffer.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ private:
|
|||||||
u8* virtual_base{};
|
u8* virtual_base{};
|
||||||
size_t virtual_base_offset{};
|
size_t virtual_base_offset{};
|
||||||
// Windows requires it for kernels whom lack proper support for some functions!
|
// 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
|
} // 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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
|
||||||
@@ -13,11 +13,39 @@ PageTable::PageTable() = default;
|
|||||||
|
|
||||||
PageTable::~PageTable() noexcept = default;
|
PageTable::~PageTable() noexcept = default;
|
||||||
|
|
||||||
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_bits) {
|
bool PageTable::BeginTraversal(TraversalEntry* out_entry, TraversalContext* out_context,
|
||||||
auto const num_page_table_entries = 1ULL << (address_space_width_in_bits - page_bits);
|
Common::ProcessAddress address) const {
|
||||||
entries.ResizeAndClear(num_page_table_entries);
|
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_address_space_width_in_bits = address_space_width_in_bits;
|
||||||
current_page_bits = page_bits;
|
page_size = 1ULL << page_size_in_bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|||||||
+56
-64
@@ -9,22 +9,22 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/sparse_large_vector.h"
|
|
||||||
#include "common/typed_address.h"
|
#include "common/typed_address.h"
|
||||||
|
#include "common/virtual_buffer.h"
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
enum class PageType : u8 {
|
enum class PageType : u8 {
|
||||||
/// Page is unmapped and should cause an access error.
|
/// 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.
|
/// 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
|
/// Page is mapped to regular memory, but inaccessible from CPU fastmem and must use
|
||||||
/// the callbacks.
|
/// the callbacks.
|
||||||
DebugMemory = 0b10,
|
DebugMemory,
|
||||||
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
/// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
|
||||||
/// invalidation
|
/// invalidation
|
||||||
RasterizerCachedMemory = 0b11,
|
RasterizerCachedMemory,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,86 +42,57 @@ struct PageTable {
|
|||||||
u64 next_offset{};
|
u64 next_offset{};
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Masks out bits reserved for attribute tagging.
|
/// Number of bits reserved for attribute tagging.
|
||||||
static constexpr u64 ATTRIBUTE_MASK = ((1ULL << 44) - 1) << 12;
|
/// This can be at most the guaranteed alignment of the pointers in the page table.
|
||||||
|
static constexpr int ATTRIBUTE_BITS = 2;
|
||||||
/// Specifies sign bit for page table entries.
|
|
||||||
static constexpr u64 SIGN_BIT = 45 + 12; // 44 bits of data + page offset
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Atomic tuple of host pointer, page type, and block id.
|
* Pair of host pointer and page type attribute.
|
||||||
* This uses the lower bits of a given pointer to store the attributes.
|
* 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
|
* 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.
|
* call. In other words, they are guaranteed to be synchronized at all times.
|
||||||
*/
|
*/
|
||||||
class PageEntryData {
|
class PageInfo {
|
||||||
public:
|
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
|
/// Returns the page pointer
|
||||||
[[nodiscard]] uintptr_t Pointer(bool ignored_marked = false) const noexcept {
|
[[nodiscard]] uintptr_t Pointer() const noexcept {
|
||||||
return ExtractPointer(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)), ignored_marked);
|
return ExtractPointer(raw.load(std::memory_order_relaxed));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the page type attribute
|
/// Returns the page type attribute
|
||||||
[[nodiscard]] PageType Type() const noexcept {
|
[[nodiscard]] PageType Type() const noexcept {
|
||||||
return static_cast<PageType>(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)).type);
|
return ExtractType(raw.load(std::memory_order_relaxed));
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the block identifier.
|
|
||||||
[[nodiscard]] u16 Block() const noexcept {
|
|
||||||
return ExtractBlock(std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the page pointer and attribute pair, extracted from the same atomic read
|
/// 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 {
|
[[nodiscard]] std::pair<uintptr_t, PageType> PointerType() const noexcept {
|
||||||
const auto non_atomic_raw = std::bit_cast<Data>(data_raw.load(std::memory_order_relaxed));
|
const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
|
||||||
return {ExtractPointer(non_atomic_raw, ignore_marked), static_cast<PageType>(non_atomic_raw.type), ExtractBlock(non_atomic_raw)};
|
return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write page info atomically
|
/// Returns the raw representation of the page information.
|
||||||
constexpr void Store(bool marked, PageType type, u16 block, uintptr_t pointer) noexcept {
|
/// Use ExtractPointer and ExtractType to unpack the value.
|
||||||
data_raw.store(std::bit_cast<u64>(Data{marked, type, block, pointer}));
|
[[nodiscard]] uintptr_t Raw() const noexcept {
|
||||||
|
return raw.load(std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void MarkRasterizerCached() noexcept {
|
/// Write a page pointer and type pair atomically
|
||||||
data_raw.fetch_or(0b111);
|
void Store(uintptr_t pointer, PageType type) noexcept {
|
||||||
}
|
raw.store(pointer | uintptr_t(type));
|
||||||
|
|
||||||
constexpr void MarkDebug(u64 ptr, u16 block) noexcept {
|
|
||||||
Store(true, PageType::DebugMemory, block, ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unpack a pointer from a page info raw representation
|
/// Unpack a pointer from a page info raw representation
|
||||||
[[nodiscard]] static uintptr_t ExtractPointer(Data raw, bool ignore_marked = false) noexcept {
|
[[nodiscard]] static uintptr_t ExtractPointer(uintptr_t raw) noexcept {
|
||||||
return raw.marked && !ignore_marked ? 0
|
return raw & (~uintptr_t{0} << ATTRIBUTE_BITS);
|
||||||
// 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 u16 ExtractBlock(Data raw) noexcept {
|
/// Unpack a page type from a page info raw representation
|
||||||
return static_cast<u16>(raw.block | (raw.block2 << 9));
|
[[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
|
||||||
|
return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::atomic<u64> data_raw;
|
std::atomic<uintptr_t> raw;
|
||||||
static_assert(sizeof(Data) == sizeof(std::atomic<u64>));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PageTable();
|
PageTable();
|
||||||
@@ -129,8 +100,13 @@ struct PageTable {
|
|||||||
|
|
||||||
PageTable(const PageTable&) = delete;
|
PageTable(const PageTable&) = delete;
|
||||||
PageTable& operator=(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
|
* 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;
|
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
|
/// Vector of memory pointers backing each page. An entry can only be non-null if the
|
||||||
/// corresponding attribute element is of type `Memory`.
|
/// corresponding attribute element is of type `Memory`.
|
||||||
SparseLargeVector<PageEntryData> entries;
|
struct PageEntryData {
|
||||||
static_assert(sizeof(PageEntryData) == 8);
|
PageInfo ptr;
|
||||||
|
u64 block;
|
||||||
|
u64 addr;
|
||||||
|
u64 padding;
|
||||||
|
};
|
||||||
|
VirtualBuffer<PageEntryData> entries;
|
||||||
|
static_assert(sizeof(PageEntryData) == 32);
|
||||||
|
|
||||||
u8* fastmem_arena{};
|
u8* fastmem_arena{};
|
||||||
std::size_t current_address_space_width_in_bits{};
|
std::size_t current_address_space_width_in_bits{};
|
||||||
std::size_t current_page_bits{};
|
std::size_t page_size{};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Common
|
} // 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
|
||||||
+2
-26
@@ -388,30 +388,6 @@ struct Values {
|
|||||||
true,
|
true,
|
||||||
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,
|
SwitchableSetting<bool> frame_gen{linkage, false, "frame_gen", Category::Renderer,
|
||||||
Specialization::Default, true, false};
|
Specialization::Default, true, false};
|
||||||
|
|
||||||
@@ -882,7 +858,7 @@ struct Values {
|
|||||||
SwitchableSetting<std::string> program_args{linkage,
|
SwitchableSetting<std::string> program_args{linkage,
|
||||||
std::string(),
|
std::string(),
|
||||||
"program_args",
|
"program_args",
|
||||||
Category::Debugging,
|
Category::System,
|
||||||
Specialization::Default,
|
Specialization::Default,
|
||||||
true, // save_ - persist in config file
|
true, // save_ - persist in config file
|
||||||
false}; // runtime_modifiable_ - startup-only
|
false}; // runtime_modifiable_ - startup-only
|
||||||
@@ -928,7 +904,7 @@ struct Values {
|
|||||||
0,
|
0,
|
||||||
65535,
|
65535,
|
||||||
"debug_knobs",
|
"debug_knobs",
|
||||||
Category::Debugging,
|
Category::System,
|
||||||
Specialization::Countable,
|
Specialization::Countable,
|
||||||
true,
|
true,
|
||||||
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-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: 2014 Tony Wasserka
|
// SPDX-FileCopyrightText: 2014 Tony Wasserka
|
||||||
@@ -7,128 +7,752 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __ARM_NEON
|
||||||
|
#include <arm_neon.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
|
|
||||||
template <typename T, size_t N>
|
template <typename T>
|
||||||
class Vec {
|
class Vec2;
|
||||||
|
template <typename T>
|
||||||
|
class Vec3;
|
||||||
|
template <typename T>
|
||||||
|
class Vec4;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class Vec2 {
|
||||||
public:
|
public:
|
||||||
std::array<T, N> elems{};
|
T x{};
|
||||||
|
T y{};
|
||||||
|
|
||||||
constexpr Vec() = default;
|
constexpr Vec2() = default;
|
||||||
constexpr Vec(T e0) noexcept : elems{e0} {}
|
constexpr Vec2(const T& x_, const T& y_) : x(x_), y(y_) {}
|
||||||
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_} {}
|
|
||||||
|
|
||||||
[[nodiscard]] constexpr Vec<decltype(T{} + T{}), N> operator+(const Vec o) const noexcept {
|
template <typename T2>
|
||||||
Vec<decltype(T{} + T{}), N> r{};
|
[[nodiscard]] constexpr Vec2<T2> Cast() const {
|
||||||
for (size_t i = 0; i < N; ++i)
|
return Vec2<T2>(static_cast<T2>(x), static_cast<T2>(y));
|
||||||
r.elems[i] = elems[i] + o.elems[i];
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
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 {
|
[[nodiscard]] static constexpr Vec2 AssignToAll(const T& f) {
|
||||||
Vec<decltype(T{} - T{}), N> r{};
|
return Vec2{f, f};
|
||||||
for (size_t i = 0; i < N; ++i)
|
}
|
||||||
r.elems[i] = elems[i] - o.elems[i];
|
|
||||||
return r;
|
[[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>
|
template <typename U = T>
|
||||||
[[nodiscard]] constexpr Vec<std::enable_if_t<std::is_signed_v<U>, U>, N> operator-() const noexcept {
|
[[nodiscard]] constexpr Vec2<std::enable_if_t<std::is_signed_v<U>, U>> operator-() const {
|
||||||
Vec<U, N> r{};
|
return {-x, -y};
|
||||||
for (size_t i = 0; i < N; ++i)
|
}
|
||||||
r.elems[i] = -elems[i];
|
[[nodiscard]] constexpr Vec2<decltype(T{} * T{})> operator*(const Vec2& other) const {
|
||||||
return r;
|
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>
|
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 TV = decltype(T{} * V{});
|
||||||
using C = std::common_type_t<T, V>;
|
using C = std::common_type_t<T, V>;
|
||||||
Vec<TV, N> r{};
|
|
||||||
for (size_t i = 0; i < N; ++i)
|
return {
|
||||||
r.elems[i] = TV(C(elems[i]) * C(f));
|
static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
|
||||||
return r;
|
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>
|
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 TV = decltype(T{} / V{});
|
||||||
using C = std::common_type_t<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 {
|
return {
|
||||||
1.0f - 2.0f * (y2 + z2),
|
static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
|
||||||
2.0f * (xy + wz),
|
static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
|
||||||
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 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>
|
template <typename T, typename V>
|
||||||
[[nodiscard]] constexpr Vec<T, N> operator*(const V f, const Vec<T, N> v) noexcept {
|
[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
|
||||||
using C = std::common_type_t<T, V>;
|
using C = std::common_type_t<T, V>;
|
||||||
Vec<T, N> r{};
|
|
||||||
for (size_t i = 0; i < N; ++i)
|
return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
|
||||||
r.elems[i] = T(C(f) * C(v.elems[i]));
|
static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)));
|
||||||
return r;
|
}
|
||||||
|
|
||||||
|
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
|
} // 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/kernel_helpers.h
|
||||||
hle/service/lbl/lbl.cpp
|
hle/service/lbl/lbl.cpp
|
||||||
hle/service/lbl/lbl.h
|
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.cpp
|
||||||
hle/service/ldn/lan_discovery.h
|
hle/service/ldn/lan_discovery.h
|
||||||
hle/service/ldn/ldn.cpp
|
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_control.h
|
||||||
hle/service/ns/system_update_interface.cpp
|
hle/service/ns/system_update_interface.cpp
|
||||||
hle/service/ns/system_update_interface.h
|
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.cpp
|
||||||
hle/service/nvdrv/core/container.h
|
hle/service/nvdrv/core/container.h
|
||||||
hle/service/nvdrv/core/heap_mapper.cpp
|
hle/service/nvdrv/core/heap_mapper.cpp
|
||||||
@@ -1063,6 +1067,8 @@ add_library(core STATIC
|
|||||||
hle/service/sockets/sockets.h
|
hle/service/sockets/sockets.h
|
||||||
hle/service/sockets/sockets_translate.cpp
|
hle/service/sockets/sockets_translate.cpp
|
||||||
hle/service/sockets/sockets_translate.h
|
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.cpp
|
||||||
hle/service/spl/spl.h
|
hle/service/spl/spl.h
|
||||||
hle/service/spl/spl_module.cpp
|
hle/service/spl/spl_module.cpp
|
||||||
@@ -1077,6 +1083,8 @@ add_library(core STATIC
|
|||||||
hle/service/ssl/ssl_types.h
|
hle/service/ssl/ssl_types.h
|
||||||
hle/service/tma/tma.cpp
|
hle/service/tma/tma.cpp
|
||||||
hle/service/tma/tma.h
|
hle/service/tma/tma.h
|
||||||
|
hle/service/ulsf/ulsf.cpp
|
||||||
|
hle/service/ulsf/ulsf.h
|
||||||
hle/service/usb/usb.cpp
|
hle/service/usb/usb.cpp
|
||||||
hle/service/usb/usb.h
|
hle/service/usb/usb.h
|
||||||
hle/service/vi/application_display_service.cpp
|
hle/service/vi/application_display_service.cpp
|
||||||
|
|||||||
@@ -172,12 +172,12 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
|||||||
if (page_table) {
|
if (page_table) {
|
||||||
constexpr size_t PageBits = 12;
|
constexpr size_t PageBits = 12;
|
||||||
constexpr size_t NumPageTableEntries = 1 << (32 - PageBits);
|
constexpr size_t NumPageTableEntries = 1 << (32 - PageBits);
|
||||||
|
constexpr size_t PageLog2Stride = 5;
|
||||||
|
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
|
||||||
|
|
||||||
// Dynarmic will not write to the page table, const_cast is safe here
|
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(page_table->entries.data());
|
||||||
config.page_table = reinterpret_cast<std::array<std::uint8_t*, NumPageTableEntries>*>(
|
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
config.page_table_log2_stride = PageLog2Stride;
|
||||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
|
||||||
config.page_table_marked_bit = 0;
|
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
config.only_detect_misalignment_via_page_table_on_page_boundary = true;
|
||||||
@@ -188,13 +188,6 @@ void ArmDynarmic32::MakeJit(Common::PageTable* page_table) {
|
|||||||
|
|
||||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||||
config.recompile_on_exclusive_fastmem_failure = true;
|
config.recompile_on_exclusive_fastmem_failure = true;
|
||||||
|
|
||||||
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
|
|
||||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
|
||||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
|
||||||
// we have to manually sign extend when our actual pointer is negative.
|
|
||||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-process state
|
// Multi-process state
|
||||||
@@ -427,7 +420,6 @@ void ArmDynarmic32::SignalInterrupt(Kernel::KThread* thread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArmDynarmic32::ClearInstructionCache() {
|
void ArmDynarmic32::ClearInstructionCache() {
|
||||||
m_cb->last_code_addr = u64(-1);
|
|
||||||
m_jit->ClearCache();
|
m_jit->ClearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -211,12 +211,13 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
|||||||
|
|
||||||
// Memory
|
// Memory
|
||||||
if (page_table) {
|
if (page_table) {
|
||||||
// Dynarmic will not write to the page table, const_cast is safe here
|
constexpr size_t PageLog2Stride = 5;
|
||||||
config.page_table = reinterpret_cast<void**>(
|
static_assert(1 << PageLog2Stride == sizeof(Common::PageTable::PageEntryData));
|
||||||
const_cast<Common::PageTable::PageEntryData*>(page_table->entries.data()));
|
|
||||||
|
config.page_table = reinterpret_cast<void**>(page_table->entries.data());
|
||||||
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
config.page_table_address_space_bits = std::uint32_t(address_space_bits);
|
||||||
config.page_table_pointer_mask = Common::PageTable::ATTRIBUTE_MASK;
|
config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
|
||||||
config.page_table_marked_bit = 0;
|
config.page_table_log2_stride = PageLog2Stride;
|
||||||
config.silently_mirror_page_table = false;
|
config.silently_mirror_page_table = false;
|
||||||
config.absolute_offset_page_table = true;
|
config.absolute_offset_page_table = true;
|
||||||
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
|
||||||
@@ -230,13 +231,6 @@ void ArmDynarmic64::MakeJit(Common::PageTable* page_table, std::size_t address_s
|
|||||||
|
|
||||||
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
config.fastmem_exclusive_access = config.fastmem_pointer != std::nullopt;
|
||||||
config.recompile_on_exclusive_fastmem_failure = true;
|
config.recompile_on_exclusive_fastmem_failure = true;
|
||||||
|
|
||||||
if (reinterpret_cast<u64>(m_system.DeviceMemory().buffer.BackingBasePointer() +
|
|
||||||
Kernel::Board::Nintendo::Nx::KSystemControl::Init::GetIntendedMemorySize()) < (1ULL << 39)) {
|
|
||||||
// Systems like FreeBSD allocate memory really low by default, and since we pack our page table entries,
|
|
||||||
// we have to manually sign extend when our actual pointer is negative.
|
|
||||||
config.page_table_sign_extension = Common::PageTable::SIGN_BIT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi-process state
|
// Multi-process state
|
||||||
@@ -453,7 +447,6 @@ void ArmDynarmic64::SignalInterrupt(Kernel::KThread* thread) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ArmDynarmic64::ClearInstructionCache() {
|
void ArmDynarmic64::ClearInstructionCache() {
|
||||||
m_cb->last_code_addr = u64(-1);
|
|
||||||
m_jit->ClearCache();
|
m_jit->ClearCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,10 +121,7 @@ union Exclusive {
|
|||||||
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
|
constexpr explicit Exclusive(u32 raw_) : raw{raw_} {}
|
||||||
|
|
||||||
constexpr bool Verify() {
|
constexpr bool Verify() {
|
||||||
if (this->GetSig() != 0x10) return false;
|
return this->GetSig() == 0x10;
|
||||||
const bool pair = decltype(l)::ExtractValue(raw) & 1;
|
|
||||||
const bool fixed_rt2 = decltype(rt2)::ExtractValue(raw) == 0b11111;
|
|
||||||
return pair || fixed_rt2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr u32 GetSig() {
|
constexpr u32 GetSig() {
|
||||||
|
|||||||
@@ -312,10 +312,6 @@ struct System::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) {
|
SystemResultStatus Load(System& system, Frontend::EmuWindow& emu_window, const std::string& filepath, Service::AM::FrontendAppletParameters& params) {
|
||||||
if (params.launch_type == Service::AM::LaunchType::FrontendInitiated) {
|
|
||||||
fs_controller.InitTempStorage();
|
|
||||||
}
|
|
||||||
|
|
||||||
InitializeKernel(system);
|
InitializeKernel(system);
|
||||||
|
|
||||||
if (params.applet_type == Service::AM::AppletType::Application) {
|
if (params.applet_type == Service::AM::AppletType::Application) {
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -29,11 +26,8 @@ public:
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const {
|
Common::PhysicalAddress GetPhysicalAddr(const T* ptr) const {
|
||||||
return GetPhysicalAddr(reinterpret_cast<uintptr_t>(ptr));
|
return (reinterpret_cast<uintptr_t>(ptr) -
|
||||||
}
|
reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
||||||
|
|
||||||
Common::PhysicalAddress GetPhysicalAddr(uintptr_t ptr) const {
|
|
||||||
return (ptr - reinterpret_cast<uintptr_t>(buffer.BackingBasePointer())) +
|
|
||||||
DramMemoryMap::Base;
|
DramMemoryMap::Base;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/range_mutex.h"
|
#include "common/range_mutex.h"
|
||||||
#include "common/scratch_buffer.h"
|
#include "common/scratch_buffer.h"
|
||||||
#include "common/sparse_large_vector.h"
|
#include "common/virtual_buffer.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
@@ -178,8 +178,8 @@ private:
|
|||||||
u32 continuity_tracker;
|
u32 continuity_tracker;
|
||||||
u32 compressed_physical_ptr;
|
u32 compressed_physical_ptr;
|
||||||
};
|
};
|
||||||
Common::SparseLargeVector<u32> compressed_device_addr;
|
Common::VirtualBuffer<u32> compressed_device_addr;
|
||||||
Common::SparseLargeVector<TrackedEntry> tracked_entries;
|
Common::VirtualBuffer<TrackedEntry> tracked_entries;
|
||||||
|
|
||||||
// Process memory interfaces
|
// Process memory interfaces
|
||||||
|
|
||||||
@@ -200,8 +200,8 @@ private:
|
|||||||
return std::make_pair(asid, address);
|
return std::make_pair(asid, address);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
void InsertCPUBacking(size_t page_index, VAddr address, Asid asid) {
|
||||||
tracked_entries.GetUnchecked(page_index).cpu_backing_address = address | (asid.id << asid_start_bit);
|
tracked_entries[page_index].cpu_backing_address = address | (asid.id << asid_start_bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<TranslationEntry, 4> t_slot{};
|
std::array<TranslationEntry, 4> t_slot{};
|
||||||
|
|||||||
@@ -177,6 +177,17 @@ DeviceMemoryManager<Traits>::DeviceMemoryManager(const DeviceMemory& device_memo
|
|||||||
{
|
{
|
||||||
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
|
impl = std::make_unique<DeviceMemoryManagerAllocator<Traits>>();
|
||||||
cached_pages = std::make_unique<CachedPages>();
|
cached_pages = std::make_unique<CachedPages>();
|
||||||
|
|
||||||
|
const size_t total_virtual = device_as_size >> Memory::YUZU_PAGEBITS;
|
||||||
|
for (size_t i = 0; i < total_virtual; i++) {
|
||||||
|
tracked_entries[i].compressed_physical_ptr = 0;
|
||||||
|
tracked_entries[i].continuity_tracker = 1;
|
||||||
|
tracked_entries[i].cpu_backing_address = 0;
|
||||||
|
}
|
||||||
|
const size_t total_phys = 1ULL << ((Settings::values.memory_layout_mode.GetValue() == Settings::MemoryLayout::Memory_4Gb ? physical_min_bits : physical_max_bits) - Memory::YUZU_PAGEBITS);
|
||||||
|
for (size_t i = 0; i < total_phys; i++) {
|
||||||
|
compressed_device_addr[i] = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
@@ -209,28 +220,26 @@ void DeviceMemoryManager<Traits>::Map(DAddr address, VAddr virtual_address, size
|
|||||||
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
|
size_t start_page_d = address >> Memory::YUZU_PAGEBITS;
|
||||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||||
std::scoped_lock lk(mapping_guard);
|
std::scoped_lock lk(mapping_guard);
|
||||||
|
|
||||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
|
|
||||||
for (size_t i = 0; i < num_pages; i++) {
|
for (size_t i = 0; i < num_pages; i++) {
|
||||||
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
|
const VAddr new_vaddress = virtual_address + i * Memory::YUZU_PAGESIZE;
|
||||||
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
|
auto* ptr = process_memory->GetPointerSilent(Common::ProcessAddress(new_vaddress));
|
||||||
if (ptr == nullptr) [[unlikely]] {
|
if (ptr == nullptr) [[unlikely]] {
|
||||||
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = 0;
|
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
|
auto phys_addr = static_cast<u32>(GetRawPhysicalAddr(ptr) >> Memory::YUZU_PAGEBITS) + 1U;
|
||||||
tracked_entries.GetUnchecked(start_page_d + i).compressed_physical_ptr = phys_addr;
|
tracked_entries[start_page_d + i].compressed_physical_ptr = phys_addr;
|
||||||
InsertCPUBacking(start_page_d + i, new_vaddress, asid);
|
InsertCPUBacking(start_page_d + i, new_vaddress, asid);
|
||||||
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||||
const u32 new_dev = static_cast<u32>(start_page_d + i);
|
const u32 new_dev = static_cast<u32>(start_page_d + i);
|
||||||
if (base_dev == 0) [[likely]] {
|
if (base_dev == 0) [[likely]] {
|
||||||
compressed_device_addr.GetAndFault(phys_addr - 1U) = new_dev;
|
compressed_device_addr[phys_addr - 1U] = new_dev;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
u32 start_id = base_dev & MULTI_MASK;
|
u32 start_id = base_dev & MULTI_MASK;
|
||||||
if ((base_dev >> MULTI_FLAG_BITS) == 0) {
|
if ((base_dev >> MULTI_FLAG_BITS) == 0) {
|
||||||
start_id = impl->multi_dev_address.Register(base_dev);
|
start_id = impl->multi_dev_address.Register(base_dev);
|
||||||
compressed_device_addr.GetAndFault(phys_addr - 1U) = MULTI_FLAG | start_id;
|
compressed_device_addr[phys_addr - 1U] = MULTI_FLAG | start_id;
|
||||||
}
|
}
|
||||||
impl->multi_dev_address.Register(new_dev, start_id);
|
impl->multi_dev_address.Register(new_dev, start_id);
|
||||||
}
|
}
|
||||||
@@ -246,26 +255,24 @@ void DeviceMemoryManager<Traits>::Unmap(DAddr address, size_t size) {
|
|||||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||||
device_inter->InvalidateRegion(address, size);
|
device_inter->InvalidateRegion(address, size);
|
||||||
std::scoped_lock lk(mapping_guard);
|
std::scoped_lock lk(mapping_guard);
|
||||||
|
|
||||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages); // should already be committed, but just in case
|
|
||||||
for (size_t i = 0; i < num_pages; i++) {
|
for (size_t i = 0; i < num_pages; i++) {
|
||||||
auto& entry = tracked_entries.GetUnchecked(start_page_d + i);
|
auto phys_addr = tracked_entries[start_page_d + i].compressed_physical_ptr;
|
||||||
auto phys_addr = entry.compressed_physical_ptr;
|
tracked_entries[start_page_d + i].compressed_physical_ptr = 0;
|
||||||
entry.compressed_physical_ptr = 0;
|
tracked_entries[start_page_d + i].cpu_backing_address = 0;
|
||||||
entry.cpu_backing_address = 0;
|
|
||||||
if (phys_addr != 0) [[likely]] {
|
if (phys_addr != 0) [[likely]] {
|
||||||
u32& base_dev = compressed_device_addr.GetAndFault(phys_addr - 1U);
|
const u32 base_dev = compressed_device_addr[phys_addr - 1U];
|
||||||
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
|
if ((base_dev >> MULTI_FLAG_BITS) == 0) [[likely]] {
|
||||||
base_dev = 0;
|
compressed_device_addr[phys_addr - 1] = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
|
const auto [more_entries, new_start] = impl->multi_dev_address.Unregister(
|
||||||
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
|
static_cast<u32>(start_page_d + i), base_dev & MULTI_MASK);
|
||||||
if (!more_entries) {
|
if (!more_entries) {
|
||||||
base_dev = impl->multi_dev_address.ReleaseEntry(new_start);
|
compressed_device_addr[phys_addr - 1] =
|
||||||
|
impl->multi_dev_address.ReleaseEntry(new_start);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
base_dev = new_start | MULTI_FLAG;
|
compressed_device_addr[phys_addr - 1] = new_start | MULTI_FLAG;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t_slot = {};
|
t_slot = {};
|
||||||
@@ -278,8 +285,6 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
|
|||||||
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
size_t num_pages = Common::AlignUp(size, Memory::YUZU_PAGESIZE) >> Memory::YUZU_PAGEBITS;
|
||||||
uintptr_t last_ptr = 0;
|
uintptr_t last_ptr = 0;
|
||||||
size_t page_count = 1;
|
size_t page_count = 1;
|
||||||
|
|
||||||
tracked_entries.CommitRegion(start_page_d, start_page_d + num_pages);
|
|
||||||
for (size_t i = num_pages; i > 0; i--) {
|
for (size_t i = num_pages; i > 0; i--) {
|
||||||
size_t index = i - 1;
|
size_t index = i - 1;
|
||||||
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE;
|
const VAddr new_vaddress = virtual_address + index * Memory::YUZU_PAGESIZE;
|
||||||
@@ -291,14 +296,14 @@ void DeviceMemoryManager<Traits>::TrackContinuityImpl(DAddr address, VAddr virtu
|
|||||||
page_count = 1;
|
page_count = 1;
|
||||||
}
|
}
|
||||||
last_ptr = new_ptr;
|
last_ptr = new_ptr;
|
||||||
tracked_entries.GetUnchecked(start_page_d + index).continuity_tracker = static_cast<u32>(page_count) - 1;
|
tracked_entries[start_page_d + index].continuity_tracker = static_cast<u32>(page_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
template <typename Traits>
|
template <typename Traits>
|
||||||
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
|
u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) {
|
||||||
size_t page_index = src_addr >> page_bits;
|
size_t page_index = src_addr >> page_bits;
|
||||||
size_t subbits = src_addr & page_mask;
|
size_t subbits = src_addr & page_mask;
|
||||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) {
|
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
|
||||||
return GetPointer<u8>(src_addr);
|
return GetPointer<u8>(src_addr);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -308,7 +313,7 @@ template <typename Traits>
|
|||||||
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
|
const u8* DeviceMemoryManager<Traits>::GetSpan(const DAddr src_addr, const std::size_t size) const {
|
||||||
size_t page_index = src_addr >> page_bits;
|
size_t page_index = src_addr >> page_bits;
|
||||||
size_t subbits = src_addr & page_mask;
|
size_t subbits = src_addr & page_mask;
|
||||||
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker+1) << page_bits) >= size + subbits) {
|
if ((static_cast<size_t>(tracked_entries[page_index].continuity_tracker) << page_bits) >= size + subbits) {
|
||||||
return GetPointer<u8>(src_addr);
|
return GetPointer<u8>(src_addr);
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
@@ -378,7 +383,7 @@ void DeviceMemoryManager<Traits>::WalkBlock(DAddr addr, std::size_t size, auto o
|
|||||||
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
|
std::size_t page_index = addr >> Memory::YUZU_PAGEBITS;
|
||||||
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
|
std::size_t page_offset = addr & Memory::YUZU_PAGEMASK;
|
||||||
while (remaining_size) {
|
while (remaining_size) {
|
||||||
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker+1);
|
const size_t next_pages = std::size_t(tracked_entries[page_index].continuity_tracker);
|
||||||
const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
|
const std::size_t copy_amount = (std::min)((next_pages << Memory::YUZU_PAGEBITS) - page_offset, remaining_size);
|
||||||
const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset);
|
const auto current_vaddr = u64((page_index << Memory::YUZU_PAGEBITS) + page_offset);
|
||||||
SCOPE_EXIT{
|
SCOPE_EXIT{
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
@@ -124,7 +121,6 @@ size_t HierarchicalIntegrityVerificationStorage::Read(u8* buffer, size_t size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t HierarchicalIntegrityVerificationStorage::GetSize() const {
|
size_t HierarchicalIntegrityVerificationStorage::GetSize() const {
|
||||||
ASSERT(m_data_size >= 0);
|
|
||||||
return m_data_size;
|
return m_data_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
|
|||||||
|
|
||||||
static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
static IPSwitchRecord EscapeStringSequences(std::string_view sv) {
|
||||||
IPSwitchRecord r{};
|
IPSwitchRecord r{};
|
||||||
for (auto it = sv.cbegin(); it < sv.cend(); ) {
|
for (auto it = sv.cbegin(); it != sv.cend(); ) {
|
||||||
if (*it == '\\' && it + 1 < sv.cend()) {
|
if (*it == '\\' && it + 1 < sv.cend()) {
|
||||||
switch (it[1]) {
|
switch (it[1]) {
|
||||||
case 'a': r.data[r.count] = '\a'; break;
|
case 'a': r.data[r.count] = '\a'; break;
|
||||||
@@ -198,8 +198,6 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
|||||||
LOG_WARNING(Loader, "Unknown flag {}", line);
|
LOG_WARNING(Loader, "Unknown flag {}", line);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (patches.empty()) {
|
|
||||||
LOG_WARNING(Loader, "Invalid line not in a patch {}", line);
|
|
||||||
} else {
|
} else {
|
||||||
size_t offset = size_t(std::strtoul(line.data(), nullptr, 16));
|
size_t offset = size_t(std::strtoul(line.data(), nullptr, 16));
|
||||||
offset += size_t(offset_shift);
|
offset += size_t(offset_shift);
|
||||||
@@ -255,30 +253,25 @@ void IPSwitchCompiler::Parse(std::span<u8 const> bytes) {
|
|||||||
// now make a nominal preprocessed line: remove comments
|
// now make a nominal preprocessed line: remove comments
|
||||||
char quote = '\0';
|
char quote = '\0';
|
||||||
auto const sline_start = p;
|
auto const sline_start = p;
|
||||||
auto last_char = p;
|
|
||||||
for (; p < sline.cend(); ) {
|
for (; p < sline.cend(); ) {
|
||||||
// we dont check for "//", IPS checks for '/' only...
|
// we dont check for "//", IPS checks for '/' only...
|
||||||
if (std::isspace(*p)) {
|
if ((!quote && p[0] == '/')
|
||||||
++p;
|
|
||||||
} else if ((!quote && p[0] == '/')
|
|
||||||
|| (!quote && p[0] == '#')) {
|
|| (!quote && p[0] == '#')) {
|
||||||
break;
|
break;
|
||||||
} else if (p[0] == '\"' || p[0] == '\'') {
|
} else if (p[0] == '\"' || p[0] == '\'') {
|
||||||
quote = (p[0] == quote) ? '\0' : p[0];
|
quote = (p[0] == quote) ? '\0' : p[0];
|
||||||
++p;
|
++p;
|
||||||
last_char = p;
|
|
||||||
} else if (p + 1 < sline.cend() && p[0] == '\\') {
|
} else if (p + 1 < sline.cend() && p[0] == '\\') {
|
||||||
p += 2;
|
p += 2;
|
||||||
last_char = p;
|
|
||||||
} else {
|
} else {
|
||||||
++p;
|
++p;
|
||||||
last_char = p;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// now we have the preprocessed string ;)
|
// now we have the preprocessed string ;)
|
||||||
std::string_view const pp_str(sline_start, last_char);
|
std::string_view pp_str(sline_start, p);
|
||||||
if (pp_str.size() > 0 && !parse_line(pp_str))
|
if (pp_str.size() > 0 && !parse_line(pp_str)) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,11 @@ std::string GetFutureSaveDataPath(SaveDataSpaceId space_id, SaveDataType type, u
|
|||||||
|
|
||||||
SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
|
SaveDataFactory::SaveDataFactory(Core::System& system_, ProgramId program_id_,
|
||||||
VirtualDir save_directory_)
|
VirtualDir save_directory_)
|
||||||
: system{system_}, program_id{program_id_}, dir{std::move(save_directory_)} {}
|
: system{system_}, program_id{program_id_}, dir{std::move(save_directory_)} {
|
||||||
|
// Delete all temporary storages
|
||||||
|
// On hardware, it is expected that temporary storage be empty at first use.
|
||||||
|
dir->DeleteSubdirectoryRecursive("temp");
|
||||||
|
}
|
||||||
|
|
||||||
SaveDataFactory::~SaveDataFactory() = default;
|
SaveDataFactory::~SaveDataFactory() = default;
|
||||||
|
|
||||||
|
|||||||
@@ -40,14 +40,10 @@ VirtualDir SystemVersion() {
|
|||||||
VirtualFile file = std::make_shared<VectorVfsFile>(file_data, "file");
|
VirtualFile file = std::make_shared<VectorVfsFile>(file_data, "file");
|
||||||
|
|
||||||
// the "/digest"
|
// the "/digest"
|
||||||
if (HLE::ApiVersion::VERSION_DIGEST[0] == '\0') {
|
std::vector<u8> digest_data(sizeof(HLE::ApiVersion::VERSION_DIGEST));
|
||||||
return std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{file}, std::vector<VirtualDir>{}, "data");
|
std::memcpy(digest_data.data(), HLE::ApiVersion::VERSION_DIGEST, sizeof(HLE::ApiVersion::VERSION_DIGEST));
|
||||||
} else {
|
VirtualFile digest_file = std::make_shared<VectorVfsFile>(digest_data, "digest");
|
||||||
std::vector<u8> digest_data(sizeof(HLE::ApiVersion::VERSION_DIGEST));
|
return std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{file, digest_file}, std::vector<VirtualDir>{}, "data");
|
||||||
std::memcpy(digest_data.data(), HLE::ApiVersion::VERSION_DIGEST, sizeof(HLE::ApiVersion::VERSION_DIGEST));
|
|
||||||
VirtualFile digest_file = std::make_shared<VectorVfsFile>(digest_data, "digest");
|
|
||||||
return std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{file, digest_file}, std::vector<VirtualDir>{}, "data");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FileSys::SystemArchive
|
} // namespace FileSys::SystemArchive
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user