mirror of
https://github.com/ARMSX2/ARMSX2.git
synced 2026-08-24 16:50:16 -07:00
Ported from EmuCoreX with sashkinbro's approval. The GPU family alone was never enough to decide behaviour: the same Mali part behaves differently under ARM's proprietary driver than under Mesa PanVK, a lesson this tree learned twice the expensive way - the r44p1 DEVICE_LOST fix had to be gated on driverID rather than vendorID, and the 8 Elite push-descriptor disable likewise. Driver identity, version and a bug set are now recorded in a table, so the next device quirk is an entry rather than another bespoke branch. Twenty-five rules, with match confidence so a vendor-wide rule never overrides a driver-version-specific one. The shaders gain gpu_bitwise_and / gpu_bitwise_not / gpu_boolean_not, whose bodies the device emits: plain a & b normally, scalarized where the database says the driver miscompiles vector bitwise ops. A device matching no rule renders exactly as before. opengl/tfx_fs.glsl already carried this fix as IAND3/UAND2/UAND4 macros gated on GPU_PROFILE_MALI. Those are replaced by the shared helpers, but the GL macro is deliberately (workaround || IsMaliGPUProfile()): Mali reached through ANGLE or Panfrost resolves a non-ARM driver and matches no rule, so a database-only gate would have silently removed a fix those users have today. Widening only. SHADER_CACHE_VERSION 108 -> 109. Every TFX and convert shader's source text changed, so a blob cached from 108 no longer matches the source that produced it; without the bump users would get stale binaries and garbage rendering after updating. RewriteConstantLoads is deliberately absent - the database records BrokenConstantLoad with no workaround bits, so the macro would be permanently zero and the shader code dead. Shaders validated offline with glslc across 136 Vulkan and 296 GL/GLES permutations, every macro on and off; the failure set is byte-identical to the pre-change baseline.
668 lines
15 KiB
GLSL
668 lines
15 KiB
GLSL
// 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
|