feat: collect shaders, machine roms and the doom wad

This commit is contained in:
Abdessamad Derraz committed 2026-08-11 09:56:14 +02:00
1 parent 4ffc11b4cf
commit d2bc1288b5
39 files changed
+6914 -3

No files matched your search

@@ -0,0 +1,64 @@
// Based on CAS_Shader.glsl
// Copyright(c) 2019 Advanced Micro Devices, Inc.All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
uniform uvec4 const0;
uniform uvec4 const1;
uniform ivec2 srcOffset;
layout(binding=0) uniform sampler2D imgSrc;
layout(binding=0, rgba8) uniform writeonly image2D imgDst;
#define A_GPU 1
#define A_GLSL 1
#include "ffx_a.h"
AF3 CasLoad(ASU2 p)
{
return texelFetch(imgSrc, srcOffset + ivec2(p), 0).rgb;
}
// Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h
// In this case, our input is already linear and between 0 and 1
void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {}
#include "ffx_cas.h"
layout(local_size_x=64) in;
void main()
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u);
// Filter.
AF4 c = vec4(0.0f);
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
gxy.x += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
gxy.y += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
gxy.x -= 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
}
@@ -0,0 +1,667 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 POSITION;
layout(location = 1) in vec2 TEXCOORD0;
layout(location = 7) in vec4 COLOR;
out vec4 PSin_p;
out vec2 PSin_t;
out vec4 PSin_c;
void vs_main()
{
PSin_p = vec4(POSITION, 0.5f, 1.0f);
PSin_t = TEXCOORD0;
PSin_c = COLOR;
gl_Position = vec4(POSITION, 0.5f, 1.0f); // NOTE I don't know if it is possible to merge POSITION_OUT and gl_Position
}
#endif
#ifdef FRAGMENT_SHADER
// Adreno's GLSL ES preprocessor errors on undefined identifiers in #if
// expressions (desktop treats them as 0). The PrimID DATE-init programs are
// compiled WITHOUT the per-ShaderConvert macro block (see GSDeviceOGL's
// ps_primid_image_init loop), so every macro in the OUTPUT chain below needs
// an explicit default.
#ifndef HAS_INTEGER_OUTPUT
#define HAS_INTEGER_OUTPUT 0
#endif
#ifndef HAS_DEPTH_OUTPUT
#define HAS_DEPTH_OUTPUT 0
#endif
#ifndef HAS_FLOAT32_OUTPUT
#define HAS_FLOAT32_OUTPUT 0
#endif
#ifndef HAS_STENCIL_OUTPUT
#define HAS_STENCIL_OUTPUT 0
#endif
#ifndef HAS_FLOAT32_INPUT
#define HAS_FLOAT32_INPUT 0
#endif
#ifndef HAS_BILN
#define HAS_BILN 0
#endif
in vec4 PSin_p;
in vec2 PSin_t;
in vec4 PSin_c;
layout(binding = 0) uniform sampler2D TextureSampler;
#if HAS_INTEGER_OUTPUT
layout(location = 0) out uint o_col0;
#define OUTPUT o_col0
#elif HAS_DEPTH_OUTPUT
// gl_FragDepth must not be redeclared on GLES ("reserved built-in name"
// compile error on Adreno/Mali) — it's available as a built-in there.
#ifndef GL_ES
out float gl_FragDepth;
#endif
#define OUTPUT gl_FragDepth
#elif HAS_FLOAT32_OUTPUT
layout(location = 0) out float o_col0;
#define OUTPUT o_col0
#elif HAS_STENCIL_OUTPUT
#else
layout(location = 0) out vec4 o_col0;
#define OUTPUT o_col0
#endif
#if HAS_FLOAT32_INPUT
float sample_c()
{
return texture(TextureSampler, PSin_t).r;
}
#else
vec4 sample_c()
{
return texture(TextureSampler, PSin_t);
}
#endif
uint rgba8_to_uint(vec4 c)
{
uvec4 i = gpu_bitwise_and(uvec4(c * 255.5f), uvec4(0xFFu));
return i.r | (i.g << 8) | (i.b << 16) | (i.a << 24);
}
uint rgb5a1_to_uint(vec4 c)
{
uvec4 i = gpu_bitwise_and(uvec4(c * 255.5f), uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u));
return (i.r >> 3) | (i.g << 2) | (i.b << 7) | (i.a << 8);
}
uint depth_to_uint(float d)
{
return uint(d * exp2(32.0f));
}
vec4 uint_to_rgba8(uint i)
{
return vec4((i & 0xFFu), ((i >> 8) & 0xFFu), ((i >> 16) & 0xFFu), ((i >> 24) & 0xFFu)) / 255.0f;
}
vec4 uint_to_rgb5a1(uint i)
{
return vec4(gpu_bitwise_and(uvec4(i << 3, i >> 2, i >> 7, i >> 8), uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u))) / 255.0f;
}
float uint_to_depth32(uint i)
{
return float(i) * exp2(-32.0f);
}
float uint_to_depth24(uint i)
{
return float(i & 0xFFFFFFu) * exp2(-32.0f);
}
float uint_to_depth16(uint i)
{
return float(i & 0xFFFFu) * exp2(-32.0f);
}
float rgba8_to_depth32(vec4 val)
{
return uint_to_depth32(rgba8_to_uint(val));
}
float rgba8_to_depth24(vec4 val)
{
return uint_to_depth24(rgba8_to_uint(val));
}
float rgba8_to_depth16(vec4 val)
{
return uint_to_depth16(rgba8_to_uint(val));
}
float rgb5a1_to_depth16(vec4 val)
{
return uint_to_depth16(rgb5a1_to_uint(val));
}
vec4 depth32_to_rgba8(float d)
{
return uint_to_rgba8(depth_to_uint(d));
}
vec4 depth16_to_rgb5a1(float d)
{
return uint_to_rgb5a1(depth_to_uint(d));
}
float depth32_to_depth24(float d)
{
return uint_to_depth24(depth_to_uint(d));
}
#ifdef ps_copy
void ps_copy()
{
OUTPUT = sample_c();
}
#endif
#ifdef ps_depth_copy
void ps_depth_copy()
{
OUTPUT = sample_c();
}
#endif
#ifdef ps_downsample_copy
uniform ivec2 ClampMin;
uniform int DownsampleFactor;
uniform float Weight;
uniform float StepMultiplier;
void ps_downsample_copy()
{
ivec2 coord = max(ivec2(gl_FragCoord.xy) * DownsampleFactor, ClampMin);
vec4 result = vec4(0);
for (int yoff = 0; yoff < DownsampleFactor; yoff++)
{
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
result += texelFetch(TextureSampler, coord + ivec2(float(xoff) * StepMultiplier, float(yoff) * StepMultiplier), 0);
}
o_col0 = result / Weight;
}
#endif
#ifdef ps_convert_rgb5a1_16bits
void ps_convert_rgb5a1_16bits()
{
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
OUTPUT = rgb5a1_to_uint(sample_c());
}
#endif
#ifdef ps_convert_depth32_32bits
void ps_convert_depth32_32bits()
{
// Convert a GL_FLOAT32 depth texture into a 32 bits UINT texture
OUTPUT = depth_to_uint(sample_c());
}
#endif
#ifdef ps_convert_depth32_rgba8
void ps_convert_depth32_rgba8()
{
// Convert a GL_FLOAT32 depth texture into a RGBA color texture
OUTPUT = depth32_to_rgba8(sample_c());
}
#endif
#ifdef ps_convert_depth16_rgb5a1
void ps_convert_depth16_rgb5a1()
{
// Convert a GL_FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
OUTPUT = depth16_to_rgb5a1(sample_c());
}
#endif
#ifdef ps_convert_depth32_depth24
void ps_convert_depth32_depth24()
{
// Truncates depth value to 24bits
OUTPUT = depth32_to_depth24(sample_c());
}
#endif
#define SAMPLE_RGBA_DEPTH_BILN(CONVERT_FN) \
ivec2 dims = textureSize(TextureSampler, 0); \
vec2 top_left_f = PSin_t * vec2(dims) - 0.5f; \
ivec2 top_left = ivec2(floor(top_left_f)); \
ivec4 coords = clamp(ivec4(top_left, top_left + 1), ivec4(0), dims.xyxy - 1); \
vec2 mix_vals = fract(top_left_f); \
float depthTL = CONVERT_FN(texelFetch(TextureSampler, coords.xy, 0)); \
float depthTR = CONVERT_FN(texelFetch(TextureSampler, coords.zy, 0)); \
float depthBL = CONVERT_FN(texelFetch(TextureSampler, coords.xw, 0)); \
float depthBR = CONVERT_FN(texelFetch(TextureSampler, coords.zw, 0)); \
OUTPUT = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
#ifdef ps_convert_rgba8_depth32
void ps_convert_rgba8_depth32()
{
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
#else
OUTPUT = rgba8_to_depth32(sample_c());
#endif
}
#endif
#ifdef ps_convert_rgba8_depth24
void ps_convert_rgba8_depth24()
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
#else
OUTPUT = rgba8_to_depth24(sample_c());
#endif
}
#endif
#ifdef ps_convert_rgba8_depth16
void ps_convert_rgba8_depth16()
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
#else
OUTPUT = rgba8_to_depth16(sample_c());
#endif
}
#endif
#ifdef ps_convert_rgb5a1_depth16
void ps_convert_rgb5a1_depth16()
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
#else
OUTPUT = rgb5a1_to_depth16(sample_c());
#endif
}
#endif
#ifdef ps_convert_rgb5a1_8i
uniform uint SBW;
uniform uint DBW;
uniform uint PSM;
uniform float ScaleFactor;
void ps_convert_rgb5a1_8i()
{
// Convert a RGB5A1 texture into a 8 bits packed texture
// Input column: 16x2 RGB5A1 pixels
// 0: 16 RGBA
// 1: 16 RGBA
// Output column: 16x4 Index pixels
// 0: 16 R5G2
// 1: 16 R5G2
// 2: 16 G2B5A1
// 3: 16 G2B5A1
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 column = gpu_bitwise_and(pos, ~uvec2(0u, 3u)) / uvec2(1u, 2u);
uvec2 subcolumn = gpu_bitwise_and(pos, uvec2(0u, 1u));
column.x -= (column.x / 128u) * 64u;
column.y += (column.y / 32u) * 32u;
// Deal with swizzling differences
if ((PSM & 0x8u) != 0u) // PSMCT16S
{
if ((pos.x & 32u) != 0u)
{
column.y += 32u; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32u;
}
if ((pos.x & 64u) != 0u)
{
column.x -= 32u;
}
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30u) != 0u) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32u;
column.y ^= 16u;
}
}
else // PSMCT16
{
if ((pos.y & 32u) != 0u)
{
column.y -= 16u;
column.x += 32u;
}
if ((pos.x & 96u) != 0u)
{
uint multi = (pos.x & 96u) / 32u;
column.y += 16u * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96u);
}
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30u) != 0u) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32u;
column.y ^= 32u;
}
}
uvec2 coord = column | subcolumn;
// Compensate for potentially differing page pitch.
uvec2 block_xy = coord / uvec2(64u, 64u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 64u);
coord = (coord % uvec2(64u, 64u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (floor(ScaleFactor) != ScaleFactor)
coord = uvec2(vec2(coord) * ScaleFactor);
else
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(TextureSampler, ivec2(coord), 0);
uvec4 denorm_c = uvec4(pixel * 255.5f);
if ((pos.y & 2u) == 0u)
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
o_col0 = vec4(float(((green << 5) | red) & 0xFFu) / 255.0f);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
o_col0 = vec4(float((alpha | (blue << 2) | (green >> 3)) & 0xFFu) / 255.0f);
}
}
#endif
#ifdef ps_convert_rgba_8i
uniform uint SBW;
uniform uint DBW;
uniform uint PSM;
uniform float ScaleFactor;
void ps_convert_rgba_8i()
{
// Convert a RGBA texture into a 8 bits packed texture
// Input column: 8x2 RGBA pixels
// 0: 8 RGBA
// 1: 8 RGBA
// Output column: 16x4 Index pixels
// 0: 8 R | 8 B
// 1: 8 R | 8 B
// 2: 8 G | 8 A
// 3: 8 G | 8 A
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 block = gpu_bitwise_and(pos, ~uvec2(15u, 3u)) >> 1;
uvec2 subblock = gpu_bitwise_and(pos, uvec2(7u, 1u));
uvec2 coord = block | subblock;
// Compensate for potentially differing page pitch.
uvec2 block_xy = coord / uvec2(64u, 32u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 32u);
coord = (coord % uvec2(64u, 32u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (floor(ScaleFactor) != ScaleFactor)
coord = uvec2(vec2(coord) * ScaleFactor);
else
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(TextureSampler, ivec2(coord), 0);
vec2 sel0 = (pos.y & 2u) == 0u ? pixel.rb : pixel.ga;
float sel1 = (pos.x & 8u) == 0u ? sel0.x : sel0.y;
o_col0 = vec4(sel1);
}
#endif
#ifdef ps_filter_transparency
void ps_filter_transparency()
{
vec4 c = sample_c();
o_col0 = vec4(c.rgb, 1.0);
}
#endif
// Used for DATE (stencil)
// DATM == 1
#ifdef ps_datm1
void ps_datm1()
{
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
discard;
}
#endif
// Used for DATE (stencil)
// DATM == 0
#ifdef ps_datm0
void ps_datm0()
{
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
discard;
}
#endif
// Used for DATE (stencil)
// DATM == 1
#ifdef ps_datm1_rta_correction
void ps_datm1_rta_correction()
{
if(sample_c().a < (254.5f / 255.0f)) // >= 0x80 pass
discard;
}
#endif
// Used for DATE (stencil)
// DATM == 0
#ifdef ps_datm0_rta_correction
void ps_datm0_rta_correction()
{
if((254.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
discard;
}
#endif
#ifdef ps_rta_correction
void ps_rta_correction()
{
vec4 value = sample_c();
o_col0 = vec4(value.rgb, value.a / (128.25f / 255.0f));
}
#endif
#ifdef ps_rta_decorrection
void ps_rta_decorrection()
{
vec4 value = sample_c();
o_col0 = vec4(value.rgb, value.a * (128.25f / 255.0f));
}
#endif
#ifdef ps_colclip_init
void ps_colclip_init()
{
vec4 value = sample_c();
o_col0 = vec4(round(value.rgb * 255.0f) / 65535.0f, value.a);
}
#endif
#ifdef ps_colclip_resolve
void ps_colclip_resolve()
{
vec4 value = sample_c();
o_col0 = vec4(vec3(gpu_bitwise_and(uvec3(value.rgb * 65535.0f), uvec3(255u))) / 255.0f, value.a);
}
#endif
#ifdef ps_convert_clut_4
uniform uvec3 offset;
uniform float scale;
void ps_convert_clut_4()
{
// CLUT4 is easy, just two rows of 8x8.
uint index = uint(gl_FragCoord.x) + offset.z;
uvec2 pos = uvec2(index % 8u, index / 8u);
ivec2 final = ivec2(floor(vec2(offset.xy + pos) * vec2(scale)));
o_col0 = texelFetch(TextureSampler, final, 0);
}
#endif
#ifdef ps_convert_clut_8
uniform uvec3 offset;
uniform float scale;
void ps_convert_clut_8()
{
uint index = min(uint(gl_FragCoord.x) + offset.z, 255u);
// CLUT is arranged into 8 groups of 16x2, with the top-right and bottom-left quadrants swapped.
// This can probably be done better..
uint subgroup = (index / 8u) % 4u;
uvec2 pos;
pos.x = (index % 8u) + ((subgroup >= 2u) ? 8u : 0u);
pos.y = ((index / 32u) * 2u) + (subgroup % 2u);
ivec2 final = ivec2(floor(vec2(offset.xy + pos) * vec2(scale)));
o_col0 = texelFetch(TextureSampler, final, 0);
}
#endif
#ifdef ps_yuv
uniform ivec2 EMOD;
void ps_yuv()
{
vec4 i = sample_c();
vec4 o = vec4(0.0f);
mat3 rgb2yuv; // Value from GS manual
rgb2yuv[0] = vec3(0.587, -0.311, -0.419);
rgb2yuv[1] = vec3(0.114, 0.500, -0.081);
rgb2yuv[2] = vec3(0.299, -0.169, 0.500);
vec3 yuv = rgb2yuv * i.gbr;
float Y = float(0xDB)/255.0f * yuv.x + float(0x10)/255.0f;
float Cr = float(0xE0)/255.0f * yuv.y + float(0x80)/255.0f;
float Cb = float(0xE0)/255.0f * yuv.z + float(0x80)/255.0f;
switch(EMOD.x)
{
case 0:
o.a = i.a;
break;
case 1:
o.a = Y;
break;
case 2:
o.a = Y/2.0f;
break;
case 3:
o.a = 0.0f;
break;
}
switch(EMOD.y)
{
case 0:
o.rgb = i.rgb;
break;
case 1:
o.rgb = vec3(Y);
break;
case 2:
o.rgb = vec3(Y, Cb, Cr);
break;
case 3:
o.rgb = vec3(i.a);
break;
}
o_col0 = o;
}
#endif
#if defined(ps_primid_image_init_0) || defined(ps_primid_image_init_1) || defined(ps_primid_image_init_2) || defined(ps_primid_image_init_3)
void main()
{
o_col0 = vec4(0x7FFFFFFF);
#ifdef ps_primid_image_init_0
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
o_col0 = vec4(-1);
#endif
#ifdef ps_primid_image_init_1
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
o_col0 = vec4(-1);
#endif
#ifdef ps_primid_image_init_2
if((254.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
o_col0 = vec4(-1);
#endif
#ifdef ps_primid_image_init_3
if(sample_c().a < (254.5f / 255.0f)) // >= 0x80 pass
o_col0 = vec4(-1);
#endif
}
#endif
#endif
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 Position;
layout(location = 1) in vec2 UV;
layout(location = 2) in vec4 Color;
uniform mat4 ProjMtx;
out vec2 Frag_UV;
out vec4 Frag_Color;
void vs_main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = ProjMtx * vec4(Position.xy, 0.0, 1.0);
}
#endif
#ifdef FRAGMENT_SHADER
layout(binding = 0) uniform sampler2D Texture;
in vec2 Frag_UV;
in vec4 Frag_Color;
layout(location = 0) out vec4 Out_Color;
void ps_main()
{
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
}
#endif
@@ -0,0 +1,205 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
#ifdef FRAGMENT_SHADER
in vec4 PSin_p;
in vec2 PSin_t;
in vec4 PSin_c;
uniform vec4 ZrH;
layout(binding = 0) uniform sampler2D TextureSampler;
layout(location = 0) out vec4 SV_Target0;
// Weave shader
void ps_main0()
{
int idx = int(ZrH.x); // buffer index passed from CPU
int field = idx & 1; // current field
int vpos = int(gl_FragCoord.y); // vertical position of destination texture
if ((vpos & 1) == field)
SV_Target0 = textureLod(TextureSampler, PSin_t, 0.0);
else
discard;
}
// Bob shader
void ps_main1()
{
SV_Target0 = textureLod(TextureSampler, PSin_t, 0.0);
}
// Blend shader
void ps_main2()
{
vec2 vstep = vec2(0.0f, ZrH.y);
vec4 c0 = textureLod(TextureSampler, PSin_t - vstep, 0.0);
vec4 c1 = textureLod(TextureSampler, PSin_t, 0.0);
vec4 c2 = textureLod(TextureSampler, PSin_t + vstep, 0.0);
SV_Target0 = (c0 + c1 * 2.0f + c2) / 4.0f;
}
// MAD shader - buffering
void ps_main3()
{
// We take half the lines from the current frame and stores them in the MAD frame buffer.
// the MAD frame buffer is split in 2 consecutive banks of 2 fields each, the fields in each bank
// are interleaved (top field at even lines and bottom field at odd lines).
// When the source texture has an odd vres, the first line of bank 1 would be an odd index
// causing the wrong lines to be discarded, so a vertical offset (lofs) is added to the vertical
// position of the destination texture to force the proper field alignment
int idx = int(ZrH.x); // buffer index passed from CPU
int bank = idx >> 1; // current bank
int field = idx & 1; // current field
int vres = int(ZrH.z) >> 1; // vertical resolution of source texture
int lofs = ((((vres + 1) >> 1) << 1) - vres) & bank; // line alignment offset for bank 1
int vpos = int(gl_FragCoord.y) + lofs; // vertical position of destination texture
// if the index of current destination line belongs to the current fiels we update it, otherwise
// we leave the old line in the destination buffer
if ((vpos & 1) == field)
SV_Target0 = textureLod(TextureSampler, PSin_t, 0.0);
else
discard;
}
// MAD shader - reconstruction
void ps_main4()
{
// we use the contents of the MAD frame buffer to reconstruct the missing lines from the current field.
int idx = int(ZrH.x); // buffer index passed from CPU
int field = idx & 1; // current field
int vpos = int(gl_FragCoord.y); // vertical position of destination texture
float sensitivity = ZrH.w; // passed from CPU, higher values mean more likely to use weave
vec3 motion_thr = vec3(1.0, 1.0, 1.0) * sensitivity; //
vec2 bofs = vec2(0.0f, 0.5f); // position of the bank 1 relative to source texture size
vec2 vscale = vec2(1.0f, 0.5f); // scaling factor from source to destination texture
vec2 lofs = vec2(0.0f, ZrH.y) * vscale; // distance between two adjacent lines relative to source texture size
vec2 iptr = PSin_t * vscale; // pointer to the current pixel in the source texture
vec2 p_t0; // pointer to current pixel (missing or not) from most recent frame
vec2 p_t1; // pointer to current pixel (missing or not) from one frame back
vec2 p_t2; // pointer to current pixel (missing or not) from two frames back
vec2 p_t3; // pointer to current pixel (missing or not) from three frames back
switch (idx)
{
case 1:
p_t0 = iptr;
p_t1 = iptr;
p_t2 = iptr + bofs;
p_t3 = iptr + bofs;
break;
case 2:
p_t0 = iptr + bofs;
p_t1 = iptr;
p_t2 = iptr;
p_t3 = iptr + bofs;
break;
case 3:
p_t0 = iptr + bofs;
p_t1 = iptr + bofs;
p_t2 = iptr;
p_t3 = iptr;
break;
default:
p_t0 = iptr;
p_t1 = iptr + bofs;
p_t2 = iptr + bofs;
p_t3 = iptr;
break;
}
// calculating motion, only relevant for missing lines where the "center line" is pointed
// by p_t1
vec4 hn = textureLod(TextureSampler, p_t0 - lofs, 0.0); // new high pixel
vec4 cn = textureLod(TextureSampler, p_t1, 0.0); // new center pixel
vec4 ln = textureLod(TextureSampler, p_t0 + lofs, 0.0); // new low pixel
vec4 ho = textureLod(TextureSampler, p_t2 - lofs, 0.0); // old high pixel
vec4 co = textureLod(TextureSampler, p_t3, 0.0); // old center pixel
vec4 lo = textureLod(TextureSampler, p_t2 + lofs, 0.0); // old low pixel
vec3 mh = hn.rgb - ho.rgb; // high pixel motion
vec3 mc = cn.rgb - co.rgb; // center pixel motion
vec3 ml = ln.rgb - lo.rgb; // low pixel motion
mh = max(mh, -mh) - motion_thr;
mc = max(mc, -mc) - motion_thr;
ml = max(ml, -ml) - motion_thr;
#if 1 // use this code to evaluate each color motion separately
float mh_max = max(max(mh.x, mh.y), mh.z);
float mc_max = max(max(mc.x, mc.y), mc.z);
float ml_max = max(max(ml.x, ml.y), ml.z);
#else // use this code to evaluate average color motion
float mh_max = mh.x + mh.y + mh.z;
float mc_max = mc.x + mc.y + mc.z;
float ml_max = ml.x + ml.y + ml.z;
#endif
// selecting deinterlacing output
if ((vpos & 1) == field)
{
// output coordinate present on current field
SV_Target0 = textureLod(TextureSampler, p_t0, 0.0);
}
else if ((iptr.y > 0.5f - lofs.y) || (iptr.y < 0.0 + lofs.y))
{
// top and bottom lines are always weaved
SV_Target0 = cn;
}
else
{
// missing line needs to be reconstructed
if(((mh_max > 0.0f) || (ml_max > 0.0f)) || (mc_max > 0.0f))
// high motion -> interpolate pixels above and below
SV_Target0 = (hn + ln) / 2.0f;
else
{
// Check if it's completely static first, we don't need to mess with any of that.
if((mh_max != -motion_thr.x) || (ml_max != -motion_thr.x) || (mc_max != -motion_thr.x))
{
// Check the diff with the above and below lines, if the difference is smaller between the new high and low lines
// compared to the new centre line and the high line (with some threshold of about 25 color steps), then reconstruct.
vec3 mhln = hn.rgb - ln.rgb;
vec3 mchn = hn.rgb - cn.rgb;
mhln = max(mhln, -mhln) - motion_thr;
mchn = max(mchn, -mchn) - motion_thr;
float mhln_max = max(max(mhln.x, mhln.y), mhln.z);
float mchn_max = max(max(mchn.x, mchn.y), mchn.z);
// The new centre line is a fair chunk different from those surrounding it, so quite likely incorrect.
if (mhln_max < 0.0f && mchn_max >= (mhln_max * 0.90f))
SV_Target0 = (hn + ln) / 2.0f;
else
// low motion -> weave
SV_Target0 = cn;
}
else
// low motion -> weave
SV_Target0 = cn;
}
}
}
#endif
@@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
#ifdef FRAGMENT_SHADER
in vec4 PSin_p;
in vec2 PSin_t;
in vec4 PSin_c;
uniform vec4 BGColor;
layout(binding = 0) uniform sampler2D TextureSampler;
layout(location = 0) out vec4 SV_Target0;
void ps_main0()
{
vec4 c = texture(TextureSampler, PSin_t);
// Note: clamping will be done by fixed unit
c.a *= 2.0f;
SV_Target0 = c;
}
void ps_main1()
{
vec4 c = texture(TextureSampler, PSin_t);
c.a = BGColor.a;
SV_Target0 = c;
}
#endif
@@ -0,0 +1,476 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 POSITION;
layout(location = 1) in vec2 TEXCOORD0;
layout(location = 7) in vec4 COLOR;
// FIXME set the interpolation (don't know what dx do)
// flat means that there is no interpolation. The value given to the fragment shader is based on the provoking vertex conventions.
//
// noperspective means that there will be linear interpolation in window-space. This is usually not what you want, but it can have its uses.
//
// smooth, the default, means to do perspective-correct interpolation.
//
// The centroid qualifier only matters when multisampling. If this qualifier is not present, then the value is interpolated to the pixel's center, anywhere in the pixel, or to one of the pixel's samples. This sample may lie outside of the actual primitive being rendered, since a primitive can cover only part of a pixel's area. The centroid qualifier is used to prevent this; the interpolation point must fall within both the pixel's area and the primitive's area.
out vec4 PSin_p;
out vec2 PSin_t;
out vec4 PSin_c;
void vs_main()
{
PSin_p = vec4(POSITION, 0.5f, 1.0f);
PSin_t = TEXCOORD0;
PSin_c = COLOR;
gl_Position = vec4(POSITION, 0.5f, 1.0f); // NOTE I don't know if it is possible to merge POSITION_OUT and gl_Position
}
#endif
#ifdef FRAGMENT_SHADER
uniform vec4 u_source_rect;
uniform vec4 u_target_rect;
uniform vec2 u_source_size;
uniform vec2 u_target_size;
uniform vec2 u_target_resolution;
uniform vec2 u_rcp_target_resolution; // 1 / u_target_resolution
uniform vec2 u_source_resolution;
uniform vec2 u_rcp_source_resolution; // 1 / u_source_resolution
uniform float u_time;
in vec4 PSin_p;
in vec2 PSin_t;
in vec4 PSin_c;
layout(binding = 0) uniform sampler2D TextureSampler;
layout(location = 0) out vec4 SV_Target0;
vec4 sample_c()
{
return texture(TextureSampler, PSin_t);
}
vec4 sample_c(vec2 uv)
{
return texture(TextureSampler, uv);
}
vec4 ps_crt(uint i)
{
vec4 mask[4] = vec4[4](
vec4(1, 0, 0, 0),
vec4(0, 1, 0, 0),
vec4(0, 0, 1, 0),
vec4(1, 1, 1, 0));
return sample_c() * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
}
#ifdef ps_copy
void ps_copy()
{
SV_Target0 = sample_c();
}
#endif
#ifdef ps_filter_scanlines
vec4 ps_scanlines(uint i)
{
vec4 mask[2] = vec4[2]
(
vec4(1, 1, 1, 0),
vec4(0, 0, 0, 0)
);
return sample_c() * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
}
void ps_filter_scanlines() // scanlines
{
highp uvec4 p = uvec4(gl_FragCoord);
vec4 c = ps_scanlines(p.y % 2u);
SV_Target0 = c;
}
#endif
#ifdef ps_filter_diagonal
void ps_filter_diagonal() // diagonal
{
highp uvec4 p = uvec4(gl_FragCoord);
vec4 c = ps_crt((p.x + (p.y % 3u)) % 3u);
SV_Target0 = c;
}
#endif
#ifdef ps_filter_triangular
void ps_filter_triangular() // triangular
{
highp uvec4 p = uvec4(gl_FragCoord);
vec4 c = ps_crt(((p.x + ((p.y >> 1u) & 1u) * 3u) >> 1u) % 3u);
SV_Target0 = c;
}
#endif
#ifdef ps_filter_complex
void ps_filter_complex()
{
const float PI = 3.14159265359f;
vec2 texdim = vec2(textureSize(TextureSampler, 0));
float factor = (0.9f - 0.4f * cos(2.0f * PI * PSin_t.y * texdim.y));
vec4 c = factor * texture(TextureSampler, vec2(PSin_t.x, (floor(PSin_t.y * texdim.y) + 0.5f) / texdim.y));
SV_Target0 = c;
}
#endif
#ifdef ps_filter_lottes
#define MaskingType 4 //[1|2|3|4] The type of CRT shadow masking used. 1: compressed TV style, 2: Aperture-grille, 3: Stretched VGA style, 4: VGA style.
#define ScanBrightness -8.00 //[-16.0 to 1.0] The overall brightness of the scanline effect. Lower for darker, higher for brighter.
#define FilterCRTAmount -3.00 //[-4.0 to 1.0] The amount of filtering used, to replicate the TV CRT look. Lower for less, higher for more.
#define HorizontalWarp 0.00 //[0.0 to 0.1] The distortion warping effect for the horizontal (x) axis of the screen. Use small increments.
#define VerticalWarp 0.00 //[0.0 to 0.1] The distortion warping effect for the verticle (y) axis of the screen. Use small increments.
#define MaskAmountDark 0.50 //[0.0 to 1.0] The value of the dark masking line effect used. Lower for darker lower end masking, higher for brighter.
#define MaskAmountLight 1.50 //[0.0 to 2.0] The value of the light masking line effect used. Lower for darker higher end masking, higher for brighter.
#define BloomPixel -1.50 //[-2.0 -0.5] Pixel bloom radius. Higher for increased softness of bloom.
#define BloomScanLine -2.0 //[-4.0 -1.0] Scanline bloom radius. Higher for increased softness of bloom.
#define BloomAmount 0.15 //[0.0 1.0] Bloom intensity. Higher for brighter.
#define Shape 2.0 //[0.0 10.0] Kernal filter shape. Lower values will darken image and introduce moire patterns if used with curvature.
#define UseShadowMask 1 //[0 or 1] Enables, or disables the use of the CRT shadow mask. 0 is disabled, 1 is enabled.
float ToLinear1(float c)
{
return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);
}
vec3 ToLinear(vec3 c)
{
return vec3(ToLinear1(c.r), ToLinear1(c.g), ToLinear1(c.b));
}
float ToSrgb1(float c)
{
return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 0.41666) - 0.055;
}
vec3 ToSrgb(vec3 c)
{
return vec3(ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b));
}
vec3 Fetch(vec2 pos, vec2 off)
{
pos = (floor(pos * u_target_size + off) + vec2(0.5, 0.5)) / u_target_size;
if (max(abs(pos.x - 0.5), abs(pos.y - 0.5)) > 0.5)
{
return vec3(0.0, 0.0, 0.0);
}
else
{
return ToLinear(texture(TextureSampler, pos.xy).rgb);
}
}
vec2 Dist(vec2 pos)
{
pos = pos * vec2(640, 480);
return -((pos - floor(pos)) - vec2(0.5, 0.5));
}
float Gaus(float pos, float scale)
{
return exp2(scale * pow(abs(pos), Shape));
}
vec3 Horz3(vec2 pos, float off)
{
vec3 b = Fetch(pos, vec2(-1.0, off));
vec3 c = Fetch(pos, vec2(0.0, off));
vec3 d = Fetch(pos, vec2(1.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = FilterCRTAmount;
float wb = Gaus(dst - 1.0, scale);
float wc = Gaus(dst + 0.0, scale);
float wd = Gaus(dst + 1.0, scale);
return (b * wb + c * wc + d * wd) / (wb + wc + wd);
}
vec3 Horz5(vec2 pos, float off)
{
vec3 a = Fetch(pos, vec2(-2.0, off));
vec3 b = Fetch(pos, vec2(-1.0, off));
vec3 c = Fetch(pos, vec2(0.0, off));
vec3 d = Fetch(pos, vec2(1.0, off));
vec3 e = Fetch(pos, vec2(2.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = FilterCRTAmount;
float wa = Gaus(dst - 2.0, scale);
float wb = Gaus(dst - 1.0, scale);
float wc = Gaus(dst + 0.0, scale);
float wd = Gaus(dst + 1.0, scale);
float we = Gaus(dst + 2.0, scale);
return (a * wa + b * wb + c * wc + d * wd + e * we) / (wa + wb + wc + wd + we);
}
vec3 Horz7(vec2 pos, float off)
{
vec3 a = Fetch(pos, vec2(-3.0, off));
vec3 b = Fetch(pos, vec2(-2.0, off));
vec3 c = Fetch(pos, vec2(-1.0, off));
vec3 d = Fetch(pos, vec2( 0.0, off));
vec3 e = Fetch(pos, vec2( 1.0, off));
vec3 f = Fetch(pos, vec2( 2.0, off));
vec3 g = Fetch(pos, vec2( 3.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = BloomPixel;
float wa = Gaus(dst - 3.0, scale);
float wb = Gaus(dst - 2.0, scale);
float wc = Gaus(dst - 1.0, scale);
float wd = Gaus(dst + 0.0, scale);
float we = Gaus(dst + 1.0, scale);
float wf = Gaus(dst + 2.0, scale);
float wg = Gaus(dst + 3.0, scale);
// Return filtered sample.
return (a * wa + b * wb + c * wc + d * wd + e * we + f * wf + g * wg) / (wa + wb + wc + wd + we + wf + wg);
}
// Return scanline weight.
float Scan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, ScanBrightness);
}
float BloomScan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, BloomScanLine);
}
vec3 Tri(vec2 pos)
{
vec3 a = Horz3(pos, -1.0);
vec3 b = Horz5(pos, 0.0);
vec3 c = Horz3(pos, 1.0);
float wa = Scan(pos, -1.0);
float wb = Scan(pos, 0.0);
float wc = Scan(pos, 1.0);
return (a * wa) + (b * wb) + (c * wc);
}
vec3 Bloom(vec2 pos)
{
vec3 a = Horz5(pos,-2.0);
vec3 b = Horz7(pos,-1.0);
vec3 c = Horz7(pos, 0.0);
vec3 d = Horz7(pos, 1.0);
vec3 e = Horz5(pos, 2.0);
float wa = BloomScan(pos,-2.0);
float wb = BloomScan(pos,-1.0);
float wc = BloomScan(pos, 0.0);
float wd = BloomScan(pos, 1.0);
float we = BloomScan(pos, 2.0);
return a * wa + b * wb + c * wc + d * wd + e * we;
}
vec2 Warp(vec2 pos)
{
pos = pos * 2.0 - 1.0;
pos *= vec2(1.0 + (pos.y * pos.y) * HorizontalWarp, 1.0 + (pos.x * pos.x) * VerticalWarp);
return pos * 0.5 + 0.5;
}
vec3 Mask(vec2 pos)
{
#if MaskingType == 1
// Very compressed TV style shadow mask.
float lines = MaskAmountLight;
float odd = 0.0;
if (fract(pos.x / 6.0) < 0.5)
{
odd = 1.0;
}
if (fract((pos.y + odd) / 2.0) < 0.5)
{
lines = MaskAmountDark;
}
pos.x = fract(pos.x / 3.0);
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
mask *= lines;
return mask;
#elif MaskingType == 2
// Aperture-grille.
pos.x = fract(pos.x / 3.0);
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#elif MaskingType == 3
// Stretched VGA style shadow mask (same as prior shaders).
pos.x += pos.y * 3.0;
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
pos.x = fract(pos.x / 6.0);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#else
// VGA style shadow mask.
pos.xy = floor(pos.xy * vec2(1.0, 0.5));
pos.x += pos.y * 3.0;
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
pos.x = fract(pos.x / 6.0);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#endif
}
vec4 LottesCRTPass()
{
//flipped y axis in opengl
vec2 fragcoord = vec2(gl_FragCoord.x, u_target_resolution.y - gl_FragCoord.y) - u_target_rect.xy;
vec4 color;
vec2 inSize = u_target_resolution - (2.0 * u_target_rect.xy);
vec2 pos = Warp(fragcoord.xy / inSize);
color.rgb = Tri(pos);
color.rgb += Bloom(pos) * BloomAmount;
#if UseShadowMask
color.rgb *= Mask(fragcoord.xy);
#endif
color.rgb = ToSrgb(color.rgb);
return color;
}
void ps_filter_lottes()
{
SV_Target0 = LottesCRTPass();
}
#endif
#ifdef ps_4x_rgss
void ps_4x_rgss()
{
vec2 dxy = vec2(dFdx(PSin_t.x), dFdy(PSin_t.y));
vec3 color = vec3(0);
float s = 1.0/8.0;
float l = 3.0/8.0;
color += sample_c(PSin_t + vec2( s, l) * dxy).rgb;
color += sample_c(PSin_t + vec2( l,-s) * dxy).rgb;
color += sample_c(PSin_t + vec2(-s,-l) * dxy).rgb;
color += sample_c(PSin_t + vec2(-l, s) * dxy).rgb;
SV_Target0 = vec4(color * 0.25,1);
}
#endif
#ifdef ps_automagical_supersampling
void ps_automagical_supersampling()
{
vec2 ratio = (u_source_size / u_target_size) * 0.5;
vec2 steps = floor(ratio);
vec3 col = sample_c(PSin_t).rgb;
float div = 1.0;
for (float y = 0.0; y < steps.y; y++)
{
for (float x = 0.0; x < steps.x; x++)
{
vec2 offset = vec2(x,y) - ratio * 0.5;
col += sample_c(PSin_t + offset * u_rcp_source_resolution * 2.0).rgb;
div++;
}
}
SV_Target0 = vec4(col / div, 1.0);
}
#endif
#endif
@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
/*
** Contrast, saturation, brightness
** Code of this function is from TGM's shader pack
** http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=21057
** TGM's author comment about the license (included in the previous link)
** "do with it, what you want! its total free!
** (but would be nice, if you say that you used my shaders :wink: ) but not necessary"
*/
#ifdef FRAGMENT_SHADER
uniform vec4 params;
in vec4 PSin_p;
in vec2 PSin_t;
in vec4 PSin_c;
layout(binding = 0) uniform sampler2D TextureSampler;
layout(location = 0) out vec4 SV_Target0;
// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150%
vec4 ContrastSaturationBrightness(vec4 color)
{
float brt = params.x;
float con = params.y;
float sat = params.z;
float gam = params.w;
// Increase or decrease these values to adjust r, g and b color channels separately
const float AvgLumR = 0.5;
const float AvgLumG = 0.5;
const float AvgLumB = 0.5;
const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
vec3 brtColor = color.rgb * brt;
float dot_intensity = dot(brtColor, LumCoeff);
vec3 intensity = vec3(dot_intensity, dot_intensity, dot_intensity);
vec3 satColor = mix(intensity, brtColor, sat);
vec3 conColor = mix(AvgLumin, satColor, con);
vec3 csb = conColor;
csb = pow(csb, vec3(1.0 / gam));
color.rgb = csb;
return color;
}
void ps_main()
{
vec4 c = texture(TextureSampler, PSin_t);
SV_Target0 = ContrastSaturationBrightness(c);
}
#endif
@@ -0,0 +1,71 @@
#version 460 core
#extension GL_EXT_samplerless_texture_functions : require
// Based on CAS_Shader.glsl
// Copyright(c) 2019 Advanced Micro Devices, Inc.All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
layout(push_constant) uniform const_buffer
{
uvec4 const0;
uvec4 const1;
ivec2 srcOffset;
};
layout(set=0, binding=0) uniform texture2D imgSrc;
layout(set=0, binding=1, rgba8) uniform writeonly image2D imgDst;
layout(constant_id=0) const int sharpenOnly = 0;
#define A_GPU 1
#define A_GLSL 1
#include "ffx_a.h"
AF3 CasLoad(ASU2 p)
{
return texelFetch(imgSrc, srcOffset + ivec2(p), 0).rgb;
}
// Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h
// In this case, our input is already linear and between 0 and 1
void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {}
#include "ffx_cas.h"
layout(local_size_x=64) in;
void main()
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u);
// Filter.
AF4 c = vec4(0.0f);
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
gxy.x += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
gxy.y += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
gxy.x -= 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
}
@@ -0,0 +1,652 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
#ifdef FRAGMENT_SHADER
layout(location = 0) in vec2 v_tex;
#if HAS_INTEGER_OUTPUT
layout(location = 0) out uint o_col0;
#define OUTPUT o_col0
#elif HAS_DEPTH_OUTPUT
out float gl_FragDepth;
#define OUTPUT gl_FragDepth
#elif HAS_FLOAT32_OUTPUT
layout(location = 0) out float o_col0;
#define OUTPUT o_col0
#elif HAS_STENCIL_OUTPUT
#else
layout(location = 0) out vec4 o_col0;
#define OUTPUT o_col0
#endif
layout(set = 0, binding = 0) uniform sampler2D samp0;
#if HAS_FLOAT32_INPUT
float sample_c(vec2 uv)
{
return texture(samp0, uv).r;
}
#else
vec4 sample_c(vec2 uv)
{
return texture(samp0, uv);
}
#endif
uint rgba8_to_uint(vec4 c)
{
uvec4 i = gpu_bitwise_and(uvec4(c * 255.5f), uvec4(0xFFu));
return i.r | (i.g << 8) | (i.b << 16) | (i.a << 24);
}
uint rgb5a1_to_uint(vec4 c)
{
uvec4 i = gpu_bitwise_and(uvec4(c * 255.5f), uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u));
return (i.r >> 3) | (i.g << 2) | (i.b << 7) | (i.a << 8);
}
uint depth_to_uint(float d)
{
return uint(d * exp2(32.0f));
}
vec4 uint_to_rgba8(uint i)
{
return vec4((i & 0xFFu), ((i >> 8) & 0xFFu), ((i >> 16) & 0xFFu), ((i >> 24) & 0xFFu)) / 255.0f;
}
vec4 uint_to_rgb5a1(uint i)
{
return vec4(gpu_bitwise_and(uvec4(i << 3, i >> 2, i >> 7, i >> 8), uvec4(0xF8u, 0xF8u, 0xF8u, 0x80u))) / 255.0f;
}
float uint_to_depth32(uint i)
{
return float(i) * exp2(-32.0f);
}
float uint_to_depth24(uint i)
{
return float(i & 0xFFFFFFu) * exp2(-32.0f);
}
float uint_to_depth16(uint i)
{
return float(i & 0xFFFFu) * exp2(-32.0f);
}
float rgba8_to_depth32(vec4 val)
{
return uint_to_depth32(rgba8_to_uint(val));
}
float rgba8_to_depth24(vec4 val)
{
return uint_to_depth24(rgba8_to_uint(val));
}
float rgba8_to_depth16(vec4 val)
{
return uint_to_depth16(rgba8_to_uint(val));
}
float rgb5a1_to_depth16(vec4 val)
{
return uint_to_depth16(rgb5a1_to_uint(val));
}
vec4 depth32_to_rgba8(float d)
{
return uint_to_rgba8(depth_to_uint(d));
}
vec4 depth16_to_rgb5a1(float d)
{
return uint_to_rgb5a1(depth_to_uint(d));
}
float depth32_to_depth24(float d)
{
return uint_to_depth24(depth_to_uint(d));
}
#ifdef ps_copy
void ps_copy()
{
OUTPUT = sample_c(v_tex);
}
#endif
#ifdef ps_depth_copy
void ps_depth_copy()
{
OUTPUT = sample_c(v_tex);
}
#endif
#ifdef ps_downsample_copy
layout(push_constant) uniform cb10
{
ivec2 ClampMin;
int DownsampleFactor;
int pad0;
float Weight;
float step_multiplier;
vec2 pad1;
};
void ps_downsample_copy()
{
ivec2 coord = max(ivec2(gl_FragCoord.xy) * DownsampleFactor, ClampMin);
vec4 result = vec4(0);
for (int yoff = 0; yoff < DownsampleFactor; yoff++)
{
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
{
result += texelFetch(samp0, coord + ivec2(xoff * step_multiplier, yoff * step_multiplier), 0);
}
}
OUTPUT = result / Weight;
}
#endif
#ifdef ps_filter_transparency
void ps_filter_transparency()
{
vec4 c = sample_c(v_tex);
OUTPUT = vec4(c.rgb, 1.0);
}
#endif
#ifdef ps_convert_rgb5a1_16bits
void ps_convert_rgb5a1_16bits()
{
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
OUTPUT = rgb5a1_to_uint(sample_c(v_tex));
}
#endif
#ifdef ps_datm1
void ps_datm1()
{
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
discard;
}
#endif
#ifdef ps_datm0
void ps_datm0()
{
if((127.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
discard;
}
#endif
#ifdef ps_datm1_rta_correction
void ps_datm1_rta_correction()
{
if(sample_c(v_tex).a < (254.5f / 255.0f)) // >= 0x80 pass
discard;
}
#endif
#ifdef ps_datm0_rta_correction
void ps_datm0_rta_correction()
{
if((254.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
discard;
}
#endif
#ifdef ps_rta_correction
void ps_rta_correction()
{
vec4 value = sample_c(v_tex);
OUTPUT = vec4(value.rgb, value.a / (128.25f / 255.0f));
}
#endif
#ifdef ps_rta_decorrection
void ps_rta_decorrection()
{
vec4 value = sample_c(v_tex);
OUTPUT = vec4(value.rgb, value.a * (128.25f / 255.0f));
}
#endif
#ifdef ps_colclip_init
void ps_colclip_init()
{
vec4 value = sample_c(v_tex);
OUTPUT = vec4(roundEven(value.rgb * 255.0f) / 65535.0f, value.a);
}
#endif
#ifdef ps_colclip_resolve
void ps_colclip_resolve()
{
vec4 value = sample_c(v_tex);
OUTPUT = vec4(vec3(gpu_bitwise_and(uvec3(value.rgb * 65535.5f), uvec3(255u))) / 255.0f, value.a);
}
#endif
#ifdef ps_convert_depth32_32bits
void ps_convert_depth32_32bits()
{
// Convert a vec32 depth texture into a 32 bits UINT texture
OUTPUT = depth_to_uint(sample_c(v_tex));
}
#endif
#ifdef ps_convert_depth32_rgba8
void ps_convert_depth32_rgba8()
{
// Convert a vec32 depth texture into a RGBA color texture
OUTPUT = depth32_to_rgba8(sample_c(v_tex));
}
#endif
#ifdef ps_convert_depth16_rgb5a1
void ps_convert_depth16_rgb5a1()
{
// Convert a vec32 (only 16 lsb) depth into a RGB5A1 color texture
OUTPUT = depth16_to_rgb5a1(sample_c(v_tex));
}
#endif
#ifdef ps_convert_depth32_depth24
void ps_convert_depth32_depth24()
{
// Truncates depth value to 24bits
OUTPUT = depth32_to_depth24(sample_c(v_tex));
}
#endif
#define SAMPLE_RGBA_DEPTH_BILN(CONVERT_FN) \
ivec2 dims = textureSize(samp0, 0); \
vec2 top_left_f = v_tex * vec2(dims) - 0.5f; \
ivec2 top_left = ivec2(floor(top_left_f)); \
ivec4 coords = clamp(ivec4(top_left, top_left + 1), ivec4(0), dims.xyxy - 1); \
vec2 mix_vals = fract(top_left_f); \
float depthTL = CONVERT_FN(texelFetch(samp0, coords.xy, 0)); \
float depthTR = CONVERT_FN(texelFetch(samp0, coords.zy, 0)); \
float depthBL = CONVERT_FN(texelFetch(samp0, coords.xw, 0)); \
float depthBR = CONVERT_FN(texelFetch(samp0, coords.zw, 0)); \
OUTPUT = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
#ifdef ps_convert_rgba8_depth32
void ps_convert_rgba8_depth32()
{
// Convert an RGBA texture into a float depth texture
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
#else
OUTPUT = rgba8_to_depth32(sample_c(v_tex));
#endif
}
#endif
#ifdef ps_convert_rgba8_depth24
void ps_convert_rgba8_depth24()
{
// Same as above but without the alpha channel (24 bits Z)
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
#else
OUTPUT = rgba8_to_depth24(sample_c(v_tex));
#endif
}
#endif
#ifdef ps_convert_rgba8_depth16
void ps_convert_rgba8_depth16()
{
// Same as above but without the A/B channels (16 bits Z)
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
#else
OUTPUT = rgba8_to_depth16(sample_c(v_tex));
#endif
}
#endif
#ifdef ps_convert_rgb5a1_depth16
void ps_convert_rgb5a1_depth16()
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
#if HAS_BILN
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
#else
OUTPUT = rgb5a1_to_depth16(sample_c(v_tex));
#endif
}
#endif
#ifdef ps_convert_rgb5a1_8i
layout(push_constant) uniform cb10
{
uint SBW;
uint DBW;
uint PSM;
float cb_pad1;
float ScaleFactor;
vec3 cb_pad2;
};
void ps_convert_rgb5a1_8i()
{
// Convert a RGB5A1 texture into a 8 bits packed texture
// Input column: 16x2 RGB5A1 pixels
// 0: 16 RGBA
// 1: 16 RGBA
// Output column: 16x4 Index pixels
// 0: 16 R5G2
// 1: 16 R5G2
// 2: 16 G2B5A1
// 3: 16 G2B5A1
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 column = gpu_bitwise_and(pos, ~uvec2(0u, 3u)) / uvec2(1u, 2u);
uvec2 subcolumn = gpu_bitwise_and(pos, uvec2(0u, 1u));
column.x -= (column.x / 128u) * 64u;
column.y += (column.y / 32u) * 32u;
// Deal with swizzling differences
if ((PSM & 0x8u) != 0u) // PSMCT16S
{
if ((pos.x & 32u) != 0u)
{
column.y += 32u; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32u;
}
if ((pos.x & 64u) != 0u)
{
column.x -= 32u;
}
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30u) != 0u) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32u;
column.y ^= 16u;
}
}
else // PSMCT16
{
if ((pos.y & 32u) != 0u)
{
column.y -= 16u;
column.x += 32u;
}
if ((pos.x & 96u) != 0u)
{
uint multi = (pos.x & 96u) / 32u;
column.y += 16u * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96u);
}
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30u) != 0u) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32u;
column.y ^= 32u;
}
}
uvec2 coord = column | subcolumn;
// Compensate for potentially differing page pitch.
uvec2 block_xy = coord / uvec2(64u, 64u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 64u);
coord = (coord % uvec2(64u, 64u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (floor(ScaleFactor) != ScaleFactor)
coord = uvec2(vec2(coord) * ScaleFactor);
else
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(samp0, ivec2(coord), 0);
uvec4 denorm_c = uvec4(pixel * 255.5f);
if ((pos.y & 2u) == 0u)
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
o_col0 = vec4(float(((green << 5) | red) & 0xFFu) / 255.0f);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
o_col0 = vec4(float((alpha | (blue << 2) | (green >> 3)) & 0xFFu) / 255.0f);
}
}
#endif
#ifdef ps_convert_rgba_8i
layout(push_constant) uniform cb10
{
uint SBW;
uint DBW;
uint PSM;
float cb_pad1;
float ScaleFactor;
vec3 cb_pad2;
};
void ps_convert_rgba_8i()
{
// Convert a RGBA texture into a 8 bits packed texture
// Input column: 8x2 RGBA pixels
// 0: 8 RGBA
// 1: 8 RGBA
// Output column: 16x4 Index pixels
// 0: 8 R | 8 B
// 1: 8 R | 8 B
// 2: 8 G | 8 A
// 3: 8 G | 8 A
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 block = gpu_bitwise_and(pos, ~uvec2(15u, 3u)) >> 1;
uvec2 subblock = gpu_bitwise_and(pos, uvec2(7u, 1u));
uvec2 coord = block | subblock;
// Compensate for potentially differing page pitch.
uvec2 block_xy = coord / uvec2(64u, 32u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 32u);
coord = (coord % uvec2(64u, 32u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (floor(ScaleFactor) != ScaleFactor)
coord = uvec2(vec2(coord) * ScaleFactor);
else
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(samp0, ivec2(coord), 0);
vec2 sel0 = (pos.y & 2u) == 0u ? pixel.rb : pixel.ga;
float sel1 = (pos.x & 8u) == 0u ? sel0.x : sel0.y;
o_col0 = vec4(sel1); // Divide by something here?
}
#endif
#ifdef ps_convert_clut_4
layout(push_constant) uniform cb10
{
uvec2 offset;
uint doffset;
uint cb_pad1;
float scale;
vec3 cb_pad2;
};
void ps_convert_clut_4()
{
// CLUT4 is easy, just two rows of 8x8.
uint index = uint(gl_FragCoord.x) + doffset;
uvec2 pos = uvec2(index % 8u, index / 8u);
ivec2 final = ivec2(floor(vec2(offset + pos) * vec2(scale)));
o_col0 = texelFetch(samp0, final, 0);
}
#endif
#ifdef ps_convert_clut_8
layout(push_constant) uniform cb10
{
uvec2 offset;
uint doffset;
uint cb_pad1;
float scale;
vec3 cb_pad2;
};
void ps_convert_clut_8()
{
uint index = min(uint(gl_FragCoord.x) + doffset, 255u);
// CLUT is arranged into 8 groups of 16x2, with the top-right and bottom-left quadrants swapped.
// This can probably be done better..
uint subgroup = (index / 8u) % 4u;
uvec2 pos;
pos.x = (index % 8u) + ((subgroup >= 2u) ? 8u : 0u);
pos.y = ((index / 32u) * 2u) + (subgroup % 2u);
ivec2 final = ivec2(floor(vec2(offset + pos) * vec2(scale)));
o_col0 = texelFetch(samp0, final, 0);
}
#endif
#ifdef ps_yuv
layout(push_constant) uniform cb10
{
int EMODA;
int EMODC;
};
void ps_yuv()
{
vec4 i = sample_c(v_tex);
vec4 o = vec4(0.0f);
mat3 rgb2yuv;
rgb2yuv[0] = vec3(0.587, -0.311, -0.419);
rgb2yuv[1] = vec3(0.114, 0.500, -0.081);
rgb2yuv[2] = vec3(0.299, -0.169, 0.500);
vec3 yuv = rgb2yuv * i.gbr;
float Y = float(0xDB)/255.0f * yuv.x + float(0x10)/255.0f;
float Cr = float(0xE0)/255.0f * yuv.y + float(0x80)/255.0f;
float Cb = float(0xE0)/255.0f * yuv.z + float(0x80)/255.0f;
switch(EMODA)
{
case 0:
o.a = i.a;
break;
case 1:
o.a = Y;
break;
case 2:
o.a = Y/2.0f;
break;
case 3:
o.a = 0.0f;
break;
}
switch(EMODC)
{
case 0:
o.rgb = i.rgb;
break;
case 1:
o.rgb = vec3(Y);
break;
case 2:
o.rgb = vec3(Y, Cb, Cr);
break;
case 3:
o.rgb = vec3(i.a);
break;
}
o_col0 = o;
}
#endif
#if defined(ps_primid_image_init_0) || defined(ps_primid_image_init_1) || defined(ps_primid_image_init_2) || defined(ps_primid_image_init_3)
void main()
{
o_col0 = vec4(0x7FFFFFFF);
#ifdef ps_primid_image_init_0
if((127.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
o_col0 = vec4(-1);
#endif
#ifdef ps_primid_image_init_1
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
o_col0 = vec4(-1);
#endif
#ifdef ps_primid_image_init_2
if((254.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
o_col0 = vec4(-1);
#endif
#ifdef ps_primid_image_init_3
if(sample_c(v_tex).a < (254.5f / 255.0f)) // >= 0x80 pass
o_col0 = vec4(-1);
#endif
}
#endif
#endif
@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 Position;
layout(location = 1) in vec2 UV;
layout(location = 2) in vec4 Color;
layout(push_constant) uniform PushConstants
{
vec2 uScale;
vec2 uTranslate;
};
layout(location = 0) out vec2 Frag_UV;
layout(location = 1) out vec4 Frag_Color;
void vs_main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = vec4(Position * uScale + uTranslate, 0.0f, 1.0f);
}
#endif
#ifdef FRAGMENT_SHADER
layout(binding = 0) uniform sampler2D Texture;
layout(location = 0) in vec2 Frag_UV;
layout(location = 1) in vec4 Frag_Color;
layout(location = 0) out vec4 Out_Color;
void ps_main()
{
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
}
#endif
@@ -0,0 +1,227 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
#ifdef FRAGMENT_SHADER
layout(location = 0) in vec2 v_tex;
layout(location = 0) out vec4 o_col0;
layout(push_constant) uniform cb0
{
vec4 ZrH;
};
layout(set = 0, binding = 0) uniform sampler2D samp0;
// Weave shader
#ifdef ps_main0
void ps_main0()
{
const int idx = int(ZrH.x); // buffer index passed from CPU
const int field = idx & 1; // current field
const int vpos = int(gl_FragCoord.y); // vertical position of destination texture
if ((vpos & 1) == field)
o_col0 = textureLod(samp0, v_tex, 0);
else
discard;
}
#endif
// Bob shader
#ifdef ps_main1
void ps_main1()
{
o_col0 = textureLod(samp0, v_tex, 0);
}
#endif
// Blend shader
#ifdef ps_main2
void ps_main2()
{
vec2 vstep = vec2(0.0f, ZrH.y);
vec4 c0 = textureLod(samp0, v_tex - vstep, 0);
vec4 c1 = textureLod(samp0, v_tex, 0);
vec4 c2 = textureLod(samp0, v_tex + vstep, 0);
o_col0 = (c0 + c1 * 2.0f + c2) / 4.0f;
}
#endif
// MAD shader - buffering
#ifdef ps_main3
void ps_main3()
{
// We take half the lines from the current frame and stores them in the MAD frame buffer.
// the MAD frame buffer is split in 2 consecutive banks of 2 fields each, the fields in each bank
// are interleaved (top field at even lines and bottom field at odd lines).
// When the source texture has an odd vres, the first line of bank 1 would be an odd index
// causing the wrong lines to be discarded, so a vertical offset (lofs) is added to the vertical
// position of the destination texture to force the proper field alignment
const int idx = int(ZrH.x); // buffer index passed from CPU
const int bank = idx >> 1; // current bank
const int field = idx & 1; // current field
const int vres = int(ZrH.z) >> 1; // vertical resolution of source texture
const int lofs = ((((vres + 1) >> 1) << 1) - vres) & bank; // line alignment offset for bank 1
const int vpos = int(gl_FragCoord.y) + lofs; // vertical position of destination texture
// if the index of current destination line belongs to the current fiels we update it, otherwise
// we leave the old line in the destination buffer
if ((vpos & 1) == field)
o_col0 = textureLod(samp0, v_tex, 0);
else
discard;
}
#endif
// MAD shader - reconstruction
#ifdef ps_main4
void ps_main4()
{
// we use the contents of the MAD frame buffer to reconstruct the missing lines from the current
// field.
const int idx = int(ZrH.x); // buffer index passed from CPU
const int bank = idx >> 1; // current bank
const int field = idx & 1; // current field
const int vpos = int(gl_FragCoord.y); // vertical position of destination texture
const float sensitivity = ZrH.w; // passed from CPU, higher values mean more likely to use weave
const vec3 motion_thr = vec3(1.0, 1.0, 1.0) * sensitivity; //
const vec2 bofs = vec2(0.0f, 0.5f); // position of the bank 1 relative to source texture size
const vec2 vscale = vec2(1.0f, 0.5f); // scaling factor from source to destination texture
const vec2 lofs = vec2(0.0f, ZrH.y) * vscale; // distance between two adjacent lines relative to source texture size
const vec2 iptr = v_tex * vscale; // pointer to the current pixel in the source texture
vec2 p_t0; // pointer to current pixel (missing or not) from most recent frame
vec2 p_t1; // pointer to current pixel (missing or not) from one frame back
vec2 p_t2; // pointer to current pixel (missing or not) from two frames back
vec2 p_t3; // pointer to current pixel (missing or not) from three frames back
switch (idx)
{
case 1:
p_t0 = iptr;
p_t1 = iptr;
p_t2 = iptr + bofs;
p_t3 = iptr + bofs;
break;
case 2:
p_t0 = iptr + bofs;
p_t1 = iptr;
p_t2 = iptr;
p_t3 = iptr + bofs;
break;
case 3:
p_t0 = iptr + bofs;
p_t1 = iptr + bofs;
p_t2 = iptr;
p_t3 = iptr;
break;
default:
p_t0 = iptr;
p_t1 = iptr + bofs;
p_t2 = iptr + bofs;
p_t3 = iptr;
break;
}
// calculating motion, only relevant for missing lines where the "center line" is pointed by p_t1
vec4 hn = textureLod(samp0, p_t0 - lofs, 0); // new high pixel
vec4 cn = textureLod(samp0, p_t1, 0); // new center pixel
vec4 ln = textureLod(samp0, p_t0 + lofs, 0); // new low pixel
vec4 ho = textureLod(samp0, p_t2 - lofs, 0); // old high pixel
vec4 co = textureLod(samp0, p_t3, 0); // old center pixel
vec4 lo = textureLod(samp0, p_t2 + lofs, 0); // old low pixel
vec3 mh = hn.rgb - ho.rgb; // high pixel motion
vec3 mc = cn.rgb - co.rgb; // center pixel motion
vec3 ml = ln.rgb - lo.rgb; // low pixel motion
mh = max(mh, -mh) - motion_thr;
mc = max(mc, -mc) - motion_thr;
ml = max(ml, -ml) - motion_thr;
#if 1 // use this code to evaluate each color motion separately
float mh_max = max(max(mh.x, mh.y), mh.z);
float mc_max = max(max(mc.x, mc.y), mc.z);
float ml_max = max(max(ml.x, ml.y), ml.z);
#else // use this code to evaluate average color motion
float mh_max = mh.x + mh.y + mh.z;
float mc_max = mc.x + mc.y + mc.z;
float ml_max = ml.x + ml.y + ml.z;
#endif
// selecting deinterlacing output
if ((vpos & 1) == field) // output coordinate present on current field
{
// output coordinate present on current field
o_col0 = textureLod(samp0, p_t0, 0);
}
else if ((iptr.y > 0.5f - lofs.y) || (iptr.y < 0.0 + lofs.y))
{
// top and bottom lines are always weaved
o_col0 = cn;
}
else
{
// missing line needs to be reconstructed
if(((mh_max > 0.0f) || (ml_max > 0.0f)) || (mc_max > 0.0f))
// high motion -> interpolate pixels above and below
o_col0 = (hn + ln) / 2.0f;
else
{
// Check if it's completely static first, we don't need to mess with any of that.
if((mh_max != -motion_thr.x) || (ml_max != -motion_thr.x) || (mc_max != -motion_thr.x))
{
// Check the diff with the above and below lines, if the difference is smaller between the new high and low lines
// compared to the new centre line and the high line (with some threshold of about 25 color steps), then reconstruct.
vec3 mhln = hn.rgb - ln.rgb;
vec3 mchn = hn.rgb - cn.rgb;
mhln = max(mhln, -mhln) - motion_thr;
mchn = max(mchn, -mchn) - motion_thr;
float mhln_max = max(max(mhln.x, mhln.y), mhln.z);
float mchn_max = max(max(mchn.x, mchn.y), mchn.z);
// The new centre line is a fair chunk different from those surrounding it, so quite likely incorrect.
if (mhln_max < 0.0f && mchn_max >= (mhln_max * 0.90f))
o_col0 = (hn + ln) / 2.0f;
else
// low motion -> weave
o_col0 = cn;
}
else
// low motion -> weave
o_col0 = cn;
}
}
}
#endif
#endif
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
#ifdef FRAGMENT_SHADER
layout(location = 0) in vec2 v_tex;
layout(location = 0) out vec4 o_col0;
layout(push_constant) uniform cb10
{
vec4 BGColor;
};
layout(set = 0, binding = 0) uniform sampler2D samp0;
void ps_main0()
{
vec4 c = texture(samp0, v_tex);
// Note: clamping will be done by fixed unit
c.a *= 2.0f;
o_col0 = c;
}
void ps_main1()
{
vec4 c = texture(samp0, v_tex);
c.a = BGColor.a;
o_col0 = c;
}
#endif
@@ -0,0 +1,446 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
#ifdef FRAGMENT_SHADER
layout(push_constant) uniform cb10
{
vec4 u_source_rect;
vec4 u_target_rect;
vec2 u_source_size;
vec2 u_target_size;
vec2 u_target_resolution;
vec2 u_rcp_target_resolution; // 1 / u_target_resolution
vec2 u_source_resolution;
vec2 u_rcp_source_resolution; // 1 / u_source_resolution
float u_time;
};
layout(location = 0) in vec2 v_tex;
layout(location = 0) out vec4 o_col0;
layout(set = 0, binding = 0) uniform sampler2D samp0;
vec4 sample_c(vec2 uv)
{
return texture(samp0, uv);
}
vec4 ps_crt(uint i)
{
vec4 mask[4] = vec4[4](
vec4(1, 0, 0, 0),
vec4(0, 1, 0, 0),
vec4(0, 0, 1, 0),
vec4(1, 1, 1, 0));
return sample_c(v_tex) * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
}
vec4 ps_scanlines(uint i)
{
vec4 mask[2] =
{
vec4(1, 1, 1, 0),
vec4(0, 0, 0, 0)};
return sample_c(v_tex) * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
}
#ifdef ps_copy
void ps_copy()
{
o_col0 = sample_c(v_tex);
}
#endif
#ifdef ps_filter_scanlines
void ps_filter_scanlines() // scanlines
{
uvec4 p = uvec4(gl_FragCoord);
o_col0 = ps_scanlines(p.y % 2);
}
#endif
#ifdef ps_filter_diagonal
void ps_filter_diagonal() // diagonal
{
uvec4 p = uvec4(gl_FragCoord);
o_col0 = ps_crt((p.x + (p.y % 3)) % 3);
}
#endif
#ifdef ps_filter_triangular
void ps_filter_triangular() // triangular
{
uvec4 p = uvec4(gl_FragCoord);
// output.c = ps_crt(input, ((p.x + (p.y & 1) * 3) >> 1) % 3);
o_col0 = ps_crt(((p.x + ((p.y >> 1) & 1) * 3) >> 1) % 3);
}
#endif
#ifdef ps_filter_complex
void ps_filter_complex() // triangular
{
const float PI = 3.14159265359f;
vec2 texdim = vec2(textureSize(samp0, 0));
o_col0 = (0.9 - 0.4 * cos(2 * PI * v_tex.y * texdim.y)) * sample_c(vec2(v_tex.x, (floor(v_tex.y * texdim.y) + 0.5) / texdim.y));
}
#endif
#ifdef ps_filter_lottes
#define MaskingType 4 //[1|2|3|4] The type of CRT shadow masking used. 1: compressed TV style, 2: Aperture-grille, 3: Stretched VGA style, 4: VGA style.
#define ScanBrightness -8.00 //[-16.0 to 1.0] The overall brightness of the scanline effect. Lower for darker, higher for brighter.
#define FilterCRTAmount -3.00 //[-4.0 to 1.0] The amount of filtering used, to replicate the TV CRT look. Lower for less, higher for more.
#define HorizontalWarp 0.00 //[0.0 to 0.1] The distortion warping effect for the horizontal (x) axis of the screen. Use small increments.
#define VerticalWarp 0.00 //[0.0 to 0.1] The distortion warping effect for the verticle (y) axis of the screen. Use small increments.
#define MaskAmountDark 0.50 //[0.0 to 1.0] The value of the dark masking line effect used. Lower for darker lower end masking, higher for brighter.
#define MaskAmountLight 1.50 //[0.0 to 2.0] The value of the light masking line effect used. Lower for darker higher end masking, higher for brighter.
#define BloomPixel -1.50 //[-2.0 -0.5] Pixel bloom radius. Higher for increased softness of bloom.
#define BloomScanLine -2.0 //[-4.0 -1.0] Scanline bloom radius. Higher for increased softness of bloom.
#define BloomAmount 0.15 //[0.0 1.0] Bloom intensity. Higher for brighter.
#define Shape 2.0 //[0.0 10.0] Kernal filter shape. Lower values will darken image and introduce moire patterns if used with curvature.
#define UseShadowMask 1 //[0 or 1] Enables, or disables the use of the CRT shadow mask. 0 is disabled, 1 is enabled.
float ToLinear1(float c)
{
return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);
}
vec3 ToLinear(vec3 c)
{
return vec3(ToLinear1(c.r), ToLinear1(c.g), ToLinear1(c.b));
}
float ToSrgb1(float c)
{
return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 0.41666) - 0.055;
}
vec3 ToSrgb(vec3 c)
{
return vec3(ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b));
}
vec3 Fetch(vec2 pos, vec2 off)
{
pos = (floor(pos * u_target_size + off) + vec2(0.5, 0.5)) / u_target_size;
if (max(abs(pos.x - 0.5), abs(pos.y - 0.5)) > 0.5)
{
return vec3(0.0, 0.0, 0.0);
}
else
{
return ToLinear(texture(samp0, pos.xy).rgb);
}
}
vec2 Dist(vec2 pos)
{
pos = pos * vec2(640, 480);
return -((pos - floor(pos)) - vec2(0.5, 0.5));
}
float Gaus(float pos, float scale)
{
return exp2(scale * pow(abs(pos), Shape));
}
vec3 Horz3(vec2 pos, float off)
{
vec3 b = Fetch(pos, vec2(-1.0, off));
vec3 c = Fetch(pos, vec2(0.0, off));
vec3 d = Fetch(pos, vec2(1.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = FilterCRTAmount;
float wb = Gaus(dst - 1.0, scale);
float wc = Gaus(dst + 0.0, scale);
float wd = Gaus(dst + 1.0, scale);
return (b * wb + c * wc + d * wd) / (wb + wc + wd);
}
vec3 Horz5(vec2 pos, float off)
{
vec3 a = Fetch(pos, vec2(-2.0, off));
vec3 b = Fetch(pos, vec2(-1.0, off));
vec3 c = Fetch(pos, vec2(0.0, off));
vec3 d = Fetch(pos, vec2(1.0, off));
vec3 e = Fetch(pos, vec2(2.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = FilterCRTAmount;
float wa = Gaus(dst - 2.0, scale);
float wb = Gaus(dst - 1.0, scale);
float wc = Gaus(dst + 0.0, scale);
float wd = Gaus(dst + 1.0, scale);
float we = Gaus(dst + 2.0, scale);
return (a * wa + b * wb + c * wc + d * wd + e * we) / (wa + wb + wc + wd + we);
}
vec3 Horz7(vec2 pos, float off)
{
vec3 a = Fetch(pos, vec2(-3.0, off));
vec3 b = Fetch(pos, vec2(-2.0, off));
vec3 c = Fetch(pos, vec2(-1.0, off));
vec3 d = Fetch(pos, vec2( 0.0, off));
vec3 e = Fetch(pos, vec2( 1.0, off));
vec3 f = Fetch(pos, vec2( 2.0, off));
vec3 g = Fetch(pos, vec2( 3.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = BloomPixel;
float wa = Gaus(dst - 3.0, scale);
float wb = Gaus(dst - 2.0, scale);
float wc = Gaus(dst - 1.0, scale);
float wd = Gaus(dst + 0.0, scale);
float we = Gaus(dst + 1.0, scale);
float wf = Gaus(dst + 2.0, scale);
float wg = Gaus(dst + 3.0, scale);
// Return filtered sample.
return (a * wa + b * wb + c * wc + d * wd + e * we + f * wf + g * wg) / (wa + wb + wc + wd + we + wf + wg);
}
// Return scanline weight.
float Scan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, ScanBrightness);
}
float BloomScan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, BloomScanLine);
}
vec3 Tri(vec2 pos)
{
vec3 a = Horz3(pos, -1.0);
vec3 b = Horz5(pos, 0.0);
vec3 c = Horz3(pos, 1.0);
float wa = Scan(pos, -1.0);
float wb = Scan(pos, 0.0);
float wc = Scan(pos, 1.0);
return (a * wa) + (b * wb) + (c * wc);
}
vec3 Bloom(vec2 pos)
{
vec3 a = Horz5(pos,-2.0);
vec3 b = Horz7(pos,-1.0);
vec3 c = Horz7(pos, 0.0);
vec3 d = Horz7(pos, 1.0);
vec3 e = Horz5(pos, 2.0);
float wa = BloomScan(pos,-2.0);
float wb = BloomScan(pos,-1.0);
float wc = BloomScan(pos, 0.0);
float wd = BloomScan(pos, 1.0);
float we = BloomScan(pos, 2.0);
return a * wa + b * wb + c * wc + d * wd + e * we;
}
vec2 Warp(vec2 pos)
{
pos = pos * 2.0 - 1.0;
pos *= vec2(1.0 + (pos.y * pos.y) * HorizontalWarp, 1.0 + (pos.x * pos.x) * VerticalWarp);
return pos * 0.5 + 0.5;
}
vec3 Mask(vec2 pos)
{
#if MaskingType == 1
// Very compressed TV style shadow mask.
float lines = MaskAmountLight;
float odd = 0.0;
if (fract(pos.x / 6.0) < 0.5)
{
odd = 1.0;
}
if (fract((pos.y + odd) / 2.0) < 0.5)
{
lines = MaskAmountDark;
}
pos.x = fract(pos.x / 3.0);
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
mask *= lines;
return mask;
#elif MaskingType == 2
// Aperture-grille.
pos.x = fract(pos.x / 3.0);
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#elif MaskingType == 3
// Stretched VGA style shadow mask (same as prior shaders).
pos.x += pos.y * 3.0;
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
pos.x = fract(pos.x / 6.0);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#else
// VGA style shadow mask.
pos.xy = floor(pos.xy * vec2(1.0, 0.5));
pos.x += pos.y * 3.0;
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
pos.x = fract(pos.x / 6.0);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#endif
}
vec4 LottesCRTPass()
{
vec4 color;
vec4 fragcoord = gl_FragCoord - u_target_rect;
vec2 inSize = u_target_resolution - (2 * u_target_rect.xy);
vec2 pos = Warp(fragcoord.xy / inSize);
color.rgb = Tri(pos);
color.rgb += Bloom(pos) * BloomAmount;
#if UseShadowMask
color.rgb *= Mask(fragcoord.xy);
#endif
color.rgb = ToSrgb(color.rgb);
return color;
}
void ps_filter_lottes()
{
o_col0 = LottesCRTPass();
}
#endif
#ifdef ps_4x_rgss
void ps_4x_rgss()
{
vec2 dxy = vec2(dFdx(v_tex.x), dFdy(v_tex.y));
vec3 color = vec3(0);
float s = 1.0/8.0;
float l = 3.0/8.0;
color += sample_c(v_tex + vec2( s, l) * dxy).rgb;
color += sample_c(v_tex + vec2( l,-s) * dxy).rgb;
color += sample_c(v_tex + vec2(-s,-l) * dxy).rgb;
color += sample_c(v_tex + vec2(-l, s) * dxy).rgb;
o_col0 = vec4(color * 0.25, 1.0);
}
#endif
#ifdef ps_automagical_supersampling
void ps_automagical_supersampling()
{
vec2 ratio = (u_source_size / u_target_size) * 0.5;
vec2 steps = floor(ratio);
vec3 col = sample_c(v_tex).rgb;
float div = 1.0;
for (float y = 0.0; y < steps.y; y++)
{
for (float x = 0.0; x < steps.x; x++)
{
vec2 offset = vec2(x,y) - ratio * 0.5;
col += sample_c(v_tex + offset * u_rcp_source_resolution * 2.0).rgb;
div++;
}
}
o_col0 = vec4(col / div, 1.0);
}
#endif
#endif
@@ -0,0 +1,77 @@
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
/*
** Contrast, saturation, brightness
** Code of this function is from TGM's shader pack
** http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=21057
** TGM's author comment about the license (included in the previous link)
** "do with it, what you want! its total free!
** (but would be nice, if you say that you used my shaders :wink: ) but not necessary"
*/
#ifdef FRAGMENT_SHADER
layout(push_constant) uniform cb0
{
vec4 params;
};
layout(set = 0, binding = 0) uniform sampler2D samp0;
layout(location = 0) in vec2 v_tex;
layout(location = 0) out vec4 o_col0;
// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150%
vec4 ContrastSaturationBrightness(vec4 color)
{
float brt = params.x;
float con = params.y;
float sat = params.z;
float gam = params.w;
// Increase or decrease these values to adjust r, g and b color channels separately
const float AvgLumR = 0.5;
const float AvgLumG = 0.5;
const float AvgLumB = 0.5;
const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
vec3 brtColor = color.rgb * brt;
float dot_intensity = dot(brtColor, LumCoeff);
vec3 intensity = vec3(dot_intensity, dot_intensity, dot_intensity);
vec3 satColor = mix(intensity, brtColor, sat);
vec3 conColor = mix(AvgLumin, satColor, con);
vec3 csb = conColor;
csb = pow(csb, vec3(1.0 / gam));
color.rgb = csb;
return color;
}
void main()
{
vec4 c = texture(samp0, v_tex);
o_col0 = ContrastSaturationBrightness(c);
}
#endif
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,65 @@
// Based on CAS_Shader.glsl
// Copyright(c) 2019 Advanced Micro Devices, Inc.All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
uniform uvec4 const0;
uniform uvec4 const1;
uniform ivec2 srcOffset;
// Sampler binding defaults to 0 if not explicitly set; texture unit 0 is used in code.
uniform sampler2D imgSrc;
layout(binding=0, rgba8) uniform writeonly image2D imgDst;
#define A_GPU 1
#define A_GLSL 1
#include "ffx_a.h"
AF3 CasLoad(ASU2 p)
{
return texelFetch(imgSrc, srcOffset + ivec2(p), 0).rgb;
}
// Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h
// In this case, our input is already linear and between 0 and 1
void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {}
#include "ffx_cas.h"
layout(local_size_x=64) in;
void main()
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u);
// Filter.
AF4 c = vec4(0.0f);
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
gxy.x += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
gxy.y += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
gxy.x -= 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, CAS_SHARPEN_ONLY);
imageStore(imgDst, ASU2(gxy), c);
}
@@ -0,0 +1,655 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 POSITION;
layout(location = 1) in vec2 TEXCOORD0;
layout(location = 7) in vec4 COLOR;
// FIXME set the interpolation (don't know what dx do)
// flat means that there is no interpolation. The value given to the fragment shader is based on the provoking vertex conventions.
//
// noperspective means that there will be linear interpolation in window-space. This is usually not what you want, but it can have its uses.
//
// smooth, the default, means to do perspective-correct interpolation.
//
// The centroid qualifier only matters when multisampling. If this qualifier is not present, then the value is interpolated to the pixel's center, anywhere in the pixel, or to one of the pixel's samples. This sample may lie outside of the actual primitive being rendered, since a primitive can cover only part of a pixel's area. The centroid qualifier is used to prevent this; the interpolation point must fall within both the pixel's area and the primitive's area.
out vec4 PSin_p;
out vec2 PSin_t;
out vec4 PSin_c;
void vs_main()
{
PSin_p = vec4(POSITION, 0.5f, 1.0f);
PSin_t = TEXCOORD0;
PSin_c = COLOR;
gl_Position = vec4(POSITION, 0.5f, 1.0f); // NOTE I don't know if it is possible to merge POSITION_OUT and gl_Position
}
#endif
#ifdef FRAGMENT_SHADER
in vec4 PSin_p;
in vec2 PSin_t;
in vec4 PSin_c;
layout(binding = 0) uniform sampler2D TextureSampler;
// Give a different name so I remember there is a special case!
#if defined(ps_convert_rgba8_16bits) || defined(ps_convert_float32_32bits)
layout(location = 0) out uint SV_Target1;
#elif defined(ps_convert_float32_depth_to_color)
layout(location = 0) out float SV_Target0;
#else
layout(location = 0) out vec4 SV_Target0;
#endif
vec4 sample_c()
{
return texture(TextureSampler, PSin_t);
}
#ifdef ps_copy
void ps_copy()
{
SV_Target0 = sample_c();
}
#endif
#ifdef ps_depth_copy
void ps_depth_copy()
{
gl_FragDepth = sample_c().r;
}
#endif
#ifdef ps_downsample_copy
uniform ivec2 ClampMin;
uniform int DownsampleFactor;
uniform float Weight;
uniform float StepMultiplier;
void ps_downsample_copy()
{
int step = int(StepMultiplier);
ivec2 coord = max(ivec2(gl_FragCoord.xy) * DownsampleFactor, ClampMin);
vec4 result = vec4(0);
for (int yoff = 0; yoff < DownsampleFactor; yoff++)
{
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
result += texelFetch(TextureSampler, coord + ivec2(xoff * step, yoff * step), 0);
}
SV_Target0 = result / Weight;
}
#endif
#ifdef ps_convert_rgba8_16bits
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
void ps_convert_rgba8_16bits()
{
highp uvec4 i = uvec4(sample_c() * vec4(255.5f, 255.5f, 255.5f, 255.5f));
SV_Target1 = ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
}
#endif
#ifdef ps_convert_float32_32bits
void ps_convert_float32_32bits()
{
// Convert a GL_FLOAT32 depth texture into a 32 bits UINT texture
#if HAS_CLIP_CONTROL
SV_Target1 = uint(exp2(32.0f) * sample_c().r);
#else
SV_Target1 = uint(exp2(24.0f) * sample_c().r);
#endif
}
#endif
#ifdef ps_convert_float32_rgba8
void ps_convert_float32_rgba8()
{
// Convert a GL_FLOAT32 depth texture into a RGBA color texture
#if HAS_CLIP_CONTROL
uint d = uint(sample_c().r * exp2(32.0f));
#else
uint d = uint(sample_c().r * exp2(24.0f));
#endif
SV_Target0 = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / vec4(255.0);
}
#endif
#ifdef ps_convert_float16_rgb5a1
void ps_convert_float16_rgb5a1()
{
// Convert a GL_FLOAT32 (only 16 lsb) depth into a RGB5A1 color texture
#if HAS_CLIP_CONTROL
uint d = uint(sample_c().r * exp2(32.0f));
#else
uint d = uint(sample_c().r * exp2(24.0f));
#endif
SV_Target0 = vec4(gpu_bitwise_and(uvec4(d << 3, d >> 2, d >> 7, d >> 8), uvec4(0xf8, 0xf8, 0xf8, 0x80))) / 255.0f;
}
#endif
float rgba8_to_depth32(vec4 unorm)
{
uvec4 c = uvec4(unorm * vec4(255.5f));
#if HAS_CLIP_CONTROL
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
#else
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-24.0f);
#endif
}
float rgba8_to_depth24(vec4 unorm)
{
uvec3 c = uvec3(unorm.rgb * vec3(255.5f));
#if HAS_CLIP_CONTROL
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
#else
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-24.0f);
#endif
}
float rgba8_to_depth16(vec4 unorm)
{
uvec2 c = uvec2(unorm.rg * vec2(255.5f));
#if HAS_CLIP_CONTROL
return float(c.r | (c.g << 8)) * exp2(-32.0f);
#else
return float(c.r | (c.g << 8)) * exp2(-24.0f);
#endif
}
float rgb5a1_to_depth16(vec4 unorm)
{
uvec4 c = uvec4(unorm * vec4(255.5f));
#if HAS_CLIP_CONTROL
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
#else
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-24.0f);
#endif
}
#ifdef ps_convert_float32_depth_to_color
void ps_convert_float32_depth_to_color()
{
SV_Target0 = sample_c().r;
}
#endif
#ifdef ps_convert_float32_color_to_depth
void ps_convert_float32_color_to_depth()
{
gl_FragDepth = sample_c().r;
}
#endif
#ifdef ps_convert_float32_float24
void ps_convert_float32_float24()
{
// Truncates depth value to 24bits
#if HAS_CLIP_CONTROL
uint d = uint(sample_c().r * exp2(32.0f)) & 0xFFFFFFu;
gl_FragDepth = float(d) * exp2(-32.0f);
#else
uint d = uint(sample_c().r * exp2(24.0f)) & 0xFFFFFFu;
gl_FragDepth = float(d) * exp2(-24.0f);
#endif
}
#endif
#ifdef ps_convert_rgba8_float32
void ps_convert_rgba8_float32()
{
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth32(sample_c());
}
#endif
#ifdef ps_convert_rgba8_float24
void ps_convert_rgba8_float24()
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth24(sample_c());
}
#endif
#ifdef ps_convert_rgba8_float16
void ps_convert_rgba8_float16()
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth16(sample_c());
}
#endif
#ifdef ps_convert_rgb5a1_float16
void ps_convert_rgb5a1_float16()
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
gl_FragDepth = rgb5a1_to_depth16(sample_c());
}
#endif
#define SAMPLE_RGBA_DEPTH_BILN(CONVERT_FN) \
ivec2 dims = textureSize(TextureSampler, 0); \
vec2 top_left_f = PSin_t * vec2(dims) - 0.5f; \
ivec2 top_left = ivec2(floor(top_left_f)); \
ivec4 coords = clamp(ivec4(top_left, top_left + 1), ivec4(0), dims.xyxy - 1); \
vec2 mix_vals = fract(top_left_f); \
float depthTL = CONVERT_FN(texelFetch(TextureSampler, coords.xy, 0)); \
float depthTR = CONVERT_FN(texelFetch(TextureSampler, coords.zy, 0)); \
float depthBL = CONVERT_FN(texelFetch(TextureSampler, coords.xw, 0)); \
float depthBR = CONVERT_FN(texelFetch(TextureSampler, coords.zw, 0)); \
gl_FragDepth = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
#ifdef ps_convert_rgba8_float32_biln
void ps_convert_rgba8_float32_biln()
{
// Convert an RGBA texture into a float depth texture
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
}
#endif
#ifdef ps_convert_rgba8_float24_biln
void ps_convert_rgba8_float24_biln()
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
}
#endif
#ifdef ps_convert_rgba8_float16_biln
void ps_convert_rgba8_float16_biln()
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
}
#endif
#ifdef ps_convert_rgb5a1_float16_biln
void ps_convert_rgb5a1_float16_biln()
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
}
#endif
#ifdef ps_convert_rgb5a1_8i
uniform uint SBW;
uniform uint DBW;
uniform uint PSM;
uniform float ScaleFactor;
void ps_convert_rgb5a1_8i()
{
// Convert a RGB5A1 texture into a 8 bits packed texture
// Input column: 16x2 RGB5A1 pixels
// 0: 16 RGBA
// 1: 16 RGBA
// Output column: 16x4 Index pixels
// 0: 16 R5G2
// 1: 16 R5G2
// 2: 16 G2B5A1
// 3: 16 G2B5A1
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 column = gpu_bitwise_and(pos, ~uvec2(0u, 3u)) / uvec2(1,2);
uvec2 subcolumn = gpu_bitwise_and(pos, uvec2(0u, 1u));
column.x -= (column.x / 128u) * 64u;
column.y += (column.y / 32u) * 32u;
// Deal with swizzling differences
if ((PSM & 0x8u) != 0u) // PSMCT16S
{
if ((pos.x & 32u) != 0u)
{
column.y += 32u; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32u;
}
if ((pos.x & 64u) != 0u)
{
column.x -= 32u;
}
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30u) != 0u) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32u;
column.y ^= 16u;
}
}
else // PSMCT16
{
if ((pos.y & 32u) != 0u)
{
column.y -= 16u;
column.x += 32u;
}
if ((pos.x & 96u) != 0u)
{
uint multi = (pos.x & 96u) / 32u;
column.y += 16u * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96u);
}
if (((pos.x & 16u) != 0u) != ((pos.y & 16u) != 0u))
{
column.x ^= 16u;
column.y ^= 8u;
}
if ((PSM & 0x30u) != 0u) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32u;
column.y ^= 32u;
}
}
uvec2 coord = column | subcolumn;
// Compensate for potentially differing page pitch.
uvec2 block_xy = coord / uvec2(64u, 64u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 64u);
coord = (coord % uvec2(64u, 64u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (floor(ScaleFactor) != ScaleFactor)
coord = uvec2(vec2(coord) * ScaleFactor);
else
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(TextureSampler, ivec2(coord), 0);
uvec4 denorm_c = uvec4(pixel * 255.5f);
if ((pos.y & 2u) == 0u)
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
float sel0 = float(((green << 5) | red) & 0xFFu) / 255.0f;
SV_Target0 = vec4(sel0);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
float sel0 = float((alpha | (blue << 2) | (green >> 3)) & 0xFFu) / 255.0f;
SV_Target0 = vec4(sel0);
}
}
#endif
#ifdef ps_convert_rgba_8i
uniform uint SBW;
uniform uint DBW;
uniform uint PSM;
uniform float ScaleFactor;
void ps_convert_rgba_8i()
{
// Convert a RGBA texture into a 8 bits packed texture
// Input column: 8x2 RGBA pixels
// 0: 8 RGBA
// 1: 8 RGBA
// Output column: 16x4 Index pixels
// 0: 8 R | 8 B
// 1: 8 R | 8 B
// 2: 8 G | 8 A
// 3: 8 G | 8 A
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 block = gpu_bitwise_and(pos, ~uvec2(15u, 3u)) >> 1;
uvec2 subblock = gpu_bitwise_and(pos, uvec2(7u, 1u));
uvec2 coord = block | subblock;
// Compensate for potentially differing page pitch.
uvec2 block_xy = coord / uvec2(64u, 32u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 32u);
coord = (coord % uvec2(64u, 32u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (floor(ScaleFactor) != ScaleFactor)
coord = uvec2(vec2(coord) * ScaleFactor);
else
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(TextureSampler, ivec2(coord), 0);
vec2 sel0 = (pos.y & 2u) == 0u ? pixel.rb : pixel.ga;
float sel1 = (pos.x & 8u) == 0u ? sel0.x : sel0.y;
SV_Target0 = vec4(sel1);
}
#endif
#ifdef ps_filter_transparency
void ps_filter_transparency()
{
vec4 c = sample_c();
SV_Target0 = vec4(c.rgb, 1.0);
}
#endif
// Used for DATE (stencil)
// DATM == 1
#ifdef ps_datm1
void ps_datm1()
{
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
discard;
}
#endif
// Used for DATE (stencil)
// DATM == 0
#ifdef ps_datm0
void ps_datm0()
{
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
discard;
}
#endif
// Used for DATE (stencil)
// DATM == 1
#ifdef ps_datm1_rta_correction
void ps_datm1_rta_correction()
{
if(sample_c().a < (254.5f / 255.0f)) // >= 0x80 pass
discard;
}
#endif
// Used for DATE (stencil)
// DATM == 0
#ifdef ps_datm0_rta_correction
void ps_datm0_rta_correction()
{
if((254.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
discard;
}
#endif
#ifdef ps_rta_correction
void ps_rta_correction()
{
vec4 value = sample_c();
SV_Target0 = vec4(value.rgb, value.a / (128.25f / 255.0f));
}
#endif
#ifdef ps_rta_decorrection
void ps_rta_decorrection()
{
vec4 value = sample_c();
SV_Target0 = vec4(value.rgb, value.a * (128.25f / 255.0f));
}
#endif
#ifdef ps_colclip_init
void ps_colclip_init()
{
vec4 value = sample_c();
SV_Target0 = vec4(round(value.rgb * 255.0f) / 65535.0f, value.a);
}
#endif
#ifdef ps_colclip_resolve
void ps_colclip_resolve()
{
vec4 value = sample_c();
SV_Target0 = vec4(vec3(gpu_bitwise_and(uvec3(value.rgb * 65535.0f), uvec3(255u))) / 255.0f, value.a);
}
#endif
#ifdef ps_convert_clut_4
uniform uvec3 offset;
uniform float scale;
void ps_convert_clut_4()
{
// CLUT4 is easy, just two rows of 8x8.
uint index = uint(gl_FragCoord.x) + offset.z;
uvec2 pos = uvec2(index % 8u, index / 8u);
ivec2 final = ivec2(floor(vec2(offset.xy + pos) * vec2(scale)));
SV_Target0 = texelFetch(TextureSampler, final, 0);
}
#endif
#ifdef ps_convert_clut_8
uniform uvec3 offset;
uniform float scale;
void ps_convert_clut_8()
{
uint index = min(uint(gl_FragCoord.x) + offset.z, 255u);
// CLUT is arranged into 8 groups of 16x2, with the top-right and bottom-left quadrants swapped.
// This can probably be done better..
uint subgroup = (index / 8u) % 4u;
uvec2 pos;
pos.x = (index % 8u) + ((subgroup >= 2u) ? 8u : 0u);
pos.y = ((index / 32u) * 2u) + (subgroup % 2u);
ivec2 final = ivec2(floor(vec2(offset.xy + pos) * vec2(scale)));
SV_Target0 = texelFetch(TextureSampler, final, 0);
}
#endif
#ifdef ps_yuv
uniform ivec2 EMOD;
void ps_yuv()
{
vec4 i = sample_c();
vec4 o = vec4(0.0f);
mat3 rgb2yuv; // Value from GS manual
rgb2yuv[0] = vec3(0.587, -0.311, -0.419);
rgb2yuv[1] = vec3(0.114, 0.500, -0.081);
rgb2yuv[2] = vec3(0.299, -0.169, 0.500);
vec3 yuv = rgb2yuv * i.gbr;
float Y = float(0xDB)/255.0f * yuv.x + float(0x10)/255.0f;
float Cr = float(0xE0)/255.0f * yuv.y + float(0x80)/255.0f;
float Cb = float(0xE0)/255.0f * yuv.z + float(0x80)/255.0f;
switch(EMOD.x)
{
case 0:
o.a = i.a;
break;
case 1:
o.a = Y;
break;
case 2:
o.a = Y/2.0f;
break;
case 3:
o.a = 0.0f;
break;
}
switch(EMOD.y)
{
case 0:
o.rgb = i.rgb;
break;
case 1:
o.rgb = vec3(Y);
break;
case 2:
o.rgb = vec3(Y, Cb, Cr);
break;
case 3:
o.rgb = vec3(i.a);
break;
}
SV_Target0 = o;
}
#endif
#if defined(ps_stencil_image_init_0) || defined(ps_stencil_image_init_1) || defined(ps_stencil_image_init_2) || defined(ps_stencil_image_init_3)
void main()
{
SV_Target0 = vec4(0x7FFFFFFF);
#ifdef ps_stencil_image_init_0
if((127.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
SV_Target0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_1
if(sample_c().a < (127.5f / 255.0f)) // >= 0x80 pass
SV_Target0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_2
if((254.5f / 255.0f) < sample_c().a) // < 0x80 pass (== 0x80 should not pass)
SV_Target0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_3
if(sample_c().a < (254.5f / 255.0f)) // >= 0x80 pass
SV_Target0 = vec4(-1);
#endif
}
#endif
#endif
@@ -0,0 +1,38 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 Position;
layout(location = 1) in vec2 UV;
layout(location = 2) in vec4 Color;
uniform mat4 ProjMtx;
out vec2 Frag_UV;
out vec4 Frag_Color;
void vs_main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = ProjMtx * vec4(Position.xy, 0.0, 1.0);
}
#endif
#ifdef FRAGMENT_SHADER
layout(binding = 0) uniform sampler2D Texture;
in vec2 Frag_UV;
in vec4 Frag_Color;
layout(location = 0) out vec4 Out_Color;
void ps_main()
{
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
}
#endif
@@ -0,0 +1,181 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
#ifdef FRAGMENT_SHADER
in vec4 PSin_p;
in vec2 PSin_t;
in vec4 PSin_c;
uniform vec4 ZrH;
layout(binding = 0) uniform sampler2D TextureSampler;
layout(location = 0) out vec4 SV_Target0;
// Weave shader
void ps_main0()
{
int idx = int(ZrH.x); // buffer index passed from CPU
int field = idx & 1; // current field
int vpos = int(gl_FragCoord.y); // vertical position of destination texture
if ((vpos & 1) == field)
SV_Target0 = textureLod(TextureSampler, PSin_t, 0.0f);
else
discard;
}
// Bob shader
void ps_main1()
{
SV_Target0 = textureLod(TextureSampler, PSin_t, 0.0f);
}
// Blend shader
void ps_main2()
{
vec2 vstep = vec2(0.0f, ZrH.y);
vec4 c0 = textureLod(TextureSampler, PSin_t - vstep, 0.0f);
vec4 c1 = textureLod(TextureSampler, PSin_t, 0.0f);
vec4 c2 = textureLod(TextureSampler, PSin_t + vstep, 0.0f);
SV_Target0 = (c0 + c1 * 2.0f + c2) / 4.0f;
}
// MAD shader - buffering
void ps_main3()
{
// We take half the lines from the current frame and stores them in the MAD frame buffer.
// the MAD frame buffer is split in 2 consecutive banks of 2 fields each, the fields in each bank
// are interleaved (top field at even lines and bottom field at odd lines).
// When the source texture has an odd vres, the first line of bank 1 would be an odd index
// causing the wrong lines to be discarded, so a vertical offset (lofs) is added to the vertical
// position of the destination texture to force the proper field alignment
int idx = int(ZrH.x); // buffer index passed from CPU
int bank = idx >> 1; // current bank
int field = idx & 1; // current field
int vres = int(ZrH.z) >> 1; // vertical resolution of source texture
int lofs = ((((vres + 1) >> 1) << 1) - vres) & bank; // line alignment offset for bank 1
int vpos = int(gl_FragCoord.y) + lofs; // vertical position of destination texture
// if the index of current destination line belongs to the current fiels we update it, otherwise
// we leave the old line in the destination buffer
if ((vpos & 1) == field)
SV_Target0 = textureLod(TextureSampler, PSin_t, 0.0f);
else
discard;
}
// MAD shader - reconstruction
void ps_main4()
{
// we use the contents of the MAD frame buffer to reconstruct the missing lines from the current field.
int idx = int(ZrH.x); // buffer index passed from CPU
int field = idx & 1; // current field
int vpos = int(gl_FragCoord.y); // vertical position of destination texture
float sensitivity = ZrH.w; // passed from CPU, higher values mean more likely to use weave
vec3 motion_thr = vec3(1.0, 1.0, 1.0) * sensitivity; //
vec2 bofs = vec2(0.0f, 0.5f); // position of the bank 1 relative to source texture size
vec2 vscale = vec2(1.0f, 0.5f); // scaling factor from source to destination texture
vec2 lofs = vec2(0.0f, ZrH.y) * vscale; // distance between two adjacent lines relative to source texture size
vec2 iptr = PSin_t * vscale; // pointer to the current pixel in the source texture
vec2 p_t0; // pointer to current pixel (missing or not) from most recent frame
vec2 p_t1; // pointer to current pixel (missing or not) from one frame back
vec2 p_t2; // pointer to current pixel (missing or not) from two frames back
vec2 p_t3; // pointer to current pixel (missing or not) from three frames back
switch (idx)
{
case 1:
p_t0 = iptr;
p_t1 = iptr;
p_t2 = iptr + bofs;
p_t3 = iptr + bofs;
break;
case 2:
p_t0 = iptr + bofs;
p_t1 = iptr;
p_t2 = iptr;
p_t3 = iptr + bofs;
break;
case 3:
p_t0 = iptr + bofs;
p_t1 = iptr + bofs;
p_t2 = iptr;
p_t3 = iptr;
break;
default:
p_t0 = iptr;
p_t1 = iptr + bofs;
p_t2 = iptr + bofs;
p_t3 = iptr;
break;
}
// calculating motion, only relevant for missing lines where the "center line" is pointed
// by p_t1
vec4 hn = textureLod(TextureSampler, p_t0 - lofs, 0.0f); // new high pixel
vec4 cn = textureLod(TextureSampler, p_t1, 0.0f); // new center pixel
vec4 ln = textureLod(TextureSampler, p_t0 + lofs, 0.0f); // new low pixel
vec4 ho = textureLod(TextureSampler, p_t2 - lofs, 0.0f); // old high pixel
vec4 co = textureLod(TextureSampler, p_t3, 0.0f); // old center pixel
vec4 lo = textureLod(TextureSampler, p_t2 + lofs, 0.0f); // old low pixel
vec3 mh = hn.rgb - ho.rgb; // high pixel motion
vec3 mc = cn.rgb - co.rgb; // center pixel motion
vec3 ml = ln.rgb - lo.rgb; // low pixel motion
mh = max(mh, -mh) - motion_thr;
mc = max(mc, -mc) - motion_thr;
ml = max(ml, -ml) - motion_thr;
#if 1 // use this code to evaluate each color motion separately
float mh_max = max(max(mh.x, mh.y), mh.z);
float mc_max = max(max(mc.x, mc.y), mc.z);
float ml_max = max(max(ml.x, ml.y), ml.z);
#else // use this code to evaluate average color motion
float mh_max = mh.x + mh.y + mh.z;
float mc_max = mc.x + mc.y + mc.z;
float ml_max = ml.x + ml.y + ml.z;
#endif
// selecting deinterlacing output
if ((vpos & 1) == field)
{
// output coordinate present on current field
SV_Target0 = textureLod(TextureSampler, p_t0, 0.0f);
}
else if ((iptr.y > 0.5f - lofs.y) || (iptr.y < 0.0 + lofs.y))
{
// top and bottom lines are always weaved
SV_Target0 = cn;
}
else
{
// missing line needs to be reconstructed
if(((mh_max > 0.0f) || (ml_max > 0.0f)) || (mc_max > 0.0f))
// high motion -> interpolate pixels above and below
SV_Target0 = (hn + ln) / 2.0f;
else
// low motion -> weave
SV_Target0 = cn;
}
}
#endif
@@ -0,0 +1,33 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
#ifdef FRAGMENT_SHADER
in vec4 PSin_p;
in vec2 PSin_t;
in vec4 PSin_c;
uniform vec4 BGColor;
layout(binding = 0) uniform sampler2D TextureSampler;
layout(location = 0) out vec4 SV_Target0;
void ps_main0()
{
vec4 c = texture(TextureSampler, PSin_t);
// Note: clamping will be done by fixed unit
c.a *= 2.0f;
SV_Target0 = c;
}
void ps_main1()
{
vec4 c = texture(TextureSampler, PSin_t);
c.a = BGColor.a;
SV_Target0 = c;
}
#endif
@@ -0,0 +1,553 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 POSITION;
layout(location = 1) in vec2 TEXCOORD0;
layout(location = 7) in vec4 COLOR;
// FIXME set the interpolation (don't know what dx do)
// flat means that there is no interpolation. The value given to the fragment shader is based on the provoking vertex conventions.
//
// noperspective means that there will be linear interpolation in window-space. This is usually not what you want, but it can have its uses.
//
// smooth, the default, means to do perspective-correct interpolation.
//
// The centroid qualifier only matters when multisampling. If this qualifier is not present, then the value is interpolated to the pixel's center, anywhere in the pixel, or to one of the pixel's samples. This sample may lie outside of the actual primitive being rendered, since a primitive can cover only part of a pixel's area. The centroid qualifier is used to prevent this; the interpolation point must fall within both the pixel's area and the primitive's area.
out vec4 PSin_p;
out vec2 PSin_t;
out vec4 PSin_c;
void vs_main()
{
PSin_p = vec4(POSITION, 0.5f, 1.0f);
PSin_t = TEXCOORD0;
PSin_c = COLOR;
gl_Position = vec4(POSITION, 0.5f, 1.0f); // NOTE I don't know if it is possible to merge POSITION_OUT and gl_Position
}
#endif
#ifdef FRAGMENT_SHADER
uniform vec4 u_source_rect;
uniform vec4 u_target_rect;
uniform vec2 u_source_size;
uniform vec2 u_target_size;
uniform vec2 u_target_resolution;
uniform vec2 u_rcp_target_resolution; // 1 / u_target_resolution
uniform vec2 u_source_resolution;
uniform vec2 u_rcp_source_resolution; // 1 / u_source_resolution
uniform float u_time;
in vec4 PSin_p;
in vec2 PSin_t;
in vec4 PSin_c;
layout(binding = 0) uniform sampler2D TextureSampler;
layout(location = 0) out vec4 SV_Target0;
vec4 sample_c()
{
return texture(TextureSampler, PSin_t);
}
vec4 sample_c(vec2 uv)
{
return texture(TextureSampler, uv);
}
vec4 ps_crt(uint i)
{
vec4 mask[4] = vec4[4](
vec4(1, 0, 0, 0),
vec4(0, 1, 0, 0),
vec4(0, 0, 1, 0),
vec4(1, 1, 1, 0));
return sample_c() * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
}
#ifdef ps_copy
void ps_copy()
{
SV_Target0 = sample_c();
}
#endif
#ifdef ps_sgsr
// Snapdragon Game Super Resolution 1.
// Copyright (c) 2025, Qualcomm Innovation Center, Inc.
// SPDX-License-Identifier: BSD-3-Clause
float sgsr_fast_lanczos2(float x)
{
float wa = x - 4.0;
float wb = x * wa - wa;
wa *= wa;
return wb * wa;
}
vec2 sgsr_weight_y(float dx, float dy, float c, float stddev)
{
float x = ((dx * dx) + (dy * dy)) * 0.55 + clamp(abs(c) * stddev, 0.0, 1.0);
float w = sgsr_fast_lanczos2(x);
return vec2(w, w * c);
}
void ps_sgsr()
{
// u_time carries the selected SGSR mode for this presentation shader.
// Quality covers subtle edges, Balanced is Qualcomm's reference tuning,
// and Performance evaluates only stronger edges.
float mode = clamp(floor(u_time + 0.5), 1.0, 3.0);
float edge_threshold = (mode < 1.5) ? (4.0 / 255.0) : ((mode < 2.5) ? (8.0 / 255.0) : (16.0 / 255.0));
float edge_sharpness = (mode < 1.5) ? 2.2 : ((mode < 2.5) ? 2.0 : 1.6);
vec4 color = vec4(textureLod(TextureSampler, PSin_t, 0.0).rgb, 1.0);
vec2 img_coord = PSin_t * u_source_resolution + vec2(-0.5, 0.5);
vec2 img_coord_pixel = floor(img_coord);
vec2 coord = img_coord_pixel * u_rcp_source_resolution;
vec2 pl = img_coord - img_coord_pixel;
vec4 left = textureGather(TextureSampler, coord, 1);
float edge_vote = abs(left.z - left.y) + abs(color.g - left.y) + abs(color.g - left.z);
if (edge_vote > edge_threshold)
{
coord.x += u_rcp_source_resolution.x;
vec4 right = textureGather(TextureSampler, coord + vec2(u_rcp_source_resolution.x, 0.0), 1);
vec4 up_down;
up_down.xy = textureGather(TextureSampler, coord + vec2(0.0, -u_rcp_source_resolution.y), 1).wz;
up_down.zw = textureGather(TextureSampler, coord + vec2(0.0, u_rcp_source_resolution.y), 1).yx;
float mean = (left.y + left.z + right.x + right.w) * 0.25;
left -= vec4(mean);
right -= vec4(mean);
up_down -= vec4(mean);
float center = color.g - mean;
float sum = dot(abs(left), vec4(1.0)) + dot(abs(right), vec4(1.0)) + dot(abs(up_down), vec4(1.0));
float stddev = 2.181818 / max(sum, 1.0e-6);
vec2 wy = sgsr_weight_y(pl.x, pl.y + 1.0, up_down.x, stddev);
wy += sgsr_weight_y(pl.x - 1.0, pl.y + 1.0, up_down.y, stddev);
wy += sgsr_weight_y(pl.x - 1.0, pl.y - 2.0, up_down.z, stddev);
wy += sgsr_weight_y(pl.x, pl.y - 2.0, up_down.w, stddev);
wy += sgsr_weight_y(pl.x + 1.0, pl.y - 1.0, left.x, stddev);
wy += sgsr_weight_y(pl.x, pl.y - 1.0, left.y, stddev);
wy += sgsr_weight_y(pl.x, pl.y, left.z, stddev);
wy += sgsr_weight_y(pl.x + 1.0, pl.y, left.w, stddev);
wy += sgsr_weight_y(pl.x - 1.0, pl.y - 1.0, right.x, stddev);
wy += sgsr_weight_y(pl.x - 2.0, pl.y - 1.0, right.y, stddev);
wy += sgsr_weight_y(pl.x - 2.0, pl.y, right.z, stddev);
wy += sgsr_weight_y(pl.x - 1.0, pl.y, right.w, stddev);
float final_y = wy.y / max(wy.x, 1.0e-6);
float max_y = max(max(left.y, left.z), max(right.x, right.w));
float min_y = min(min(left.y, left.z), min(right.x, right.w));
float delta_y = clamp(edge_sharpness * final_y, min_y, max_y) - center;
delta_y = clamp(delta_y, -23.0 / 255.0, 23.0 / 255.0);
color.rgb = clamp(color.rgb + vec3(delta_y), vec3(0.0), vec3(1.0));
}
SV_Target0 = color;
}
#endif
#ifdef ps_filter_scanlines
vec4 ps_scanlines(uint i)
{
vec4 mask[2] = vec4[2]
(
vec4(1, 1, 1, 0),
vec4(0, 0, 0, 0)
);
return sample_c() * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
}
void ps_filter_scanlines() // scanlines
{
highp uvec4 p = uvec4(gl_FragCoord);
vec4 c = ps_scanlines(p.y % 2u);
SV_Target0 = c;
}
#endif
#ifdef ps_filter_diagonal
void ps_filter_diagonal() // diagonal
{
highp uvec4 p = uvec4(gl_FragCoord);
vec4 c = ps_crt((p.x + (p.y % 3u)) % 3u);
SV_Target0 = c;
}
#endif
#ifdef ps_filter_triangular
void ps_filter_triangular() // triangular
{
highp uvec4 p = uvec4(gl_FragCoord);
vec4 c = ps_crt(((p.x + ((p.y >> 1u) & 1u) * 3u) >> 1u) % 3u);
SV_Target0 = c;
}
#endif
#ifdef ps_filter_complex
void ps_filter_complex()
{
const float PI = 3.14159265359f;
vec2 texdim = vec2(textureSize(TextureSampler, 0));
float factor = (0.9f - 0.4f * cos(2.0f * PI * PSin_t.y * texdim.y));
vec4 c = factor * texture(TextureSampler, vec2(PSin_t.x, (floor(PSin_t.y * texdim.y) + 0.5f) / texdim.y));
SV_Target0 = c;
}
#endif
#ifdef ps_filter_lottes
#define MaskingType 4 //[1|2|3|4] The type of CRT shadow masking used. 1: compressed TV style, 2: Aperture-grille, 3: Stretched VGA style, 4: VGA style.
#define ScanBrightness -8.00 //[-16.0 to 1.0] The overall brightness of the scanline effect. Lower for darker, higher for brighter.
#define FilterCRTAmount -3.00 //[-4.0 to 1.0] The amount of filtering used, to replicate the TV CRT look. Lower for less, higher for more.
#define HorizontalWarp 0.00 //[0.0 to 0.1] The distortion warping effect for the horizontal (x) axis of the screen. Use small increments.
#define VerticalWarp 0.00 //[0.0 to 0.1] The distortion warping effect for the verticle (y) axis of the screen. Use small increments.
#define MaskAmountDark 0.50 //[0.0 to 1.0] The value of the dark masking line effect used. Lower for darker lower end masking, higher for brighter.
#define MaskAmountLight 1.50 //[0.0 to 2.0] The value of the light masking line effect used. Lower for darker higher end masking, higher for brighter.
#define BloomPixel -1.50 //[-2.0 -0.5] Pixel bloom radius. Higher for increased softness of bloom.
#define BloomScanLine -2.0 //[-4.0 -1.0] Scanline bloom radius. Higher for increased softness of bloom.
#define BloomAmount 0.15 //[0.0 1.0] Bloom intensity. Higher for brighter.
#define Shape 2.0 //[0.0 10.0] Kernal filter shape. Lower values will darken image and introduce moire patterns if used with curvature.
#define UseShadowMask 1 //[0 or 1] Enables, or disables the use of the CRT shadow mask. 0 is disabled, 1 is enabled.
float ToLinear1(float c)
{
return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);
}
vec3 ToLinear(vec3 c)
{
return vec3(ToLinear1(c.r), ToLinear1(c.g), ToLinear1(c.b));
}
float ToSrgb1(float c)
{
return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 0.41666) - 0.055;
}
vec3 ToSrgb(vec3 c)
{
return vec3(ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b));
}
vec3 Fetch(vec2 pos, vec2 off)
{
pos = (floor(pos * u_target_size + off) + vec2(0.5, 0.5)) / u_target_size;
if (max(abs(pos.x - 0.5), abs(pos.y - 0.5)) > 0.5)
{
return vec3(0.0, 0.0, 0.0);
}
else
{
return ToLinear(texture(TextureSampler, pos.xy).rgb);
}
}
vec2 Dist(vec2 pos)
{
pos = pos * vec2(640, 480);
return -((pos - floor(pos)) - vec2(0.5, 0.5));
}
float Gaus(float pos, float scale)
{
return exp2(scale * pow(abs(pos), Shape));
}
vec3 Horz3(vec2 pos, float off)
{
vec3 b = Fetch(pos, vec2(-1.0, off));
vec3 c = Fetch(pos, vec2(0.0, off));
vec3 d = Fetch(pos, vec2(1.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = FilterCRTAmount;
float wb = Gaus(dst - 1.0, scale);
float wc = Gaus(dst + 0.0, scale);
float wd = Gaus(dst + 1.0, scale);
return (b * wb + c * wc + d * wd) / (wb + wc + wd);
}
vec3 Horz5(vec2 pos, float off)
{
vec3 a = Fetch(pos, vec2(-2.0, off));
vec3 b = Fetch(pos, vec2(-1.0, off));
vec3 c = Fetch(pos, vec2(0.0, off));
vec3 d = Fetch(pos, vec2(1.0, off));
vec3 e = Fetch(pos, vec2(2.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = FilterCRTAmount;
float wa = Gaus(dst - 2.0, scale);
float wb = Gaus(dst - 1.0, scale);
float wc = Gaus(dst + 0.0, scale);
float wd = Gaus(dst + 1.0, scale);
float we = Gaus(dst + 2.0, scale);
return (a * wa + b * wb + c * wc + d * wd + e * we) / (wa + wb + wc + wd + we);
}
vec3 Horz7(vec2 pos, float off)
{
vec3 a = Fetch(pos, vec2(-3.0, off));
vec3 b = Fetch(pos, vec2(-2.0, off));
vec3 c = Fetch(pos, vec2(-1.0, off));
vec3 d = Fetch(pos, vec2( 0.0, off));
vec3 e = Fetch(pos, vec2( 1.0, off));
vec3 f = Fetch(pos, vec2( 2.0, off));
vec3 g = Fetch(pos, vec2( 3.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = BloomPixel;
float wa = Gaus(dst - 3.0, scale);
float wb = Gaus(dst - 2.0, scale);
float wc = Gaus(dst - 1.0, scale);
float wd = Gaus(dst + 0.0, scale);
float we = Gaus(dst + 1.0, scale);
float wf = Gaus(dst + 2.0, scale);
float wg = Gaus(dst + 3.0, scale);
// Return filtered sample.
return (a * wa + b * wb + c * wc + d * wd + e * we + f * wf + g * wg) / (wa + wb + wc + wd + we + wf + wg);
}
// Return scanline weight.
float Scan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, ScanBrightness);
}
float BloomScan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, BloomScanLine);
}
vec3 Tri(vec2 pos)
{
vec3 a = Horz3(pos, -1.0);
vec3 b = Horz5(pos, 0.0);
vec3 c = Horz3(pos, 1.0);
float wa = Scan(pos, -1.0);
float wb = Scan(pos, 0.0);
float wc = Scan(pos, 1.0);
return (a * wa) + (b * wb) + (c * wc);
}
vec3 Bloom(vec2 pos)
{
vec3 a = Horz5(pos,-2.0);
vec3 b = Horz7(pos,-1.0);
vec3 c = Horz7(pos, 0.0);
vec3 d = Horz7(pos, 1.0);
vec3 e = Horz5(pos, 2.0);
float wa = BloomScan(pos,-2.0);
float wb = BloomScan(pos,-1.0);
float wc = BloomScan(pos, 0.0);
float wd = BloomScan(pos, 1.0);
float we = BloomScan(pos, 2.0);
return a * wa + b * wb + c * wc + d * wd + e * we;
}
vec2 Warp(vec2 pos)
{
pos = pos * 2.0 - 1.0;
pos *= vec2(1.0 + (pos.y * pos.y) * HorizontalWarp, 1.0 + (pos.x * pos.x) * VerticalWarp);
return pos * 0.5 + 0.5;
}
vec3 Mask(vec2 pos)
{
#if MaskingType == 1
// Very compressed TV style shadow mask.
float lines = MaskAmountLight;
float odd = 0.0;
if (fract(pos.x / 6.0) < 0.5)
{
odd = 1.0;
}
if (fract((pos.y + odd) / 2.0) < 0.5)
{
lines = MaskAmountDark;
}
pos.x = fract(pos.x / 3.0);
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
mask *= lines;
return mask;
#elif MaskingType == 2
// Aperture-grille.
pos.x = fract(pos.x / 3.0);
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#elif MaskingType == 3
// Stretched VGA style shadow mask (same as prior shaders).
pos.x += pos.y * 3.0;
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
pos.x = fract(pos.x / 6.0);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#else
// VGA style shadow mask.
pos.xy = floor(pos.xy * vec2(1.0, 0.5));
pos.x += pos.y * 3.0;
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
pos.x = fract(pos.x / 6.0);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#endif
}
vec4 LottesCRTPass()
{
//flipped y axis in opengl
vec2 fragcoord = vec2(gl_FragCoord.x, u_target_resolution.y - gl_FragCoord.y) - u_target_rect.xy;
vec4 color;
vec2 inSize = u_target_resolution - (2.0 * u_target_rect.xy);
vec2 pos = Warp(fragcoord.xy / inSize);
color.rgb = Tri(pos);
color.rgb += Bloom(pos) * BloomAmount;
#if UseShadowMask
color.rgb *= Mask(fragcoord.xy);
#endif
color.rgb = ToSrgb(color.rgb);
return color;
}
void ps_filter_lottes()
{
SV_Target0 = LottesCRTPass();
}
#endif
#ifdef ps_4x_rgss
void ps_4x_rgss()
{
vec2 dxy = vec2(dFdx(PSin_t.x), dFdy(PSin_t.y));
vec3 color = vec3(0);
float s = 1.0/8.0;
float l = 3.0/8.0;
color += sample_c(PSin_t + vec2( s, l) * dxy).rgb;
color += sample_c(PSin_t + vec2( l,-s) * dxy).rgb;
color += sample_c(PSin_t + vec2(-s,-l) * dxy).rgb;
color += sample_c(PSin_t + vec2(-l, s) * dxy).rgb;
SV_Target0 = vec4(color * 0.25,1);
}
#endif
#ifdef ps_automagical_supersampling
void ps_automagical_supersampling()
{
vec2 ratio = (u_source_size / u_target_size) * 0.5;
vec2 steps = floor(ratio);
vec3 col = sample_c(PSin_t).rgb;
float div = 1.0;
for (float y = 0.0; y < steps.y; y++)
{
for (float x = 0.0; x < steps.x; x++)
{
vec2 offset = vec2(x,y) - ratio * 0.5;
col += sample_c(PSin_t + offset * u_rcp_source_resolution * 2.0).rgb;
div++;
}
}
SV_Target0 = vec4(col / div, 1);
}
#endif
#endif
@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
/*
** Contrast, saturation, brightness
** Code of this function is from TGM's shader pack
** http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=21057
** TGM's author comment about the license (included in the previous link)
** "do with it, what you want! its total free!
** (but would be nice, if you say that you used my shaders :wink: ) but not necessary"
*/
#ifdef FRAGMENT_SHADER
uniform vec4 params;
in vec4 PSin_p;
in vec2 PSin_t;
in vec4 PSin_c;
layout(binding = 0) uniform sampler2D TextureSampler;
layout(location = 0) out vec4 SV_Target0;
// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150%
vec4 ContrastSaturationBrightness(vec4 color)
{
float brt = params.x;
float con = params.y;
float sat = params.z;
// Increase or decrease these values to adjust r, g and b color channels separately
const float AvgLumR = 0.5;
const float AvgLumG = 0.5;
const float AvgLumB = 0.5;
const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
vec3 brtColor = color.rgb * brt;
float dot_intensity = dot(brtColor, LumCoeff);
vec3 intensity = vec3(dot_intensity, dot_intensity, dot_intensity);
vec3 satColor = mix(intensity, brtColor, sat);
vec3 conColor = mix(AvgLumin, satColor, con);
color.rgb = conColor;
return color;
}
void ps_main()
{
vec4 c = texture(TextureSampler, PSin_t);
SV_Target0 = ContrastSaturationBrightness(c);
}
#endif
@@ -0,0 +1,71 @@
#version 460 core
#extension GL_EXT_samplerless_texture_functions : require
// Based on CAS_Shader.glsl
// Copyright(c) 2019 Advanced Micro Devices, Inc.All rights reserved.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
layout(push_constant) uniform const_buffer
{
uvec4 const0;
uvec4 const1;
ivec2 srcOffset;
};
layout(set=0, binding=0) uniform texture2D imgSrc;
layout(set=0, binding=1, rgba8) uniform writeonly image2D imgDst;
layout(constant_id=0) const int sharpenOnly = 0;
#define A_GPU 1
#define A_GLSL 1
#include "ffx_a.h"
AF3 CasLoad(ASU2 p)
{
return texelFetch(imgSrc, srcOffset + ivec2(p), 0).rgb;
}
// Lets you transform input from the load into a linear color space between 0 and 1. See ffx_cas.h
// In this case, our input is already linear and between 0 and 1
void CasInput(inout AF1 r, inout AF1 g, inout AF1 b) {}
#include "ffx_cas.h"
layout(local_size_x=64) in;
void main()
{
// Do remapping of local xy in workgroup for a more PS-like swizzle pattern.
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x)+AU2(gl_WorkGroupID.x<<4u,gl_WorkGroupID.y<<4u);
// Filter.
AF4 c = vec4(0.0f);
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
gxy.x += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
gxy.y += 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
gxy.x -= 8u;
CasFilter(c.r, c.g, c.b, gxy, const0, const1, sharpenOnly != 0);
imageStore(imgDst, ASU2(gxy), c);
}
@@ -0,0 +1,636 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
#ifdef FRAGMENT_SHADER
layout(location = 0) in vec2 v_tex;
#if defined(ps_convert_rgba8_16bits) || defined(ps_convert_float32_32bits)
layout(location = 0) out uint o_col0;
#elif defined(ps_convert_float32_depth_to_color)
layout(location = 0) out float o_col0;
#elif !defined(ps_datm1) && \
!defined(ps_datm0) && \
!defined(ps_datm1_rta_correction) && \
!defined(ps_datm0_rta_correction) && \
!defined(ps_convert_rgba8_float32) && \
!defined(ps_convert_rgba8_float24) && \
!defined(ps_convert_rgba8_float16) && \
!defined(ps_convert_rgb5a1_float16) && \
!defined(ps_convert_rgba8_float32_biln) && \
!defined(ps_convert_rgba8_float24_biln) && \
!defined(ps_convert_rgba8_float16_biln) && \
!defined(ps_convert_rgb5a1_float16_biln) && \
!defined(ps_depth_copy)
layout(location = 0) out vec4 o_col0;
#endif
layout(set = 0, binding = 0) uniform sampler2D samp0;
vec4 sample_c(vec2 uv)
{
return texture(samp0, uv);
}
#ifdef ps_copy
void ps_copy()
{
o_col0 = sample_c(v_tex);
}
#endif
#ifdef ps_depth_copy
void ps_depth_copy()
{
gl_FragDepth = sample_c(v_tex).r;
}
#endif
#ifdef ps_downsample_copy
layout(push_constant) uniform cb10
{
ivec2 ClampMin;
int DownsampleFactor;
int pad0;
float Weight;
float step_multiplier;
vec2 pad1;
};
void ps_downsample_copy()
{
int step = int(step_multiplier);
ivec2 coord = max(ivec2(gl_FragCoord.xy) * DownsampleFactor, ClampMin);
vec4 result = vec4(0);
for (int yoff = 0; yoff < DownsampleFactor; yoff++)
{
for (int xoff = 0; xoff < DownsampleFactor; xoff++)
result += texelFetch(samp0, coord + ivec2(xoff * step, yoff * step), 0);
}
o_col0 = result / Weight;
}
#endif
#ifdef ps_filter_transparency
void ps_filter_transparency()
{
vec4 c = sample_c(v_tex);
o_col0 = vec4(c.rgb, 1.0);
}
#endif
#ifdef ps_convert_rgba8_16bits
// Need to be careful with precision here, it can break games like Spider-Man 3 and Dogs Life
void ps_convert_rgba8_16bits()
{
uvec4 i = uvec4(sample_c(v_tex) * vec4(255.5f, 255.5f, 255.5f, 255.5f));
o_col0 = ((i.x & 0x00F8u) >> 3) | ((i.y & 0x00F8u) << 2) | ((i.z & 0x00f8u) << 7) | ((i.w & 0x80u) << 8);
}
#endif
#ifdef ps_datm1
void ps_datm1()
{
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
discard;
}
#endif
#ifdef ps_datm0
void ps_datm0()
{
if((127.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
discard;
}
#endif
#ifdef ps_datm1_rta_correction
void ps_datm1_rta_correction()
{
if(sample_c(v_tex).a < (254.5f / 255.0f)) // >= 0x80 pass
discard;
}
#endif
#ifdef ps_datm0_rta_correction
void ps_datm0_rta_correction()
{
if((254.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
discard;
}
#endif
#ifdef ps_rta_correction
void ps_rta_correction()
{
vec4 value = sample_c(v_tex);
o_col0 = vec4(value.rgb, value.a / (128.25f / 255.0f));
}
#endif
#ifdef ps_rta_decorrection
void ps_rta_decorrection()
{
vec4 value = sample_c(v_tex);
o_col0 = vec4(value.rgb, value.a * (128.25f / 255.0f));
}
#endif
#ifdef ps_colclip_init
void ps_colclip_init()
{
vec4 value = sample_c(v_tex);
o_col0 = vec4(roundEven(value.rgb * 255.0f) / 65535.0f, value.a);
}
#endif
#ifdef ps_colclip_resolve
void ps_colclip_resolve()
{
vec4 value = sample_c(v_tex);
o_col0 = vec4(vec3(gpu_bitwise_and(uvec3(value.rgb * 65535.5f), uvec3(255u))) / 255.0f, value.a);
}
#endif
#ifdef ps_convert_float32_depth_to_color
void ps_convert_float32_depth_to_color()
{
o_col0 = sample_c(v_tex).r;
}
#endif
#ifdef ps_convert_float32_color_to_depth
void ps_convert_float32_color_to_depth()
{
gl_FragDepth = sample_c(v_tex).r;
}
#endif
#ifdef ps_convert_float32_32bits
void ps_convert_float32_32bits()
{
// Convert a vec32 depth texture into a 32 bits UINT texture
o_col0 = uint(exp2(32.0f) * sample_c(v_tex).r);
}
#endif
#ifdef ps_convert_float32_rgba8
void ps_convert_float32_rgba8()
{
// Convert a vec32 depth texture into a RGBA color texture
uint d = uint(sample_c(v_tex).r * exp2(32.0f));
o_col0 = vec4(uvec4((d & 0xFFu), ((d >> 8) & 0xFFu), ((d >> 16) & 0xFFu), (d >> 24))) / vec4(255.0);
}
#endif
#ifdef ps_convert_float16_rgb5a1
void ps_convert_float16_rgb5a1()
{
// Convert a vec32 (only 16 lsb) depth into a RGB5A1 color texture
uint d = uint(sample_c(v_tex).r * exp2(32.0f));
o_col0 = vec4(gpu_bitwise_and(uvec4(d << 3, d >> 2, d >> 7, d >> 8), uvec4(0xf8, 0xf8, 0xf8, 0x80))) / 255.0f;
}
#endif
float rgba8_to_depth32(vec4 unorm)
{
uvec4 c = uvec4(unorm * vec4(255.5f));
return float(c.r | (c.g << 8) | (c.b << 16) | (c.a << 24)) * exp2(-32.0f);
}
float rgba8_to_depth24(vec4 unorm)
{
uvec3 c = uvec3(unorm.rgb * vec3(255.5f));
return float(c.r | (c.g << 8) | (c.b << 16)) * exp2(-32.0f);
}
float rgba8_to_depth16(vec4 unorm)
{
uvec2 c = uvec2(unorm.rg * vec2(255.5f));
return float(c.r | (c.g << 8)) * exp2(-32.0f);
}
float rgb5a1_to_depth16(vec4 unorm)
{
uvec4 c = uvec4(unorm * vec4(255.5f));
return float(((c.r & 0xF8u) >> 3) | ((c.g & 0xF8u) << 2) | ((c.b & 0xF8u) << 7) | ((c.a & 0x80u) << 8)) * exp2(-32.0f);
}
#ifdef ps_convert_float32_float24
void ps_convert_float32_float24()
{
// Truncates depth value to 24bits
uint d = uint(sample_c(v_tex).r * exp2(32.0f)) & 0xFFFFFFu;
gl_FragDepth = float(d) * exp2(-32.0f);
}
#endif
#ifdef ps_convert_rgba8_float32
void ps_convert_rgba8_float32()
{
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth32(sample_c(v_tex));
}
#endif
#ifdef ps_convert_rgba8_float24
void ps_convert_rgba8_float24()
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth24(sample_c(v_tex));
}
#endif
#ifdef ps_convert_rgba8_float16
void ps_convert_rgba8_float16()
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
gl_FragDepth = rgba8_to_depth16(sample_c(v_tex));
}
#endif
#ifdef ps_convert_rgb5a1_float16
void ps_convert_rgb5a1_float16()
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
gl_FragDepth = rgb5a1_to_depth16(sample_c(v_tex));
}
#endif
#define SAMPLE_RGBA_DEPTH_BILN(CONVERT_FN) \
ivec2 dims = textureSize(samp0, 0); \
vec2 top_left_f = v_tex * vec2(dims) - 0.5f; \
ivec2 top_left = ivec2(floor(top_left_f)); \
ivec4 coords = clamp(ivec4(top_left, top_left + 1), ivec4(0), dims.xyxy - 1); \
vec2 mix_vals = fract(top_left_f); \
float depthTL = CONVERT_FN(texelFetch(samp0, coords.xy, 0)); \
float depthTR = CONVERT_FN(texelFetch(samp0, coords.zy, 0)); \
float depthBL = CONVERT_FN(texelFetch(samp0, coords.xw, 0)); \
float depthBR = CONVERT_FN(texelFetch(samp0, coords.zw, 0)); \
gl_FragDepth = mix(mix(depthTL, depthTR, mix_vals.x), mix(depthBL, depthBR, mix_vals.x), mix_vals.y);
#ifdef ps_convert_rgba8_float32_biln
void ps_convert_rgba8_float32_biln()
{
// Convert an RGBA texture into a float depth texture
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth32);
}
#endif
#ifdef ps_convert_rgba8_float24_biln
void ps_convert_rgba8_float24_biln()
{
// Same as above but without the alpha channel (24 bits Z)
// Convert an RGBA texture into a float depth texture
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth24);
}
#endif
#ifdef ps_convert_rgba8_float16_biln
void ps_convert_rgba8_float16_biln()
{
// Same as above but without the A/B channels (16 bits Z)
// Convert an RGBA texture into a float depth texture
SAMPLE_RGBA_DEPTH_BILN(rgba8_to_depth16);
}
#endif
#ifdef ps_convert_rgb5a1_float16_biln
void ps_convert_rgb5a1_float16_biln()
{
// Convert an RGB5A1 (saved as RGBA8) color to a 16 bit Z
SAMPLE_RGBA_DEPTH_BILN(rgb5a1_to_depth16);
}
#endif
#ifdef ps_convert_rgb5a1_8i
layout(push_constant) uniform cb10
{
uint SBW;
uint DBW;
uint PSM;
float cb_pad1;
float ScaleFactor;
vec3 cb_pad2;
};
void ps_convert_rgb5a1_8i()
{
// Convert a RGB5A1 texture into a 8 bits packed texture
// Input column: 16x2 RGB5A1 pixels
// 0: 16 RGBA
// 1: 16 RGBA
// Output column: 16x4 Index pixels
// 0: 16 R5G2
// 1: 16 R5G2
// 2: 16 G2B5A1
// 3: 16 G2B5A1
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 column = gpu_bitwise_and(pos, ~uvec2(0u, 3u)) / uvec2(1,2);
uvec2 subcolumn = gpu_bitwise_and(pos, uvec2(0u, 1u));
column.x -= (column.x / 128) * 64;
column.y += (column.y / 32) * 32;
// Deal with swizzling differences
if ((PSM & 0x8) != 0) // PSMCT16S
{
if ((pos.x & 32) != 0)
{
column.y += 32; // 4 columns high times 4 to get bottom 4 blocks
column.x &= ~32;
}
if ((pos.x & 64) != 0)
{
column.x -= 32;
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
}
if ((PSM & 0x30) != 0) // PSMZ16S - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 16;
}
}
else // PSMCT16
{
if ((pos.y & 32) != 0)
{
column.y -= 16;
column.x += 32;
}
if ((pos.x & 96) != 0)
{
uint multi = (pos.x & 96) / 32;
column.y += 16 * multi; // 4 columns high times 4 to get bottom 4 blocks
column.x -= (pos.x & 96);
}
if (((pos.x & 16) != 0) != ((pos.y & 16) != 0))
{
column.x ^= 16;
column.y ^= 8;
}
if ((PSM & 0x30) != 0) // PSMZ16 - Untested but hopefully ok if anything uses it.
{
column.x ^= 32;
column.y ^= 32;
}
}
uvec2 coord = column | subcolumn;
// Compensate for potentially differing page pitch.
uvec2 block_xy = coord / uvec2(64u, 64u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 64u);
coord = (coord % uvec2(64u, 64u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (floor(ScaleFactor) != ScaleFactor)
coord = uvec2(vec2(coord) * ScaleFactor);
else
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(samp0, ivec2(coord), 0);
uvec4 denorm_c = uvec4(pixel * 255.5f);
if ((pos.y & 2u) == 0u)
{
uint red = (denorm_c.r >> 3) & 0x1Fu;
uint green = (denorm_c.g >> 3) & 0x1Fu;
float sel0 = float(((green << 5) | red) & 0xFF) / 255.0f;
o_col0 = vec4(sel0);
}
else
{
uint green = (denorm_c.g >> 3) & 0x1Fu;
uint blue = (denorm_c.b >> 3) & 0x1Fu;
uint alpha = denorm_c.a & 0x80u;
float sel0 = float((alpha | (blue << 2) | (green >> 3)) & 0xFF) / 255.0f;
o_col0 = vec4(sel0);
}
}
#endif
#ifdef ps_convert_rgba_8i
layout(push_constant) uniform cb10
{
uint SBW;
uint DBW;
uint PSM;
float cb_pad1;
float ScaleFactor;
vec3 cb_pad2;
};
void ps_convert_rgba_8i()
{
// Convert a RGBA texture into a 8 bits packed texture
// Input column: 8x2 RGBA pixels
// 0: 8 RGBA
// 1: 8 RGBA
// Output column: 16x4 Index pixels
// 0: 8 R | 8 B
// 1: 8 R | 8 B
// 2: 8 G | 8 A
// 3: 8 G | 8 A
uvec2 pos = uvec2(gl_FragCoord.xy);
// Collapse separate R G B A areas into their base pixel
uvec2 block = gpu_bitwise_and(pos, ~uvec2(15u, 3u)) >> 1;
uvec2 subblock = gpu_bitwise_and(pos, uvec2(7u, 1u));
uvec2 coord = block | subblock;
// Compensate for potentially differing page pitch.
uvec2 block_xy = coord / uvec2(64u, 32u);
uint block_num = (block_xy.y * (DBW / 128u)) + block_xy.x;
uvec2 block_offset = uvec2((block_num % (SBW / 64u)) * 64u, (block_num / (SBW / 64u)) * 32u);
coord = (coord % uvec2(64u, 32u)) + block_offset;
// Apply offset to cols 1 and 2
uint is_col23 = pos.y & 4u;
uint is_col13 = pos.y & 2u;
uint is_col12 = is_col23 ^ (is_col13 << 1);
coord.x ^= is_col12; // If cols 1 or 2, flip bit 3 of x
if (floor(ScaleFactor) != ScaleFactor)
coord = uvec2(vec2(coord) * ScaleFactor);
else
coord *= uvec2(ScaleFactor);
vec4 pixel = texelFetch(samp0, ivec2(coord), 0);
vec2 sel0 = (pos.y & 2u) == 0u ? pixel.rb : pixel.ga;
float sel1 = (pos.x & 8u) == 0u ? sel0.x : sel0.y;
o_col0 = vec4(sel1); // Divide by something here?
}
#endif
#ifdef ps_convert_clut_4
layout(push_constant) uniform cb10
{
uvec2 offset;
uint doffset;
uint cb_pad1;
float scale;
vec3 cb_pad2;
};
void ps_convert_clut_4()
{
// CLUT4 is easy, just two rows of 8x8.
uint index = uint(gl_FragCoord.x) + doffset;
uvec2 pos = uvec2(index % 8u, index / 8u);
ivec2 final = ivec2(floor(vec2(offset + pos) * vec2(scale)));
o_col0 = texelFetch(samp0, final, 0);
}
#endif
#ifdef ps_convert_clut_8
layout(push_constant) uniform cb10
{
uvec2 offset;
uint doffset;
uint cb_pad1;
float scale;
vec3 cb_pad2;
};
void ps_convert_clut_8()
{
uint index = min(uint(gl_FragCoord.x) + doffset, 255u);
// CLUT is arranged into 8 groups of 16x2, with the top-right and bottom-left quadrants swapped.
// This can probably be done better..
uint subgroup = (index / 8u) % 4u;
uvec2 pos;
pos.x = (index % 8u) + ((subgroup >= 2u) ? 8u : 0u);
pos.y = ((index / 32u) * 2u) + (subgroup % 2u);
ivec2 final = ivec2(floor(vec2(offset + pos) * vec2(scale)));
o_col0 = texelFetch(samp0, final, 0);
}
#endif
#ifdef ps_yuv
layout(push_constant) uniform cb10
{
int EMODA;
int EMODC;
};
void ps_yuv()
{
vec4 i = sample_c(v_tex);
vec4 o = vec4(0.0f);
mat3 rgb2yuv;
rgb2yuv[0] = vec3(0.587, -0.311, -0.419);
rgb2yuv[1] = vec3(0.114, 0.500, -0.081);
rgb2yuv[2] = vec3(0.299, -0.169, 0.500);
vec3 yuv = rgb2yuv * i.gbr;
float Y = float(0xDB)/255.0f * yuv.x + float(0x10)/255.0f;
float Cr = float(0xE0)/255.0f * yuv.y + float(0x80)/255.0f;
float Cb = float(0xE0)/255.0f * yuv.z + float(0x80)/255.0f;
switch(EMODA)
{
case 0:
o.a = i.a;
break;
case 1:
o.a = Y;
break;
case 2:
o.a = Y/2.0f;
break;
case 3:
o.a = 0.0f;
break;
}
switch(EMODC)
{
case 0:
o.rgb = i.rgb;
break;
case 1:
o.rgb = vec3(Y);
break;
case 2:
o.rgb = vec3(Y, Cb, Cr);
break;
case 3:
o.rgb = vec3(i.a);
break;
}
o_col0 = o;
}
#endif
#if defined(ps_stencil_image_init_0) || defined(ps_stencil_image_init_1) || defined(ps_stencil_image_init_2) || defined(ps_stencil_image_init_3)
void main()
{
o_col0 = vec4(0x7FFFFFFF);
#ifdef ps_stencil_image_init_0
if((127.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
o_col0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_1
if(sample_c(v_tex).a < (127.5f / 255.0f)) // >= 0x80 pass
o_col0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_2
if((254.5f / 255.0f) < sample_c(v_tex).a) // < 0x80 pass (== 0x80 should not pass)
o_col0 = vec4(-1);
#endif
#ifdef ps_stencil_image_init_3
if(sample_c(v_tex).a < (254.5f / 255.0f)) // >= 0x80 pass
o_col0 = vec4(-1);
#endif
}
#endif
#endif
@@ -0,0 +1,42 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 Position;
layout(location = 1) in vec2 UV;
layout(location = 2) in vec4 Color;
layout(push_constant) uniform PushConstants
{
vec2 uScale;
vec2 uTranslate;
};
layout(location = 0) out vec2 Frag_UV;
layout(location = 1) out vec4 Frag_Color;
void vs_main()
{
Frag_UV = UV;
Frag_Color = Color;
gl_Position = vec4(Position * uScale + uTranslate, 0.0f, 1.0f);
}
#endif
#ifdef FRAGMENT_SHADER
layout(binding = 0) uniform sampler2D Texture;
layout(location = 0) in vec2 Frag_UV;
layout(location = 1) in vec4 Frag_Color;
layout(location = 0) out vec4 Out_Color;
void ps_main()
{
Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
}
#endif
@@ -0,0 +1,203 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
#ifdef FRAGMENT_SHADER
layout(location = 0) in vec2 v_tex;
layout(location = 0) out vec4 o_col0;
layout(push_constant) uniform cb0
{
vec4 ZrH;
};
layout(set = 0, binding = 0) uniform sampler2D samp0;
// Weave shader
#ifdef ps_main0
void ps_main0()
{
const int idx = int(ZrH.x); // buffer index passed from CPU
const int field = idx & 1; // current field
const int vpos = int(gl_FragCoord.y); // vertical position of destination texture
if ((vpos & 1) == field)
o_col0 = textureLod(samp0, v_tex, 0);
else
discard;
}
#endif
// Bob shader
#ifdef ps_main1
void ps_main1()
{
o_col0 = textureLod(samp0, v_tex, 0);
}
#endif
// Blend shader
#ifdef ps_main2
void ps_main2()
{
vec2 vstep = vec2(0.0f, ZrH.y);
vec4 c0 = textureLod(samp0, v_tex - vstep, 0);
vec4 c1 = textureLod(samp0, v_tex, 0);
vec4 c2 = textureLod(samp0, v_tex + vstep, 0);
o_col0 = (c0 + c1 * 2.0f + c2) / 4.0f;
}
#endif
// MAD shader - buffering
#ifdef ps_main3
void ps_main3()
{
// We take half the lines from the current frame and stores them in the MAD frame buffer.
// the MAD frame buffer is split in 2 consecutive banks of 2 fields each, the fields in each bank
// are interleaved (top field at even lines and bottom field at odd lines).
// When the source texture has an odd vres, the first line of bank 1 would be an odd index
// causing the wrong lines to be discarded, so a vertical offset (lofs) is added to the vertical
// position of the destination texture to force the proper field alignment
const int idx = int(ZrH.x); // buffer index passed from CPU
const int bank = idx >> 1; // current bank
const int field = idx & 1; // current field
const int vres = int(ZrH.z) >> 1; // vertical resolution of source texture
const int lofs = ((((vres + 1) >> 1) << 1) - vres) & bank; // line alignment offset for bank 1
const int vpos = int(gl_FragCoord.y) + lofs; // vertical position of destination texture
// if the index of current destination line belongs to the current fiels we update it, otherwise
// we leave the old line in the destination buffer
if ((vpos & 1) == field)
o_col0 = textureLod(samp0, v_tex, 0);
else
discard;
}
#endif
// MAD shader - reconstruction
#ifdef ps_main4
void ps_main4()
{
// we use the contents of the MAD frame buffer to reconstruct the missing lines from the current
// field.
const int idx = int(ZrH.x); // buffer index passed from CPU
const int bank = idx >> 1; // current bank
const int field = idx & 1; // current field
const int vpos = int(gl_FragCoord.y); // vertical position of destination texture
const float sensitivity = ZrH.w; // passed from CPU, higher values mean more likely to use weave
const vec3 motion_thr = vec3(1.0, 1.0, 1.0) * sensitivity; //
const vec2 bofs = vec2(0.0f, 0.5f); // position of the bank 1 relative to source texture size
const vec2 vscale = vec2(1.0f, 0.5f); // scaling factor from source to destination texture
const vec2 lofs = vec2(0.0f, ZrH.y) * vscale; // distance between two adjacent lines relative to source texture size
const vec2 iptr = v_tex * vscale; // pointer to the current pixel in the source texture
vec2 p_t0; // pointer to current pixel (missing or not) from most recent frame
vec2 p_t1; // pointer to current pixel (missing or not) from one frame back
vec2 p_t2; // pointer to current pixel (missing or not) from two frames back
vec2 p_t3; // pointer to current pixel (missing or not) from three frames back
switch (idx)
{
case 1:
p_t0 = iptr;
p_t1 = iptr;
p_t2 = iptr + bofs;
p_t3 = iptr + bofs;
break;
case 2:
p_t0 = iptr + bofs;
p_t1 = iptr;
p_t2 = iptr;
p_t3 = iptr + bofs;
break;
case 3:
p_t0 = iptr + bofs;
p_t1 = iptr + bofs;
p_t2 = iptr;
p_t3 = iptr;
break;
default:
p_t0 = iptr;
p_t1 = iptr + bofs;
p_t2 = iptr + bofs;
p_t3 = iptr;
break;
}
// calculating motion, only relevant for missing lines where the "center line" is pointed by p_t1
vec4 hn = textureLod(samp0, p_t0 - lofs, 0); // new high pixel
vec4 cn = textureLod(samp0, p_t1, 0); // new center pixel
vec4 ln = textureLod(samp0, p_t0 + lofs, 0); // new low pixel
vec4 ho = textureLod(samp0, p_t2 - lofs, 0); // old high pixel
vec4 co = textureLod(samp0, p_t3, 0); // old center pixel
vec4 lo = textureLod(samp0, p_t2 + lofs, 0); // old low pixel
vec3 mh = hn.rgb - ho.rgb; // high pixel motion
vec3 mc = cn.rgb - co.rgb; // center pixel motion
vec3 ml = ln.rgb - lo.rgb; // low pixel motion
mh = max(mh, -mh) - motion_thr;
mc = max(mc, -mc) - motion_thr;
ml = max(ml, -ml) - motion_thr;
#if 1 // use this code to evaluate each color motion separately
float mh_max = max(max(mh.x, mh.y), mh.z);
float mc_max = max(max(mc.x, mc.y), mc.z);
float ml_max = max(max(ml.x, ml.y), ml.z);
#else // use this code to evaluate average color motion
float mh_max = mh.x + mh.y + mh.z;
float mc_max = mc.x + mc.y + mc.z;
float ml_max = ml.x + ml.y + ml.z;
#endif
// selecting deinterlacing output
if ((vpos & 1) == field) // output coordinate present on current field
{
// output coordinate present on current field
o_col0 = textureLod(samp0, p_t0, 0);
}
else if ((iptr.y > 0.5f - lofs.y) || (iptr.y < 0.0 + lofs.y))
{
// top and bottom lines are always weaved
o_col0 = cn;
}
else
{
// missing line needs to be reconstructed
if(((mh_max > 0.0f) || (ml_max > 0.0f)) || (mc_max > 0.0f))
// high motion -> interpolate pixels above and below
o_col0 = (hn + ln) / 2.0f;
else
// low motion -> weave
o_col0 = cn;
}
}
#endif
#endif
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
#ifdef FRAGMENT_SHADER
layout(location = 0) in vec2 v_tex;
layout(location = 0) out vec4 o_col0;
layout(push_constant) uniform cb10
{
vec4 BGColor;
};
layout(set = 0, binding = 0) uniform sampler2D samp0;
void ps_main0()
{
vec4 c = texture(samp0, v_tex);
// Note: clamping will be done by fixed unit
c.a *= 2.0f;
o_col0 = c;
}
void ps_main1()
{
vec4 c = texture(samp0, v_tex);
c.a = BGColor.a;
o_col0 = c;
}
#endif
@@ -0,0 +1,523 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
#ifdef FRAGMENT_SHADER
layout(push_constant) uniform cb10
{
vec4 u_source_rect;
vec4 u_target_rect;
vec2 u_source_size;
vec2 u_target_size;
vec2 u_target_resolution;
vec2 u_rcp_target_resolution; // 1 / u_target_resolution
vec2 u_source_resolution;
vec2 u_rcp_source_resolution; // 1 / u_source_resolution
float u_time;
};
layout(location = 0) in vec2 v_tex;
layout(location = 0) out vec4 o_col0;
layout(set = 0, binding = 0) uniform sampler2D samp0;
vec4 sample_c(vec2 uv)
{
return texture(samp0, uv);
}
vec4 ps_crt(uint i)
{
vec4 mask[4] = vec4[4](
vec4(1, 0, 0, 0),
vec4(0, 1, 0, 0),
vec4(0, 0, 1, 0),
vec4(1, 1, 1, 0));
return sample_c(v_tex) * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
}
vec4 ps_scanlines(uint i)
{
vec4 mask[2] =
{
vec4(1, 1, 1, 0),
vec4(0, 0, 0, 0)};
return sample_c(v_tex) * clamp((mask[i] + 0.5f), 0.0f, 1.0f);
}
#ifdef ps_copy
void ps_copy()
{
o_col0 = sample_c(v_tex);
}
#endif
#ifdef ps_sgsr
// Snapdragon Game Super Resolution 1.
// Copyright (c) 2025, Qualcomm Innovation Center, Inc.
// SPDX-License-Identifier: BSD-3-Clause
float sgsr_fast_lanczos2(float x)
{
float wa = x - 4.0;
float wb = x * wa - wa;
wa *= wa;
return wb * wa;
}
vec2 sgsr_weight_y(float dx, float dy, float c, float stddev)
{
float x = ((dx * dx) + (dy * dy)) * 0.55 + clamp(abs(c) * stddev, 0.0, 1.0);
float w = sgsr_fast_lanczos2(x);
return vec2(w, w * c);
}
void ps_sgsr()
{
// u_time carries the selected SGSR mode for this presentation shader.
// Quality covers subtle edges, Balanced is Qualcomm's reference tuning,
// and Performance evaluates only stronger edges.
float mode = clamp(floor(u_time + 0.5), 1.0, 3.0);
float edge_threshold = (mode < 1.5) ? (4.0 / 255.0) : ((mode < 2.5) ? (8.0 / 255.0) : (16.0 / 255.0));
float edge_sharpness = (mode < 1.5) ? 2.2 : ((mode < 2.5) ? 2.0 : 1.6);
vec4 color = vec4(textureLod(samp0, v_tex, 0.0).rgb, 1.0);
vec2 img_coord = v_tex * u_source_resolution + vec2(-0.5, 0.5);
vec2 img_coord_pixel = floor(img_coord);
vec2 coord = img_coord_pixel * u_rcp_source_resolution;
vec2 pl = img_coord - img_coord_pixel;
vec4 left = textureGather(samp0, coord, 1);
float edge_vote = abs(left.z - left.y) + abs(color.g - left.y) + abs(color.g - left.z);
if (edge_vote > edge_threshold)
{
coord.x += u_rcp_source_resolution.x;
vec4 right = textureGather(samp0, coord + vec2(u_rcp_source_resolution.x, 0.0), 1);
vec4 up_down;
up_down.xy = textureGather(samp0, coord + vec2(0.0, -u_rcp_source_resolution.y), 1).wz;
up_down.zw = textureGather(samp0, coord + vec2(0.0, u_rcp_source_resolution.y), 1).yx;
float mean = (left.y + left.z + right.x + right.w) * 0.25;
left -= vec4(mean);
right -= vec4(mean);
up_down -= vec4(mean);
float center = color.g - mean;
float sum = dot(abs(left), vec4(1.0)) + dot(abs(right), vec4(1.0)) + dot(abs(up_down), vec4(1.0));
float stddev = 2.181818 / max(sum, 1.0e-6);
vec2 wy = sgsr_weight_y(pl.x, pl.y + 1.0, up_down.x, stddev);
wy += sgsr_weight_y(pl.x - 1.0, pl.y + 1.0, up_down.y, stddev);
wy += sgsr_weight_y(pl.x - 1.0, pl.y - 2.0, up_down.z, stddev);
wy += sgsr_weight_y(pl.x, pl.y - 2.0, up_down.w, stddev);
wy += sgsr_weight_y(pl.x + 1.0, pl.y - 1.0, left.x, stddev);
wy += sgsr_weight_y(pl.x, pl.y - 1.0, left.y, stddev);
wy += sgsr_weight_y(pl.x, pl.y, left.z, stddev);
wy += sgsr_weight_y(pl.x + 1.0, pl.y, left.w, stddev);
wy += sgsr_weight_y(pl.x - 1.0, pl.y - 1.0, right.x, stddev);
wy += sgsr_weight_y(pl.x - 2.0, pl.y - 1.0, right.y, stddev);
wy += sgsr_weight_y(pl.x - 2.0, pl.y, right.z, stddev);
wy += sgsr_weight_y(pl.x - 1.0, pl.y, right.w, stddev);
float final_y = wy.y / max(wy.x, 1.0e-6);
float max_y = max(max(left.y, left.z), max(right.x, right.w));
float min_y = min(min(left.y, left.z), min(right.x, right.w));
float delta_y = clamp(edge_sharpness * final_y, min_y, max_y) - center;
delta_y = clamp(delta_y, -23.0 / 255.0, 23.0 / 255.0);
color.rgb = clamp(color.rgb + vec3(delta_y), vec3(0.0), vec3(1.0));
}
o_col0 = color;
}
#endif
#ifdef ps_filter_scanlines
void ps_filter_scanlines() // scanlines
{
uvec4 p = uvec4(gl_FragCoord);
o_col0 = ps_scanlines(p.y % 2);
}
#endif
#ifdef ps_filter_diagonal
void ps_filter_diagonal() // diagonal
{
uvec4 p = uvec4(gl_FragCoord);
o_col0 = ps_crt((p.x + (p.y % 3)) % 3);
}
#endif
#ifdef ps_filter_triangular
void ps_filter_triangular() // triangular
{
uvec4 p = uvec4(gl_FragCoord);
// output.c = ps_crt(input, ((p.x + (p.y & 1) * 3) >> 1) % 3);
o_col0 = ps_crt(((p.x + ((p.y >> 1) & 1) * 3) >> 1) % 3);
}
#endif
#ifdef ps_filter_complex
void ps_filter_complex() // triangular
{
const float PI = 3.14159265359f;
vec2 texdim = vec2(textureSize(samp0, 0));
o_col0 = (0.9 - 0.4 * cos(2 * PI * v_tex.y * texdim.y)) * sample_c(vec2(v_tex.x, (floor(v_tex.y * texdim.y) + 0.5) / texdim.y));
}
#endif
#ifdef ps_filter_lottes
#define MaskingType 4 //[1|2|3|4] The type of CRT shadow masking used. 1: compressed TV style, 2: Aperture-grille, 3: Stretched VGA style, 4: VGA style.
#define ScanBrightness -8.00 //[-16.0 to 1.0] The overall brightness of the scanline effect. Lower for darker, higher for brighter.
#define FilterCRTAmount -3.00 //[-4.0 to 1.0] The amount of filtering used, to replicate the TV CRT look. Lower for less, higher for more.
#define HorizontalWarp 0.00 //[0.0 to 0.1] The distortion warping effect for the horizontal (x) axis of the screen. Use small increments.
#define VerticalWarp 0.00 //[0.0 to 0.1] The distortion warping effect for the verticle (y) axis of the screen. Use small increments.
#define MaskAmountDark 0.50 //[0.0 to 1.0] The value of the dark masking line effect used. Lower for darker lower end masking, higher for brighter.
#define MaskAmountLight 1.50 //[0.0 to 2.0] The value of the light masking line effect used. Lower for darker higher end masking, higher for brighter.
#define BloomPixel -1.50 //[-2.0 -0.5] Pixel bloom radius. Higher for increased softness of bloom.
#define BloomScanLine -2.0 //[-4.0 -1.0] Scanline bloom radius. Higher for increased softness of bloom.
#define BloomAmount 0.15 //[0.0 1.0] Bloom intensity. Higher for brighter.
#define Shape 2.0 //[0.0 10.0] Kernal filter shape. Lower values will darken image and introduce moire patterns if used with curvature.
#define UseShadowMask 1 //[0 or 1] Enables, or disables the use of the CRT shadow mask. 0 is disabled, 1 is enabled.
float ToLinear1(float c)
{
return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);
}
vec3 ToLinear(vec3 c)
{
return vec3(ToLinear1(c.r), ToLinear1(c.g), ToLinear1(c.b));
}
float ToSrgb1(float c)
{
return c < 0.0031308 ? c * 12.92 : 1.055 * pow(c, 0.41666) - 0.055;
}
vec3 ToSrgb(vec3 c)
{
return vec3(ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b));
}
vec3 Fetch(vec2 pos, vec2 off)
{
pos = (floor(pos * u_target_size + off) + vec2(0.5, 0.5)) / u_target_size;
if (max(abs(pos.x - 0.5), abs(pos.y - 0.5)) > 0.5)
{
return vec3(0.0, 0.0, 0.0);
}
else
{
return ToLinear(texture(samp0, pos.xy).rgb);
}
}
vec2 Dist(vec2 pos)
{
pos = pos * vec2(640, 480);
return -((pos - floor(pos)) - vec2(0.5, 0.5));
}
float Gaus(float pos, float scale)
{
return exp2(scale * pow(abs(pos), Shape));
}
vec3 Horz3(vec2 pos, float off)
{
vec3 b = Fetch(pos, vec2(-1.0, off));
vec3 c = Fetch(pos, vec2(0.0, off));
vec3 d = Fetch(pos, vec2(1.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = FilterCRTAmount;
float wb = Gaus(dst - 1.0, scale);
float wc = Gaus(dst + 0.0, scale);
float wd = Gaus(dst + 1.0, scale);
return (b * wb + c * wc + d * wd) / (wb + wc + wd);
}
vec3 Horz5(vec2 pos, float off)
{
vec3 a = Fetch(pos, vec2(-2.0, off));
vec3 b = Fetch(pos, vec2(-1.0, off));
vec3 c = Fetch(pos, vec2(0.0, off));
vec3 d = Fetch(pos, vec2(1.0, off));
vec3 e = Fetch(pos, vec2(2.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = FilterCRTAmount;
float wa = Gaus(dst - 2.0, scale);
float wb = Gaus(dst - 1.0, scale);
float wc = Gaus(dst + 0.0, scale);
float wd = Gaus(dst + 1.0, scale);
float we = Gaus(dst + 2.0, scale);
return (a * wa + b * wb + c * wc + d * wd + e * we) / (wa + wb + wc + wd + we);
}
vec3 Horz7(vec2 pos, float off)
{
vec3 a = Fetch(pos, vec2(-3.0, off));
vec3 b = Fetch(pos, vec2(-2.0, off));
vec3 c = Fetch(pos, vec2(-1.0, off));
vec3 d = Fetch(pos, vec2( 0.0, off));
vec3 e = Fetch(pos, vec2( 1.0, off));
vec3 f = Fetch(pos, vec2( 2.0, off));
vec3 g = Fetch(pos, vec2( 3.0, off));
float dst = Dist(pos).x;
// Convert distance to weight.
float scale = BloomPixel;
float wa = Gaus(dst - 3.0, scale);
float wb = Gaus(dst - 2.0, scale);
float wc = Gaus(dst - 1.0, scale);
float wd = Gaus(dst + 0.0, scale);
float we = Gaus(dst + 1.0, scale);
float wf = Gaus(dst + 2.0, scale);
float wg = Gaus(dst + 3.0, scale);
// Return filtered sample.
return (a * wa + b * wb + c * wc + d * wd + e * we + f * wf + g * wg) / (wa + wb + wc + wd + we + wf + wg);
}
// Return scanline weight.
float Scan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, ScanBrightness);
}
float BloomScan(vec2 pos, float off)
{
float dst = Dist(pos).y;
return Gaus(dst + off, BloomScanLine);
}
vec3 Tri(vec2 pos)
{
vec3 a = Horz3(pos, -1.0);
vec3 b = Horz5(pos, 0.0);
vec3 c = Horz3(pos, 1.0);
float wa = Scan(pos, -1.0);
float wb = Scan(pos, 0.0);
float wc = Scan(pos, 1.0);
return (a * wa) + (b * wb) + (c * wc);
}
vec3 Bloom(vec2 pos)
{
vec3 a = Horz5(pos,-2.0);
vec3 b = Horz7(pos,-1.0);
vec3 c = Horz7(pos, 0.0);
vec3 d = Horz7(pos, 1.0);
vec3 e = Horz5(pos, 2.0);
float wa = BloomScan(pos,-2.0);
float wb = BloomScan(pos,-1.0);
float wc = BloomScan(pos, 0.0);
float wd = BloomScan(pos, 1.0);
float we = BloomScan(pos, 2.0);
return a * wa + b * wb + c * wc + d * wd + e * we;
}
vec2 Warp(vec2 pos)
{
pos = pos * 2.0 - 1.0;
pos *= vec2(1.0 + (pos.y * pos.y) * HorizontalWarp, 1.0 + (pos.x * pos.x) * VerticalWarp);
return pos * 0.5 + 0.5;
}
vec3 Mask(vec2 pos)
{
#if MaskingType == 1
// Very compressed TV style shadow mask.
float lines = MaskAmountLight;
float odd = 0.0;
if (fract(pos.x / 6.0) < 0.5)
{
odd = 1.0;
}
if (fract((pos.y + odd) / 2.0) < 0.5)
{
lines = MaskAmountDark;
}
pos.x = fract(pos.x / 3.0);
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
mask *= lines;
return mask;
#elif MaskingType == 2
// Aperture-grille.
pos.x = fract(pos.x / 3.0);
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#elif MaskingType == 3
// Stretched VGA style shadow mask (same as prior shaders).
pos.x += pos.y * 3.0;
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
pos.x = fract(pos.x / 6.0);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#else
// VGA style shadow mask.
pos.xy = floor(pos.xy * vec2(1.0, 0.5));
pos.x += pos.y * 3.0;
vec3 mask = vec3(MaskAmountDark, MaskAmountDark, MaskAmountDark);
pos.x = fract(pos.x / 6.0);
if (pos.x < 0.333)
{
mask.r = MaskAmountLight;
}
else if (pos.x < 0.666)
{
mask.g = MaskAmountLight;
}
else
{
mask.b = MaskAmountLight;
}
return mask;
#endif
}
vec4 LottesCRTPass()
{
vec4 color;
vec4 fragcoord = gl_FragCoord - u_target_rect;
vec2 inSize = u_target_resolution - (2 * u_target_rect.xy);
vec2 pos = Warp(fragcoord.xy / inSize);
color.rgb = Tri(pos);
color.rgb += Bloom(pos) * BloomAmount;
#if UseShadowMask
color.rgb *= Mask(fragcoord.xy);
#endif
color.rgb = ToSrgb(color.rgb);
return color;
}
void ps_filter_lottes()
{
o_col0 = LottesCRTPass();
}
#endif
#ifdef ps_4x_rgss
void ps_4x_rgss()
{
vec2 dxy = vec2(dFdx(v_tex.x), dFdy(v_tex.y));
vec3 color = vec3(0);
float s = 1.0/8.0;
float l = 3.0/8.0;
color += sample_c(v_tex + vec2( s, l) * dxy).rgb;
color += sample_c(v_tex + vec2( l,-s) * dxy).rgb;
color += sample_c(v_tex + vec2(-s,-l) * dxy).rgb;
color += sample_c(v_tex + vec2(-l, s) * dxy).rgb;
o_col0 = vec4(color * 0.25,1);
}
#endif
#ifdef ps_automagical_supersampling
void ps_automagical_supersampling()
{
vec2 ratio = (u_source_size / u_target_size) * 0.5;
vec2 steps = floor(ratio);
vec3 col = sample_c(v_tex).rgb;
float div = 1;
for (float y = 0; y < steps.y; y++)
{
for (float x = 0; x < steps.x; x++)
{
vec2 offset = vec2(x,y) - ratio * 0.5;
col += sample_c(v_tex + offset * u_rcp_source_resolution * 2.0).rgb;
div++;
}
}
o_col0 = vec4(col / div, 1);
}
#endif
#endif
@@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: 2002-2025 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
//#version 420 // Keep it for editor detection
#ifdef VERTEX_SHADER
layout(location = 0) in vec4 a_pos;
layout(location = 1) in vec2 a_tex;
layout(location = 0) out vec2 v_tex;
void main()
{
gl_Position = vec4(a_pos.x, -a_pos.y, a_pos.z, a_pos.w);
v_tex = a_tex;
}
#endif
/*
** Contrast, saturation, brightness
** Code of this function is from TGM's shader pack
** http://irrlicht.sourceforge.net/phpBB2/viewtopic.php?t=21057
** TGM's author comment about the license (included in the previous link)
** "do with it, what you want! its total free!
** (but would be nice, if you say that you used my shaders :wink: ) but not necessary"
*/
#ifdef FRAGMENT_SHADER
layout(push_constant) uniform cb0
{
vec4 params;
};
layout(set = 0, binding = 0) uniform sampler2D samp0;
layout(location = 0) in vec2 v_tex;
layout(location = 0) out vec4 o_col0;
// For all settings: 1.0 = 100% 0.5=50% 1.5 = 150%
vec4 ContrastSaturationBrightness(vec4 color)
{
float brt = params.x;
float con = params.y;
float sat = params.z;
// Increase or decrease these values to adjust r, g and b color channels separately
const float AvgLumR = 0.5;
const float AvgLumG = 0.5;
const float AvgLumB = 0.5;
const vec3 LumCoeff = vec3(0.2125, 0.7154, 0.0721);
vec3 AvgLumin = vec3(AvgLumR, AvgLumG, AvgLumB);
vec3 brtColor = color.rgb * brt;
float dot_intensity = dot(brtColor, LumCoeff);
vec3 intensity = vec3(dot_intensity, dot_intensity, dot_intensity);
vec3 satColor = mix(intensity, brtColor, sat);
vec3 conColor = mix(AvgLumin, satColor, con);
color.rgb = conColor;
return color;
}
void main()
{
vec4 c = texture(samp0, v_tex);
o_col0 = ContrastSaturationBrightness(c);
}
#endif
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+626 -3
View File
@@ -1,8 +1,8 @@
{
"schema_version": 1,
"generated_at": "2026-08-11T07:40:19Z",
"total_files": 8321,
"total_size": 10455720343,
"generated_at": "2026-08-11T07:56:05Z",
"total_files": 8357,
"total_size": 10466186980,
"files": {
"520d3d1b5897800af47f92efd2444a26b7a7dead": {
"path": "bios/3DO Company/3DO/3do_arcade_saot.bin",
@@ -48247,6 +48247,146 @@
"crc32": "6e8b2b47",
"adler32": "f5bc538e"
},
"b478ce9d1343b8dd201c045f010bd1b64ba158ae": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/opengl/cas.glsl",
"name": "cas.glsl",
"size": 2413,
"sha1": "b478ce9d1343b8dd201c045f010bd1b64ba158ae",
"md5": "2c928cbd0410a7e7edae6b2808a1f566",
"sha256": "3d5b9cef750ea8bedceef43c4c52215e8280abb81b5b8fa002007bb3f97f0506",
"crc32": "ce9e57e4",
"adler32": "6031f981"
},
"32777e258030ad9559864d9f4273d515d65f728f": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/opengl/convert.glsl",
"name": "convert.glsl",
"size": 15093,
"sha1": "32777e258030ad9559864d9f4273d515d65f728f",
"md5": "4791fe606bfd87d5afe710c5b4c7bbfb",
"sha256": "4ac385f0e38ee2c3a3f7014db86bd59927a5d4e4f76f24df9b8f8b3d97142390",
"crc32": "38998f1a",
"adler32": "1c0d032f"
},
"04bfcabf518320593dcda8198475c7f39295ef1f": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/opengl/imgui.glsl",
"name": "imgui.glsl",
"size": 637,
"sha1": "04bfcabf518320593dcda8198475c7f39295ef1f",
"md5": "fff1d8c84afbf21c47baaa919e1eef95",
"sha256": "25978439553bbd959fe4eb5d03b739a3d697348458a7bac3df48556148f48027",
"crc32": "b9848dc2",
"adler32": "a7d8c97b"
},
"d6209a8b3c4681823b1f0b7d0648c819ec72e2ad": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/opengl/interlace.glsl",
"name": "interlace.glsl",
"size": 6946,
"sha1": "d6209a8b3c4681823b1f0b7d0648c819ec72e2ad",
"md5": "bd4ca766cc7abf55f37336234248a6fb",
"sha256": "ff7e27fa41f33ec5d4a86b2a48a622dbcd8dd0ae37f8afe306fdd182a7aa8ec1",
"crc32": "43dd2e9e",
"adler32": "e2a83de3"
},
"7f476bffdc19d7ba2806eef40c2e8e40bdf0896c": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/opengl/merge.glsl",
"name": "merge.glsl",
"size": 579,
"sha1": "7f476bffdc19d7ba2806eef40c2e8e40bdf0896c",
"md5": "8ffd758adde9783ba80625a25b06ef7a",
"sha256": "6cd2bc21890426b5430d44b28a89289bcc6fc9ecd632bb3a8eafb6c8b818499c",
"crc32": "f1b5eedb",
"adler32": "a37cb550"
},
"ca7a974714111895bbf03360078ea1008694c52e": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/opengl/present.glsl",
"name": "present.glsl",
"size": 11730,
"sha1": "ca7a974714111895bbf03360078ea1008694c52e",
"md5": "ddb9dab93906986ec0bb63c5aad0401e",
"sha256": "27ffa5fdf1634f7be2734d2eff2d9dce0cca71c2832685e0a682388731dc5c60",
"crc32": "7f2bb4e3",
"adler32": "28b5f85e"
},
"511dc6bb2f81d0ad61fe46c6e91dce8b344fd934": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/opengl/shadeboost.glsl",
"name": "shadeboost.glsl",
"size": 1623,
"sha1": "511dc6bb2f81d0ad61fe46c6e91dce8b344fd934",
"md5": "8e8ebd0758705f947ca6f8eba4baf8e6",
"sha256": "e7297a2e333bb1882bcf82d2a109a2ab033715cce1bba4934f42ec68370f0077",
"crc32": "30ec9e5f",
"adler32": "52b30c3f"
},
"005cecf1a5e20b05407d55d0aad22cf81ad66c31": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/vulkan/cas.glsl",
"name": "cas.glsl",
"size": 2637,
"sha1": "005cecf1a5e20b05407d55d0aad22cf81ad66c31",
"md5": "deb7394a31184990c78c9753a293ff60",
"sha256": "ea3f65c6d1b18abe23b42a3d9ddf48d787df287feef6cfac52ca6109d8852d97",
"crc32": "e00e7479",
"adler32": "59323e74"
},
"53ff273e335432fdbbff9da2762e229ec2bd5078": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/vulkan/convert.glsl",
"name": "convert.glsl",
"size": 14024,
"sha1": "53ff273e335432fdbbff9da2762e229ec2bd5078",
"md5": "383ccce840167fd0264e3e382a411694",
"sha256": "e8213fa871bebc8fc883be3bbccf111c8aec1f84063e05a24850efc735312e08",
"crc32": "df5810f9",
"adler32": "a26ab337"
},
"b8d20aa6884878ba72c080d3f2107e09606963d3": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/vulkan/imgui.glsl",
"name": "imgui.glsl",
"size": 791,
"sha1": "b8d20aa6884878ba72c080d3f2107e09606963d3",
"md5": "23b4ac68a9c4f01ce319d1bfd7ba6627",
"sha256": "372e33cccb520a987983dd897c51c6b7e609f16badd67d056a95799a7f768566",
"crc32": "1b5ae2e5",
"adler32": "638efe68"
},
"d5ba24ba836c418f7f2d9d8b44bd337baa9e45a3": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/vulkan/interlace.glsl",
"name": "interlace.glsl",
"size": 7289,
"sha1": "d5ba24ba836c418f7f2d9d8b44bd337baa9e45a3",
"md5": "6888a8e85b9c0a357580d4464ddd7d0d",
"sha256": "af535e4b871b188957a7169ed883f6f8a209f0908e5b7aff17755605c8d07857",
"crc32": "21dc5ffb",
"adler32": "43e8a7f9"
},
"e84aa516874350f414e95d3891ce480fe2958aae": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/vulkan/merge.glsl",
"name": "merge.glsl",
"size": 759,
"sha1": "e84aa516874350f414e95d3891ce480fe2958aae",
"md5": "d4933454a5637402889209af223d59fe",
"sha256": "3c0eb665db06c289841829a6aa4716e46ce5ae8e08005a82ad4f3020bd8406e7",
"crc32": "4d559151",
"adler32": "b899ea33"
},
"0e22e330e10f31d7e7d7db00966cd48d1c5c6dce": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/vulkan/present.glsl",
"name": "present.glsl",
"size": 10336,
"sha1": "0e22e330e10f31d7e7d7db00966cd48d1c5c6dce",
"md5": "dee17db223f997696ecd59fc2bac56cb",
"sha256": "88d9f9b08f3a62cfd77fdf366c2a1ecca7924c407a3eabb1c1df68cfca2ee2d1",
"crc32": "980f007e",
"adler32": "31fc126f"
},
"bef69d202c6e21f2ec0d6930e439a9498642ba6b": {
"path": "bios/Other/armsx2/pcsx2/resources/shaders/vulkan/shadeboost.glsl",
"name": "shadeboost.glsl",
"size": 1914,
"sha1": "bef69d202c6e21f2ec0d6930e439a9498642ba6b",
"md5": "8d55f559c3f1a35c3b8c66f1a57ca25b",
"sha256": "31ab20c5617e0bdd3e1afaf9cac2212586f94433f176ca30810a1fecf3f338e8",
"crc32": "e5a3fed2",
"adler32": "2af75de2"
},
"25e41433147face77af96fecac4519ed11ae8072": {
"path": "bios/Other/armsx2/placeholder.png",
"name": "placeholder.png",
@@ -49017,6 +49157,26 @@
"crc32": "bb56de24",
"adler32": "28b01f1d"
},
"28c7616cd02e4286f9b4c1c4a8b8850832b49fcb": {
"path": "bios/Other/colem/EOS.ROM",
"name": "EOS.ROM",
"size": 16384,
"sha1": "28c7616cd02e4286f9b4c1c4a8b8850832b49fcb",
"md5": "cf10b154f2e006a9740d7653cfc47cab",
"sha256": "a49928fbac56380917134b54961ece2e90073022f3e04a6023d26cdbbd9c3356",
"crc32": "ef6403c5",
"adler32": "2d380b09"
},
"d4aec4efe1431e56fe52d83baf9118542c525255": {
"path": "bios/Other/colem/WRITER.ROM",
"name": "WRITER.ROM",
"size": 32768,
"sha1": "d4aec4efe1431e56fe52d83baf9118542c525255",
"md5": "4fe4f6800076ea3d897d4285653447bd",
"sha256": "3b17255d2679d11f79e6cacda86de5f6bc9bb5cddf78cbddc651af03b237317d",
"crc32": "58d86a2a",
"adler32": "ba97647e"
},
"15d52de01320d9b954e03b4c2e71ec75fd301bbb": {
"path": "bios/Other/cpcemu/ROM/COPYMATE.ROM",
"name": "COPYMATE.ROM",
@@ -49097,6 +49257,26 @@
"crc32": "588b5540",
"adler32": "b1254be6"
},
"72ecfde5f58c5993765a6ef5b8866c4777cc4ade": {
"path": "bios/Other/cspect/enNextZX.rom",
"name": "enNextZX.rom",
"size": 65536,
"sha1": "72ecfde5f58c5993765a6ef5b8866c4777cc4ade",
"md5": "8535c40c6cce978623a3e06a38e3a0fd",
"sha256": "dd901acd306794ad34f3131ad1df54b6f4de5527ad1b2aa42f82e417f93f17a5",
"crc32": "6d9951f0",
"adler32": "d3b66ba9"
},
"4f05efec7af0829f43aa3cac18a30ae2a33ded88": {
"path": "bios/Other/cspect/enNxtmmc.rom",
"name": "enNxtmmc.rom",
"size": 8192,
"sha1": "4f05efec7af0829f43aa3cac18a30ae2a33ded88",
"md5": "907a03706a196b0da832ddab204264f2",
"sha256": "ded2038d78f57b4fd7e05df0330064f661c221f7b06d8cd2bddee1ff6062f019",
"crc32": "5a443736",
"adler32": "865d3c5e"
},
"e6c0050b3c67ee26198488fbbe4a5d019c15b628": {
"path": "bios/Other/dolphin/codehandler.bin",
"name": "codehandler.bin",
@@ -49277,6 +49457,156 @@
"crc32": "35ee85c3",
"adler32": "fa9adec5"
},
"3c6b523e5432a892a0c284d9ba2593919b0173b7": {
"path": "bios/Other/emucorev/psvita_games.db",
"name": "psvita_games.db",
"size": 3801088,
"sha1": "3c6b523e5432a892a0c284d9ba2593919b0173b7",
"md5": "229cf38617d6669286b5d1a48906aa53",
"sha256": "ca56a424e87cf4c257dcd3810567e62cb10767096b6a2378e69b07040c5fbc13",
"crc32": "69787db6",
"adler32": "53e718d5"
},
"0e2ee74d1f9ebc3a6faf9dbaebbacc2ff053f4df": {
"path": "bios/Other/emucorex/games.db",
"name": "games.db",
"size": 6025216,
"sha1": "0e2ee74d1f9ebc3a6faf9dbaebbacc2ff053f4df",
"md5": "4b945ee2b52c49c397d368dc21fa42dc",
"sha256": "86883a0199a129b900f4a5c7ade6245b7e9d1399ec3c08d1fbf1838c01722e8e",
"crc32": "c9a52a37",
"adler32": "6ce8f9bd"
},
"cb0d4276f9947762af0841a8af75efa6cbe9109c": {
"path": "bios/Other/emucorex/resources/shaders/opengl/cas.glsl",
"name": "cas.glsl",
"size": 2483,
"sha1": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
"md5": "b56eb507fac5ac64a7079514c02b8ca7",
"sha256": "53e5ddc6825ecde7b502acb5fae3a173ba6696ec5b8bb7445f378f7b0ad1beb8",
"crc32": "13eed5a5",
"adler32": "552611d7"
},
"35910b67070342cdd6ec4d68eb2e56b39d350543": {
"path": "bios/Other/emucorex/resources/shaders/opengl/convert.glsl",
"name": "convert.glsl",
"size": 16416,
"sha1": "35910b67070342cdd6ec4d68eb2e56b39d350543",
"md5": "b8f058ec01942f596c890a060a32f08b",
"sha256": "fcb651c2ff39102a33ece3716579e3aee7d8b92d4700e52b56b6cf14baa8b8d1",
"crc32": "bb25c312",
"adler32": "afe8a197"
},
"ca149058af89d8f612aaa529661e8267d9569875": {
"path": "bios/Other/emucorex/resources/shaders/opengl/imgui.glsl",
"name": "imgui.glsl",
"size": 637,
"sha1": "ca149058af89d8f612aaa529661e8267d9569875",
"md5": "9338b9a25372178b4ecbf0215697e9be",
"sha256": "ea82ce9731de4a0b5af3e66e47e048af569e0e98c5739b32f268fb54caa7603a",
"crc32": "342d0e13",
"adler32": "a57ec97a"
},
"be39a049af0b8ab0c2168ad6d8cf636e15ae0dd2": {
"path": "bios/Other/emucorex/resources/shaders/opengl/interlace.glsl",
"name": "interlace.glsl",
"size": 5983,
"sha1": "be39a049af0b8ab0c2168ad6d8cf636e15ae0dd2",
"md5": "9f46187238e838a602b358705a9b813b",
"sha256": "15a58d9f7442f24e86484f4a7f0c10570b434ef68e8fb2d5c38513c0016d1e64",
"crc32": "3198c556",
"adler32": "facf1933"
},
"c8eb9c1926baad8bbba25f97be54abae233fe715": {
"path": "bios/Other/emucorex/resources/shaders/opengl/merge.glsl",
"name": "merge.glsl",
"size": 579,
"sha1": "c8eb9c1926baad8bbba25f97be54abae233fe715",
"md5": "1cbf28d65b0d6fe727b571d3e46871b8",
"sha256": "654281fb002aa1a086ee979d694e9d6443864bdc8740d20188baf8e125e386fa",
"crc32": "ae991348",
"adler32": "a15cb54f"
},
"4abe45e59dc9286321a115c76c31cc7fe85e58d5": {
"path": "bios/Other/emucorex/resources/shaders/opengl/present.glsl",
"name": "present.glsl",
"size": 14894,
"sha1": "4abe45e59dc9286321a115c76c31cc7fe85e58d5",
"md5": "480b029f6c357fb683a6720e2b78d990",
"sha256": "e31c519e6b22683cebefc5c2358b2b7ddad10964a1e478fa3eba01f3ab5adb17",
"crc32": "67a7b03b",
"adler32": "c5f5ce78"
},
"03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564": {
"path": "bios/Other/emucorex/resources/shaders/opengl/shadeboost.glsl",
"name": "shadeboost.glsl",
"size": 1548,
"sha1": "03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
"md5": "23a5e91beb2072f48e1011e7a9b6e901",
"sha256": "15ff418a0fc877886015cc781c9c3b0810a870bee5803d76741bbcbcc540b9e5",
"crc32": "54c435f2",
"adler32": "c42ff6d7"
},
"705d20d39a910d58a070fb86780ac58c8007591c": {
"path": "bios/Other/emucorex/resources/shaders/vulkan/convert.glsl",
"name": "convert.glsl",
"size": 14941,
"sha1": "705d20d39a910d58a070fb86780ac58c8007591c",
"md5": "669f55ffa59d7b754ab11f96c42f206f",
"sha256": "ac3d88c299e304faf76cda2b305510d5a6c19c06319e53ee54fdc18b028a860f",
"crc32": "fd02bb00",
"adler32": "70d4d2c4"
},
"276d984150c2fd2fb5ba287f2e5ceb7262dc5a95": {
"path": "bios/Other/emucorex/resources/shaders/vulkan/imgui.glsl",
"name": "imgui.glsl",
"size": 791,
"sha1": "276d984150c2fd2fb5ba287f2e5ceb7262dc5a95",
"md5": "b79725bff4fecb48d68ad575e13950d1",
"sha256": "2d765ffb2bef04082138e29e8ba7f7cc1c788c6415226d1aab1b7eb5bbd0431f",
"crc32": "c9511683",
"adler32": "609afe67"
},
"268f43cd946eea999c62f63a24eb00cb2fdc342c": {
"path": "bios/Other/emucorex/resources/shaders/vulkan/interlace.glsl",
"name": "interlace.glsl",
"size": 6329,
"sha1": "268f43cd946eea999c62f63a24eb00cb2fdc342c",
"md5": "b3e6aa12e24e59cad71d1b7d851a2572",
"sha256": "710e9e43ba7115e80b3e34c18072d838ef59f08f096204a7c5c9bf8704047b7f",
"crc32": "3317d2fb",
"adler32": "80778129"
},
"808b7b46bfb033c7fe4d94d0dbf5a271de30dedc": {
"path": "bios/Other/emucorex/resources/shaders/vulkan/merge.glsl",
"name": "merge.glsl",
"size": 759,
"sha1": "808b7b46bfb033c7fe4d94d0dbf5a271de30dedc",
"md5": "8fbeeb5c09477bcaf614087003df61a4",
"sha256": "2e6883d4304c5de25c464a9fd97fcd2f642d8d2af2db9c2b13882e67f0f35b93",
"crc32": "51cef730",
"adler32": "b5c5ea32"
},
"a848aad1840c7d85bfcdd552521a7181b00331cc": {
"path": "bios/Other/emucorex/resources/shaders/vulkan/present.glsl",
"name": "present.glsl",
"size": 13440,
"sha1": "a848aad1840c7d85bfcdd552521a7181b00331cc",
"md5": "b0bdec53b1c50873ba45539565f1327c",
"sha256": "2db47ffca9510468e37717266838576f96dccd9ed6b7f00c0a721c9868338b19",
"crc32": "50fb46e4",
"adler32": "8296d1bd"
},
"9b2704655a893ff1675d28df4bd9ffc9e15d3dd5": {
"path": "bios/Other/emucorex/resources/shaders/vulkan/shadeboost.glsl",
"name": "shadeboost.glsl",
"size": 1831,
"sha1": "9b2704655a893ff1675d28df4bd9ffc9e15d3dd5",
"md5": "ec6b58f723279afe732b7f9660acf27d",
"sha256": "27ab08e76e08fa8a1023b076f3e6c23bc6841732658942f30cea634394f40729",
"crc32": "69dc25d7",
"adler32": "9f4b472e"
},
"84d422b53547271e3a07342704a05ef481db3f99": {
"path": "bios/Other/fbneo/136032.101.e3",
"name": "136032.101.e3",
@@ -49297,6 +49627,16 @@
"crc32": "ebf1e0ae",
"adler32": "2e4808ff"
},
"0a42a4816c89447b16e1f3245409591efea98a4a": {
"path": "bios/Other/fbneo/136032.103.f7",
"name": "136032.103.f7",
"size": 235,
"sha1": "0a42a4816c89447b16e1f3245409591efea98a4a",
"md5": "c4abdbe2a64c2ef06b6b659f32ea883f",
"sha256": "c770a3ae305c3420292516dbd86373f67798983c74754d2cc988de6ddf6ec51f",
"crc32": "92d6a0b4",
"adler32": "cc91d423"
},
"72ba464da01bd6d3a91b8d9997d5ac14b6f47aad": {
"path": "bios/Other/fbneo/136032.104.f5",
"name": "136032.104.f5",
@@ -49407,6 +49747,16 @@
"crc32": "18c0ff3e",
"adler32": "7e6df93f"
},
"8e14e85c645187504ec9d162b3ea614a0c421d32": {
"path": "bios/Other/mednafen/apple2e-enh-kb.rom",
"name": "apple2e-enh-kb.rom",
"size": 2048,
"sha1": "8e14e85c645187504ec9d162b3ea614a0c421d32",
"md5": "b956c537e7b6b85ffa5c3493b1490d8a",
"sha256": "a1989da84ea4381d309e7e08783771f884e913236b9bcc71c3d649aacf76537a",
"crc32": "c506efb9",
"adler32": "87aaa299"
},
"58807f32c413a0c9db6deb0365f2fac9518e7c86": {
"path": "bios/Other/opentyrian/cubetxt1.dat",
"name": "cubetxt1.dat",
@@ -50147,6 +50497,16 @@
"crc32": "c6bebfc6",
"adler32": "04deae73"
},
"8a799c6615702aac72016b784e30ea1abd3e197b": {
"path": "bios/Other/prboom-plus/prboom-plus.wad",
"name": "prboom-plus.wad",
"size": 357768,
"sha1": "8a799c6615702aac72016b784e30ea1abd3e197b",
"md5": "1164ae723c9181698cc41dde6a341132",
"sha256": "29e6e18023cf39319f9984107f5f0f796b2593b1c3a55fc79adf3ae12498a6ce",
"crc32": "a4626dfa",
"adler32": "e0b1fb08"
},
"0f25848f73705328ca03b02905e95e3a5d087d8c": {
"path": "bios/Other/punes/dip.cfg",
"name": "dip.cfg",
@@ -92788,6 +93148,20 @@
"95f294d24b5e97ceab96282f1284190f": "8dba66121f3bbc4b2bdf67722ef4c2b6b6b23ee6",
"d66b0bccd743bd11eaca55bcaec925da": "c54f04b63ac6f04e0a0944ef260b5497e4494dbd",
"6f47c5daf33a759df3395db8ebe62ed1": "7b0bb79d38c573648de218645c6fb16449e156e0",
"2c928cbd0410a7e7edae6b2808a1f566": "b478ce9d1343b8dd201c045f010bd1b64ba158ae",
"4791fe606bfd87d5afe710c5b4c7bbfb": "32777e258030ad9559864d9f4273d515d65f728f",
"fff1d8c84afbf21c47baaa919e1eef95": "04bfcabf518320593dcda8198475c7f39295ef1f",
"bd4ca766cc7abf55f37336234248a6fb": "d6209a8b3c4681823b1f0b7d0648c819ec72e2ad",
"8ffd758adde9783ba80625a25b06ef7a": "7f476bffdc19d7ba2806eef40c2e8e40bdf0896c",
"ddb9dab93906986ec0bb63c5aad0401e": "ca7a974714111895bbf03360078ea1008694c52e",
"8e8ebd0758705f947ca6f8eba4baf8e6": "511dc6bb2f81d0ad61fe46c6e91dce8b344fd934",
"deb7394a31184990c78c9753a293ff60": "005cecf1a5e20b05407d55d0aad22cf81ad66c31",
"383ccce840167fd0264e3e382a411694": "53ff273e335432fdbbff9da2762e229ec2bd5078",
"23b4ac68a9c4f01ce319d1bfd7ba6627": "b8d20aa6884878ba72c080d3f2107e09606963d3",
"6888a8e85b9c0a357580d4464ddd7d0d": "d5ba24ba836c418f7f2d9d8b44bd337baa9e45a3",
"d4933454a5637402889209af223d59fe": "e84aa516874350f414e95d3891ce480fe2958aae",
"dee17db223f997696ecd59fc2bac56cb": "0e22e330e10f31d7e7d7db00966cd48d1c5c6dce",
"8d55f559c3f1a35c3b8c66f1a57ca25b": "bef69d202c6e21f2ec0d6930e439a9498642ba6b",
"ba3669cafd8b5036f15b1e682ef8c94a": "25e41433147face77af96fecac4519ed11ae8072",
"99a84ce95581f49e5f38075648884ede": "84b000485929698392676e8e7915bfcce15291ee",
"628d6b1c531d208721e104343dfbc607": "374ee91aeabee48013d2f090571752ec5081356f",
@@ -92865,6 +93239,8 @@
"8b85ec04bbc2ecb403e2bca380dbbb08": "c6578caffb1a57f6c18979dc596f3fb9cb986706",
"acb87b8d453644b45c7fc8c8247744f5": "1fbd073bbd6cb906a0c0678767fab81feb552173",
"b15266f06fa290f3f444302a74890d9b": "62a877bfffb3e690044520f52120c5cd48b97545",
"cf10b154f2e006a9740d7653cfc47cab": "28c7616cd02e4286f9b4c1c4a8b8850832b49fcb",
"4fe4f6800076ea3d897d4285653447bd": "d4aec4efe1431e56fe52d83baf9118542c525255",
"e9c4288eab8885a1e6ae7504a4736115": "15d52de01320d9b954e03b4c2e71ec75fd301bbb",
"aa2fb251814bd971b2b9874ab0971b3f": "0e247123b9d3204784eeadb4629b9d35b9dfd984",
"ffcbcbce32223a152753d30766ad6215": "582dd1706391f6f0161ad3486ae75a04fb0f7d5b",
@@ -92873,6 +93249,8 @@
"0246740371010333385665dfeb7cd0ec": "1acd01c2c03424a78b32c9440f4d890fb1104815",
"5ee871d5600eb9b5a22989c295f902a5": "200d59076dfef36db061d6d7d21d80021cab1237",
"8ca256a832e83c875f5432b3dee8a36f": "6765a91a42fed68a807325bf62a728e5ac5d622f",
"8535c40c6cce978623a3e06a38e3a0fd": "72ecfde5f58c5993765a6ef5b8866c4777cc4ade",
"907a03706a196b0da832ddab204264f2": "4f05efec7af0829f43aa3cac18a30ae2a33ded88",
"1f118b8735fcf40c20b38c3184eebfd9": "e6c0050b3c67ee26198488fbbe4a5d019c15b628",
"a1d3c0e5a40f163f4fb00a5e0774c63c": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b",
"9bef639b7426ef6948602a12b33522a8": "483a17ff25b68a8f18cae691da99f16beefbe370",
@@ -92891,8 +93269,24 @@
"5b5dd2bc68764c7204dc4fe69118f2a4": "2a15dfa682df1ff841e2092348aa6a2460e40810",
"4e10a27e6b3ceac0b3e2df4fadc48bb4": "723b0c61428321b0aca4c43fcd91406807d4f815",
"77b5b2d0c1c3a4200a9cda047f74f957": "fefcb49cb13ea191d2c87b406c141a957020432c",
"229cf38617d6669286b5d1a48906aa53": "3c6b523e5432a892a0c284d9ba2593919b0173b7",
"4b945ee2b52c49c397d368dc21fa42dc": "0e2ee74d1f9ebc3a6faf9dbaebbacc2ff053f4df",
"b56eb507fac5ac64a7079514c02b8ca7": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
"b8f058ec01942f596c890a060a32f08b": "35910b67070342cdd6ec4d68eb2e56b39d350543",
"9338b9a25372178b4ecbf0215697e9be": "ca149058af89d8f612aaa529661e8267d9569875",
"9f46187238e838a602b358705a9b813b": "be39a049af0b8ab0c2168ad6d8cf636e15ae0dd2",
"1cbf28d65b0d6fe727b571d3e46871b8": "c8eb9c1926baad8bbba25f97be54abae233fe715",
"480b029f6c357fb683a6720e2b78d990": "4abe45e59dc9286321a115c76c31cc7fe85e58d5",
"23a5e91beb2072f48e1011e7a9b6e901": "03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
"669f55ffa59d7b754ab11f96c42f206f": "705d20d39a910d58a070fb86780ac58c8007591c",
"b79725bff4fecb48d68ad575e13950d1": "276d984150c2fd2fb5ba287f2e5ceb7262dc5a95",
"b3e6aa12e24e59cad71d1b7d851a2572": "268f43cd946eea999c62f63a24eb00cb2fdc342c",
"8fbeeb5c09477bcaf614087003df61a4": "808b7b46bfb033c7fe4d94d0dbf5a271de30dedc",
"b0bdec53b1c50873ba45539565f1327c": "a848aad1840c7d85bfcdd552521a7181b00331cc",
"ec6b58f723279afe732b7f9660acf27d": "9b2704655a893ff1675d28df4bd9ffc9e15d3dd5",
"dc1aa189468255e644e44b99ad7da49c": "84d422b53547271e3a07342704a05ef481db3f99",
"fc90330915e7c494898615eb58689d68": "2d327e78832edd67ca3909c25b8c8c839637a1ed",
"c4abdbe2a64c2ef06b6b659f32ea883f": "0a42a4816c89447b16e1f3245409591efea98a4a",
"366fba6185c7abb4431ae7e8a1041a08": "72ba464da01bd6d3a91b8d9997d5ac14b6f47aad",
"cf01b762bcc96591dbd27c236cc3b1bd": "85febd34bcb166fc3326b5e5e4e80e157b660a08",
"befc5553a0a88cb989668b42d83418d7": "cbcd424510435a04e9041967a13781fd19b0f2c4",
@@ -92904,6 +93298,7 @@
"cd43b9f8ec8c742fca150f9666da47a0": "f72ebcb3ad771321e36cacb49779dca6f2b4db9e",
"034527d42a96d0f39977cb85c6a8f2d8": "fcfbbb25e06f58d13e762b02bfd468c834d98fc8",
"ee44560f34033efa3bb47b3e0aac1772": "7b03befac4fbcef8c649048fa3ff5d095fd48dd3",
"b956c537e7b6b85ffa5c3493b1490d8a": "8e14e85c645187504ec9d162b3ea614a0c421d32",
"3f2476d9ff29a06cf9fb85d52302222a": "58807f32c413a0c9db6deb0365f2fac9518e7c86",
"2b199deeccab1042fb02b46c47c2a8d0": "ee7dc2eb87e5a75564382efcd95d13554d3835ba",
"4229d5532a206aabe7e1fa11a7d8447e": "1ee93623406dc61b50ccb41a97bae769ea15cef2",
@@ -92978,6 +93373,7 @@
"7844c637dc246ec5f0e760ea33475073": "2f5ac8921b7cba56b02122ef772f5f11bbf6d8a2",
"37ce488ec574ba351c2dcf90a13d8fed": "b624812e3aa0ef2f75847f47907296e1f370cecd",
"03336a2165522a9493190e3873beebbd": "f99a13464a4dae347628f0ebb320e767e48e3a96",
"1164ae723c9181698cc41dde6a341132": "8a799c6615702aac72016b784e30ea1abd3e197b",
"05e1d1a0c8ecd45537a1d52ab925d110": "0f25848f73705328ca03b02905e95e3a5d087d8c",
"18207ca5b15ba7d05742dca8399d30c6": "8ed9fb83d8e4d73217d347e3b7965cb640cb8395",
"df976d4c51ac403796e2f51643617f92": "5109af30336443dd3107190b08dfbe8de623445b",
@@ -109507,6 +109903,47 @@
"no-save.png": [
"7b0bb79d38c573648de218645c6fb16449e156e0"
],
"cas.glsl": [
"b478ce9d1343b8dd201c045f010bd1b64ba158ae",
"005cecf1a5e20b05407d55d0aad22cf81ad66c31",
"cb0d4276f9947762af0841a8af75efa6cbe9109c"
],
"convert.glsl": [
"32777e258030ad9559864d9f4273d515d65f728f",
"53ff273e335432fdbbff9da2762e229ec2bd5078",
"35910b67070342cdd6ec4d68eb2e56b39d350543",
"705d20d39a910d58a070fb86780ac58c8007591c"
],
"imgui.glsl": [
"04bfcabf518320593dcda8198475c7f39295ef1f",
"b8d20aa6884878ba72c080d3f2107e09606963d3",
"ca149058af89d8f612aaa529661e8267d9569875",
"276d984150c2fd2fb5ba287f2e5ceb7262dc5a95"
],
"interlace.glsl": [
"d6209a8b3c4681823b1f0b7d0648c819ec72e2ad",
"d5ba24ba836c418f7f2d9d8b44bd337baa9e45a3",
"be39a049af0b8ab0c2168ad6d8cf636e15ae0dd2",
"268f43cd946eea999c62f63a24eb00cb2fdc342c"
],
"merge.glsl": [
"7f476bffdc19d7ba2806eef40c2e8e40bdf0896c",
"e84aa516874350f414e95d3891ce480fe2958aae",
"c8eb9c1926baad8bbba25f97be54abae233fe715",
"808b7b46bfb033c7fe4d94d0dbf5a271de30dedc"
],
"present.glsl": [
"ca7a974714111895bbf03360078ea1008694c52e",
"0e22e330e10f31d7e7d7db00966cd48d1c5c6dce",
"4abe45e59dc9286321a115c76c31cc7fe85e58d5",
"a848aad1840c7d85bfcdd552521a7181b00331cc"
],
"shadeboost.glsl": [
"511dc6bb2f81d0ad61fe46c6e91dce8b344fd934",
"bef69d202c6e21f2ec0d6930e439a9498642ba6b",
"03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
"9b2704655a893ff1675d28df4bd9ffc9e15d3dd5"
],
"placeholder.png": [
"25e41433147face77af96fecac4519ed11ae8072"
],
@@ -109722,6 +110159,12 @@
"cbf_zh-Hant-TW.bcfnt": [
"62a877bfffb3e690044520f52120c5cd48b97545"
],
"EOS.ROM": [
"28c7616cd02e4286f9b4c1c4a8b8850832b49fcb"
],
"WRITER.ROM": [
"d4aec4efe1431e56fe52d83baf9118542c525255"
],
"COPYMATE.ROM": [
"15d52de01320d9b954e03b4c2e71ec75fd301bbb"
],
@@ -109746,6 +110189,12 @@
"CPC6128.ROM.SE": [
"6765a91a42fed68a807325bf62a728e5ac5d622f"
],
"enNextZX.rom": [
"72ecfde5f58c5993765a6ef5b8866c4777cc4ade"
],
"enNxtmmc.rom": [
"4f05efec7af0829f43aa3cac18a30ae2a33ded88"
],
"codehandler.bin": [
"e6c0050b3c67ee26198488fbbe4a5d019c15b628",
"63e0d5b58d41fccf0853e46e7ab2f25262d2db5d",
@@ -109799,12 +110248,21 @@
"wqy_12pt.bdf": [
"fefcb49cb13ea191d2c87b406c141a957020432c"
],
"psvita_games.db": [
"3c6b523e5432a892a0c284d9ba2593919b0173b7"
],
"games.db": [
"0e2ee74d1f9ebc3a6faf9dbaebbacc2ff053f4df"
],
"136032.101.e3": [
"84d422b53547271e3a07342704a05ef481db3f99"
],
"136032.102.e5": [
"2d327e78832edd67ca3909c25b8c8c839637a1ed"
],
"136032.103.f7": [
"0a42a4816c89447b16e1f3245409591efea98a4a"
],
"136032.104.f5": [
"72ba464da01bd6d3a91b8d9997d5ac14b6f47aad"
],
@@ -109835,6 +110293,9 @@
"Master.dsk": [
"7b03befac4fbcef8c649048fa3ff5d095fd48dd3"
],
"apple2e-enh-kb.rom": [
"8e14e85c645187504ec9d162b3ea614a0c421d32"
],
"cubetxt1.dat": [
"58807f32c413a0c9db6deb0365f2fac9518e7c86"
],
@@ -110028,6 +110489,9 @@
"ppge_atlas.meta": [
"f99a13464a4dae347628f0ebb320e767e48e3a96"
],
"prboom-plus.wad": [
"8a799c6615702aac72016b784e30ea1abd3e197b"
],
"dip.cfg": [
"0f25848f73705328ca03b02905e95e3a5d087d8c"
],
@@ -120282,6 +120746,9 @@
"136032.107.b2": [
"72ba464da01bd6d3a91b8d9997d5ac14b6f47aad"
],
"apple2e-video.rom": [
"7060de104046736529c1e8a687a0dd7b84f8c51b"
],
"prism_failsafe.rom": [
"d020440638aff4d39467128413ef795677be9c23"
],
@@ -127946,6 +128413,20 @@
"2c7a1b2a": "8dba66121f3bbc4b2bdf67722ef4c2b6b6b23ee6",
"d766f908": "c54f04b63ac6f04e0a0944ef260b5497e4494dbd",
"6e8b2b47": "7b0bb79d38c573648de218645c6fb16449e156e0",
"ce9e57e4": "b478ce9d1343b8dd201c045f010bd1b64ba158ae",
"38998f1a": "32777e258030ad9559864d9f4273d515d65f728f",
"b9848dc2": "04bfcabf518320593dcda8198475c7f39295ef1f",
"43dd2e9e": "d6209a8b3c4681823b1f0b7d0648c819ec72e2ad",
"f1b5eedb": "7f476bffdc19d7ba2806eef40c2e8e40bdf0896c",
"7f2bb4e3": "ca7a974714111895bbf03360078ea1008694c52e",
"30ec9e5f": "511dc6bb2f81d0ad61fe46c6e91dce8b344fd934",
"e00e7479": "005cecf1a5e20b05407d55d0aad22cf81ad66c31",
"df5810f9": "53ff273e335432fdbbff9da2762e229ec2bd5078",
"1b5ae2e5": "b8d20aa6884878ba72c080d3f2107e09606963d3",
"21dc5ffb": "d5ba24ba836c418f7f2d9d8b44bd337baa9e45a3",
"4d559151": "e84aa516874350f414e95d3891ce480fe2958aae",
"980f007e": "0e22e330e10f31d7e7d7db00966cd48d1c5c6dce",
"e5a3fed2": "bef69d202c6e21f2ec0d6930e439a9498642ba6b",
"9e4fd72a": "25e41433147face77af96fecac4519ed11ae8072",
"15304b4e": "84b000485929698392676e8e7915bfcce15291ee",
"156472ad": "374ee91aeabee48013d2f090571752ec5081356f",
@@ -128023,6 +128504,8 @@
"d60e70f2": "c6578caffb1a57f6c18979dc596f3fb9cb986706",
"90386ab3": "1fbd073bbd6cb906a0c0678767fab81feb552173",
"bb56de24": "62a877bfffb3e690044520f52120c5cd48b97545",
"ef6403c5": "28c7616cd02e4286f9b4c1c4a8b8850832b49fcb",
"58d86a2a": "d4aec4efe1431e56fe52d83baf9118542c525255",
"fc7f544e": "15d52de01320d9b954e03b4c2e71ec75fd301bbb",
"37f1bb31": "0e247123b9d3204784eeadb4629b9d35b9dfd984",
"6e28fa67": "582dd1706391f6f0161ad3486ae75a04fb0f7d5b",
@@ -128031,6 +128514,8 @@
"2fa2e7d6": "1acd01c2c03424a78b32c9440f4d890fb1104815",
"1574923b": "200d59076dfef36db061d6d7d21d80021cab1237",
"588b5540": "6765a91a42fed68a807325bf62a728e5ac5d622f",
"6d9951f0": "72ecfde5f58c5993765a6ef5b8866c4777cc4ade",
"5a443736": "4f05efec7af0829f43aa3cac18a30ae2a33ded88",
"ba8dfa56": "e6c0050b3c67ee26198488fbbe4a5d019c15b628",
"9ac7f0c7": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b",
"6fe1f772": "483a17ff25b68a8f18cae691da99f16beefbe370",
@@ -128049,8 +128534,24 @@
"b7b59533": "2a15dfa682df1ff841e2092348aa6a2460e40810",
"0a6dccec": "723b0c61428321b0aca4c43fcd91406807d4f815",
"35ee85c3": "fefcb49cb13ea191d2c87b406c141a957020432c",
"69787db6": "3c6b523e5432a892a0c284d9ba2593919b0173b7",
"c9a52a37": "0e2ee74d1f9ebc3a6faf9dbaebbacc2ff053f4df",
"13eed5a5": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
"bb25c312": "35910b67070342cdd6ec4d68eb2e56b39d350543",
"342d0e13": "ca149058af89d8f612aaa529661e8267d9569875",
"3198c556": "be39a049af0b8ab0c2168ad6d8cf636e15ae0dd2",
"ae991348": "c8eb9c1926baad8bbba25f97be54abae233fe715",
"67a7b03b": "4abe45e59dc9286321a115c76c31cc7fe85e58d5",
"54c435f2": "03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
"fd02bb00": "705d20d39a910d58a070fb86780ac58c8007591c",
"c9511683": "276d984150c2fd2fb5ba287f2e5ceb7262dc5a95",
"3317d2fb": "268f43cd946eea999c62f63a24eb00cb2fdc342c",
"51cef730": "808b7b46bfb033c7fe4d94d0dbf5a271de30dedc",
"50fb46e4": "a848aad1840c7d85bfcdd552521a7181b00331cc",
"69dc25d7": "9b2704655a893ff1675d28df4bd9ffc9e15d3dd5",
"7e84972a": "84d422b53547271e3a07342704a05ef481db3f99",
"ebf1e0ae": "2d327e78832edd67ca3909c25b8c8c839637a1ed",
"92d6a0b4": "0a42a4816c89447b16e1f3245409591efea98a4a",
"7a29dc07": "72ba464da01bd6d3a91b8d9997d5ac14b6f47aad",
"79021d3c": "85febd34bcb166fc3326b5e5e4e80e157b660a08",
"76ee86c4": "cbcd424510435a04e9041967a13781fd19b0f2c4",
@@ -128062,6 +128563,7 @@
"066d1348": "f72ebcb3ad771321e36cacb49779dca6f2b4db9e",
"8031ff02": "fcfbbb25e06f58d13e762b02bfd468c834d98fc8",
"18c0ff3e": "7b03befac4fbcef8c649048fa3ff5d095fd48dd3",
"c506efb9": "8e14e85c645187504ec9d162b3ea614a0c421d32",
"84e948df": "58807f32c413a0c9db6deb0365f2fac9518e7c86",
"bf8e13c9": "ee7dc2eb87e5a75564382efcd95d13554d3835ba",
"7b91c5d2": "1ee93623406dc61b50ccb41a97bae769ea15cef2",
@@ -128136,6 +128638,7 @@
"0e016ecf": "2f5ac8921b7cba56b02122ef772f5f11bbf6d8a2",
"eef26072": "b624812e3aa0ef2f75847f47907296e1f370cecd",
"c6bebfc6": "f99a13464a4dae347628f0ebb320e767e48e3a96",
"a4626dfa": "8a799c6615702aac72016b784e30ea1abd3e197b",
"84f47dc9": "0f25848f73705328ca03b02905e95e3a5d087d8c",
"79b7fdc4": "8ed9fb83d8e4d73217d347e3b7965cb640cb8395",
"be691779": "5109af30336443dd3107190b08dfbe8de623445b",
@@ -136269,6 +136772,20 @@
"c749cd9513bd345b9f12ff46eac0ffbf4e54c0b0884921df97ca8297396e3fea": "8dba66121f3bbc4b2bdf67722ef4c2b6b6b23ee6",
"dfd8035387a463722ad07af3c91934e0a685f2afa57e31d305f0602656d1edb1": "c54f04b63ac6f04e0a0944ef260b5497e4494dbd",
"ab829598e15b771112130cbafbd982736400690c11e3d7c81e3fc8c582c24f47": "7b0bb79d38c573648de218645c6fb16449e156e0",
"3d5b9cef750ea8bedceef43c4c52215e8280abb81b5b8fa002007bb3f97f0506": "b478ce9d1343b8dd201c045f010bd1b64ba158ae",
"4ac385f0e38ee2c3a3f7014db86bd59927a5d4e4f76f24df9b8f8b3d97142390": "32777e258030ad9559864d9f4273d515d65f728f",
"25978439553bbd959fe4eb5d03b739a3d697348458a7bac3df48556148f48027": "04bfcabf518320593dcda8198475c7f39295ef1f",
"ff7e27fa41f33ec5d4a86b2a48a622dbcd8dd0ae37f8afe306fdd182a7aa8ec1": "d6209a8b3c4681823b1f0b7d0648c819ec72e2ad",
"6cd2bc21890426b5430d44b28a89289bcc6fc9ecd632bb3a8eafb6c8b818499c": "7f476bffdc19d7ba2806eef40c2e8e40bdf0896c",
"27ffa5fdf1634f7be2734d2eff2d9dce0cca71c2832685e0a682388731dc5c60": "ca7a974714111895bbf03360078ea1008694c52e",
"e7297a2e333bb1882bcf82d2a109a2ab033715cce1bba4934f42ec68370f0077": "511dc6bb2f81d0ad61fe46c6e91dce8b344fd934",
"ea3f65c6d1b18abe23b42a3d9ddf48d787df287feef6cfac52ca6109d8852d97": "005cecf1a5e20b05407d55d0aad22cf81ad66c31",
"e8213fa871bebc8fc883be3bbccf111c8aec1f84063e05a24850efc735312e08": "53ff273e335432fdbbff9da2762e229ec2bd5078",
"372e33cccb520a987983dd897c51c6b7e609f16badd67d056a95799a7f768566": "b8d20aa6884878ba72c080d3f2107e09606963d3",
"af535e4b871b188957a7169ed883f6f8a209f0908e5b7aff17755605c8d07857": "d5ba24ba836c418f7f2d9d8b44bd337baa9e45a3",
"3c0eb665db06c289841829a6aa4716e46ce5ae8e08005a82ad4f3020bd8406e7": "e84aa516874350f414e95d3891ce480fe2958aae",
"88d9f9b08f3a62cfd77fdf366c2a1ecca7924c407a3eabb1c1df68cfca2ee2d1": "0e22e330e10f31d7e7d7db00966cd48d1c5c6dce",
"31ab20c5617e0bdd3e1afaf9cac2212586f94433f176ca30810a1fecf3f338e8": "bef69d202c6e21f2ec0d6930e439a9498642ba6b",
"0a42ffa04c5895647026f4cbbd30cae3e1455bd6e36d069ae83142e0c229c74b": "25e41433147face77af96fecac4519ed11ae8072",
"e740b08eb0f7bd074a0a7e6033f5ab9292fb601157e64aacbd9fec6174020d42": "84b000485929698392676e8e7915bfcce15291ee",
"92848f15d5eeea9fdfe87e272e4c9ea32f876b3665f5b27ef059711b7c757502": "374ee91aeabee48013d2f090571752ec5081356f",
@@ -136346,6 +136863,8 @@
"5e39d040be8e40554009b4ff29194f7d70c468a7c9c445c0be06320539851125": "c6578caffb1a57f6c18979dc596f3fb9cb986706",
"5d786f4ef7e59e4ee20c73803753e455ebf812046a930f5ad1437839329e8aca": "1fbd073bbd6cb906a0c0678767fab81feb552173",
"dbefcc65df7e7e301af3fead2282626574b67bc7d838f45a6c618b74793ac7bc": "62a877bfffb3e690044520f52120c5cd48b97545",
"a49928fbac56380917134b54961ece2e90073022f3e04a6023d26cdbbd9c3356": "28c7616cd02e4286f9b4c1c4a8b8850832b49fcb",
"3b17255d2679d11f79e6cacda86de5f6bc9bb5cddf78cbddc651af03b237317d": "d4aec4efe1431e56fe52d83baf9118542c525255",
"f68b6a88c51d63e77893d102453df550da96227eb192dd1b970ca3c3abc23b0c": "15d52de01320d9b954e03b4c2e71ec75fd301bbb",
"7d2c4d6e2421991742c1dc9d75afd22b7d450b9ca4852462610df5af85f6ab1c": "0e247123b9d3204784eeadb4629b9d35b9dfd984",
"3053eef5f6132188facb0c27cc67e0be2eeaebc1f9c8c10b3859731550dec5c3": "582dd1706391f6f0161ad3486ae75a04fb0f7d5b",
@@ -136354,6 +136873,8 @@
"49c5b2da99bf3230dec3e4bfbb136609ae5f8d250d8920be0ebda1e5a256f88a": "1acd01c2c03424a78b32c9440f4d890fb1104815",
"8ec2639be145d242d18c02f5ed9eaa52c7d4c40b49dc672755200c3be701abf7": "200d59076dfef36db061d6d7d21d80021cab1237",
"456ced147bf5a3a11fe70971f56c1a215e85808fa86dd7d8610a73b3e9b6148e": "6765a91a42fed68a807325bf62a728e5ac5d622f",
"dd901acd306794ad34f3131ad1df54b6f4de5527ad1b2aa42f82e417f93f17a5": "72ecfde5f58c5993765a6ef5b8866c4777cc4ade",
"ded2038d78f57b4fd7e05df0330064f661c221f7b06d8cd2bddee1ff6062f019": "4f05efec7af0829f43aa3cac18a30ae2a33ded88",
"2b2ff0716aeb9b806cc61181e559041312957f25f04c435bd7da80a78917b33b": "e6c0050b3c67ee26198488fbbe4a5d019c15b628",
"38f9f59d505bc4e2d86b0196706195f33ad72b7fe9d029cf263072cde19d044f": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b",
"4ad991be2b0aa305f09b90a79fc50f57833b30b008890b5ef1336cc3d9d0bae0": "483a17ff25b68a8f18cae691da99f16beefbe370",
@@ -136372,8 +136893,24 @@
"890991d60c3aeea43058d0c29dc72fcc428d2670e38c1d94d5d1bfae03437a8f": "2a15dfa682df1ff841e2092348aa6a2460e40810",
"af64766bf3df3336c76b04f2d07749812373790b9f20ed59c0faf7cc8a5241ad": "723b0c61428321b0aca4c43fcd91406807d4f815",
"ead9acd03c8d749ddde899afed49ba4b50e6ca14585a3091bc23a7074902cfe0": "fefcb49cb13ea191d2c87b406c141a957020432c",
"ca56a424e87cf4c257dcd3810567e62cb10767096b6a2378e69b07040c5fbc13": "3c6b523e5432a892a0c284d9ba2593919b0173b7",
"86883a0199a129b900f4a5c7ade6245b7e9d1399ec3c08d1fbf1838c01722e8e": "0e2ee74d1f9ebc3a6faf9dbaebbacc2ff053f4df",
"53e5ddc6825ecde7b502acb5fae3a173ba6696ec5b8bb7445f378f7b0ad1beb8": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
"fcb651c2ff39102a33ece3716579e3aee7d8b92d4700e52b56b6cf14baa8b8d1": "35910b67070342cdd6ec4d68eb2e56b39d350543",
"ea82ce9731de4a0b5af3e66e47e048af569e0e98c5739b32f268fb54caa7603a": "ca149058af89d8f612aaa529661e8267d9569875",
"15a58d9f7442f24e86484f4a7f0c10570b434ef68e8fb2d5c38513c0016d1e64": "be39a049af0b8ab0c2168ad6d8cf636e15ae0dd2",
"654281fb002aa1a086ee979d694e9d6443864bdc8740d20188baf8e125e386fa": "c8eb9c1926baad8bbba25f97be54abae233fe715",
"e31c519e6b22683cebefc5c2358b2b7ddad10964a1e478fa3eba01f3ab5adb17": "4abe45e59dc9286321a115c76c31cc7fe85e58d5",
"15ff418a0fc877886015cc781c9c3b0810a870bee5803d76741bbcbcc540b9e5": "03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564",
"ac3d88c299e304faf76cda2b305510d5a6c19c06319e53ee54fdc18b028a860f": "705d20d39a910d58a070fb86780ac58c8007591c",
"2d765ffb2bef04082138e29e8ba7f7cc1c788c6415226d1aab1b7eb5bbd0431f": "276d984150c2fd2fb5ba287f2e5ceb7262dc5a95",
"710e9e43ba7115e80b3e34c18072d838ef59f08f096204a7c5c9bf8704047b7f": "268f43cd946eea999c62f63a24eb00cb2fdc342c",
"2e6883d4304c5de25c464a9fd97fcd2f642d8d2af2db9c2b13882e67f0f35b93": "808b7b46bfb033c7fe4d94d0dbf5a271de30dedc",
"2db47ffca9510468e37717266838576f96dccd9ed6b7f00c0a721c9868338b19": "a848aad1840c7d85bfcdd552521a7181b00331cc",
"27ab08e76e08fa8a1023b076f3e6c23bc6841732658942f30cea634394f40729": "9b2704655a893ff1675d28df4bd9ffc9e15d3dd5",
"d7626affff7dfc0a8782e0894770258ea572d0f2d9859690efbc8ce018cb36bc": "84d422b53547271e3a07342704a05ef481db3f99",
"81fb611f7dc9d531720836f31e5aff1cc5466013d0afb7ab7f947cb5b7520504": "2d327e78832edd67ca3909c25b8c8c839637a1ed",
"c770a3ae305c3420292516dbd86373f67798983c74754d2cc988de6ddf6ec51f": "0a42a4816c89447b16e1f3245409591efea98a4a",
"e9c94dc577b050e856e1c7de90c4d034fa3f7eca7c4b887adb7b47115c466cfa": "72ba464da01bd6d3a91b8d9997d5ac14b6f47aad",
"3d5bd91c33ee983e795c81ab7683d7591e73ed2d9947c7e8cec3bded31b6b686": "85febd34bcb166fc3326b5e5e4e80e157b660a08",
"0a417e3f3bdd879ddddd22e93d0093b5d4810ce0e040b45447709ad98d0a6029": "cbcd424510435a04e9041967a13781fd19b0f2c4",
@@ -136385,6 +136922,7 @@
"c72a36bbd471daf5d12df9f7b85773771583ec960ff9ca17e66d2f7b22e5c8c3": "f72ebcb3ad771321e36cacb49779dca6f2b4db9e",
"fcf442b26c492253dfb8d2146e215b7b84b3ba87443face9c94a1e9474aa3ca4": "fcfbbb25e06f58d13e762b02bfd468c834d98fc8",
"0add32bcbd0e5b350437fa9d138b3c232368e36d8009852479fdb09d9db31113": "7b03befac4fbcef8c649048fa3ff5d095fd48dd3",
"a1989da84ea4381d309e7e08783771f884e913236b9bcc71c3d649aacf76537a": "8e14e85c645187504ec9d162b3ea614a0c421d32",
"2a0bc26ac4a8cdcbe7a145c9b1868a87ecb4335e9787f2f1e5713c965372df2b": "58807f32c413a0c9db6deb0365f2fac9518e7c86",
"6604a1dcc264e8b8e98f036a8bc35579e9b90b92eccbc4d7c1effee9785d1580": "ee7dc2eb87e5a75564382efcd95d13554d3835ba",
"fcfc9762cd4cfad42feb12d975929de86851b216d4491252c701527e22b55539": "1ee93623406dc61b50ccb41a97bae769ea15cef2",
@@ -136459,6 +136997,7 @@
"f68a569f71fc090cec61bc5f699d0553dca7c5d012166709e74fc6c72568fdc2": "2f5ac8921b7cba56b02122ef772f5f11bbf6d8a2",
"49f94f7bc9fc605cbf60457937429e3c8499e515d8164b7dd7cd0345844fc86a": "b624812e3aa0ef2f75847f47907296e1f370cecd",
"e3e8ab044b973137c9864f473f345849ecb4c0a9a3234eaba399dfa16c5f06f0": "f99a13464a4dae347628f0ebb320e767e48e3a96",
"29e6e18023cf39319f9984107f5f0f796b2593b1c3a55fc79adf3ae12498a6ce": "8a799c6615702aac72016b784e30ea1abd3e197b",
"0406c436e54064b938ffe855e1ad0abb362d67f009781a5dce79b044bcd68115": "0f25848f73705328ca03b02905e95e3a5d087d8c",
"a60757b92f2f3df9447aa50e004aabe9f4cb0845bb037afca118ff89b5913ae3": "8ed9fb83d8e4d73217d347e3b7965cb640cb8395",
"3aa539a472b4a7ffbf4329356d0e3c0b17893cedc6044408ca22988d7930ecc4": "5109af30336443dd3107190b08dfbe8de623445b",
@@ -145883,6 +146422,48 @@
"nxengine/tilekey.dat": [
"74c14b15dbc2f36c81d2ad9cb65e2893298415da"
],
"pcsx2/resources/shaders/opengl/cas.glsl": [
"b478ce9d1343b8dd201c045f010bd1b64ba158ae"
],
"pcsx2/resources/shaders/opengl/convert.glsl": [
"32777e258030ad9559864d9f4273d515d65f728f"
],
"pcsx2/resources/shaders/opengl/imgui.glsl": [
"04bfcabf518320593dcda8198475c7f39295ef1f"
],
"pcsx2/resources/shaders/opengl/interlace.glsl": [
"d6209a8b3c4681823b1f0b7d0648c819ec72e2ad"
],
"pcsx2/resources/shaders/opengl/merge.glsl": [
"7f476bffdc19d7ba2806eef40c2e8e40bdf0896c"
],
"pcsx2/resources/shaders/opengl/present.glsl": [
"ca7a974714111895bbf03360078ea1008694c52e"
],
"pcsx2/resources/shaders/opengl/shadeboost.glsl": [
"511dc6bb2f81d0ad61fe46c6e91dce8b344fd934"
],
"pcsx2/resources/shaders/vulkan/cas.glsl": [
"005cecf1a5e20b05407d55d0aad22cf81ad66c31"
],
"pcsx2/resources/shaders/vulkan/convert.glsl": [
"53ff273e335432fdbbff9da2762e229ec2bd5078"
],
"pcsx2/resources/shaders/vulkan/imgui.glsl": [
"b8d20aa6884878ba72c080d3f2107e09606963d3"
],
"pcsx2/resources/shaders/vulkan/interlace.glsl": [
"d5ba24ba836c418f7f2d9d8b44bd337baa9e45a3"
],
"pcsx2/resources/shaders/vulkan/merge.glsl": [
"e84aa516874350f414e95d3891ce480fe2958aae"
],
"pcsx2/resources/shaders/vulkan/present.glsl": [
"0e22e330e10f31d7e7d7db00966cd48d1c5c6dce"
],
"pcsx2/resources/shaders/vulkan/shadeboost.glsl": [
"bef69d202c6e21f2ec0d6930e439a9498642ba6b"
],
"BeebFile/6502Tube.rom": [
"05980c296e1a7800c4aa21d6bed16323b6d859d5"
],
@@ -146030,6 +146611,45 @@
"ROM/National/CPC6128.ROM.SE": [
"6765a91a42fed68a807325bf62a728e5ac5d622f"
],
"resources/shaders/opengl/cas.glsl": [
"cb0d4276f9947762af0841a8af75efa6cbe9109c"
],
"resources/shaders/opengl/convert.glsl": [
"35910b67070342cdd6ec4d68eb2e56b39d350543"
],
"resources/shaders/opengl/imgui.glsl": [
"ca149058af89d8f612aaa529661e8267d9569875"
],
"resources/shaders/opengl/interlace.glsl": [
"be39a049af0b8ab0c2168ad6d8cf636e15ae0dd2"
],
"resources/shaders/opengl/merge.glsl": [
"c8eb9c1926baad8bbba25f97be54abae233fe715"
],
"resources/shaders/opengl/present.glsl": [
"4abe45e59dc9286321a115c76c31cc7fe85e58d5"
],
"resources/shaders/opengl/shadeboost.glsl": [
"03c8b0d3ebeb0ecdc7f594ba6f7242a6f6e4d564"
],
"resources/shaders/vulkan/convert.glsl": [
"705d20d39a910d58a070fb86780ac58c8007591c"
],
"resources/shaders/vulkan/imgui.glsl": [
"276d984150c2fd2fb5ba287f2e5ceb7262dc5a95"
],
"resources/shaders/vulkan/interlace.glsl": [
"268f43cd946eea999c62f63a24eb00cb2fdc342c"
],
"resources/shaders/vulkan/merge.glsl": [
"808b7b46bfb033c7fe4d94d0dbf5a271de30dedc"
],
"resources/shaders/vulkan/present.glsl": [
"a848aad1840c7d85bfcdd552521a7181b00331cc"
],
"resources/shaders/vulkan/shadeboost.glsl": [
"9b2704655a893ff1675d28df4bd9ffc9e15d3dd5"
],
"roms/108281-001.bin": [
"d108bb99cb1a05d808e6e25cc720da24faee9e7c"
],
@@ -154942,6 +155562,9 @@
"SCPU64/pepto-pal.vpl": [
"88a2209694f64f7167f239646aedcd0d09dc51bb"
],
"resources/shaders/vulkan/cas.glsl": [
"005cecf1a5e20b05407d55d0aad22cf81ad66c31"
],
"roms/5429.vbi": [
"8060cd4b49052572bb5ec929f8ad21e2addce237"
],