rpcs3/rpcs3/Emu/RSX/Program/GLSLSnippets/OverlayRenderFS.glsl

223 lines
6.4 KiB
GLSL

R"(
#version 420
#extension GL_ARB_separate_shader_objects : enable
%preprocessor
#ifdef VULKAN
#define USE_UBO 1
#endif
#define SAMPLER_MODE_NONE 0
#define SAMPLER_MODE_FONT2D 1
#define SAMPLER_MODE_FONT3D 2
#define SAMPLER_MODE_TEXTURE2D 3
#define SDF_DISABLED 0
#define SDF_ELLIPSE 1
#define SDF_BOX 2
#define SDF_ROUND_BOX 3
#ifdef VULKAN
layout(set=0, binding=0) uniform sampler2D fs0;
layout(set=0, binding=1) uniform sampler2DArray fs1;
#else
layout(binding=31) uniform sampler2D fs0;
layout(binding=30) uniform sampler2DArray fs1;
#endif
layout(location=0) in vec2 tc0;
layout(location=1) in vec4 color;
layout(location=2) in vec4 clip_rect;
layout(location=0) out vec4 ocol;
#if USE_UBO
layout(%push_block) uniform FragmentConfiguration
{
%push_block_offset
uint fragment_config;
float timestamp;
float blur_intensity;
vec4 sdf_params;
vec4 sdf_origin;
vec4 sdf_border_color;
};
#else
uniform uint fragment_config;
uniform float timestamp;
uniform float blur_intensity;
uniform vec4 sdf_params;
uniform vec2 sdf_origin;
uniform vec4 sdf_border_color;
#endif
struct config_t
{
bool clip_fragments;
bool use_pulse_glow;
uint sampler_mode;
uint sdf;
};
config_t unpack_fragment_options()
{
config_t result;
result.clip_fragments = bitfieldExtract(fragment_config, 0, 1) != 0;
result.use_pulse_glow = bitfieldExtract(fragment_config, 1, 1) != 0;
result.sampler_mode = bitfieldExtract(fragment_config, 2, 2);
result.sdf = bitfieldExtract(fragment_config, 4, 2);
return result;
}
vec4 SDF_blend(
const in float sd,
const in float border_width,
const in vec4 inner_color,
const in vec4 border_color,
const in vec4 outer_color)
{
// Crucially, we need to get the derivative without subracting the border width.
// Subtracting the border width makes the function non-continuous and makes the jaggies hard to get rid of.
float fw = fwidth(sd);
// Compute the two transition points. The inner edge is of course biased by the border amount as the clamping point
// Treat smoothstep as fancy clamp where e0 < x < e1
float a = smoothstep(-border_width + fw, -border_width - fw, sd); // inner edge transition
float b = smoothstep(fw, -fw, sd); // outer edge transition
// Mix the 3 colors with the transition values.
vec4 color = mix(outer_color, border_color, b);
color = mix(color, inner_color, a);
return color;
}
float SDF_fn(const in uint sdf)
{
const vec2 p = floor(gl_FragCoord.xy) - sdf_origin.xy; // Screen-spac distance
const vec2 hs = sdf_params.xy; // Half size
const float r = sdf_params.z; // Radius (for round box, ellipses use half size instead)
vec2 v; // Scratch
float d; // Distance calculated
switch (sdf)
{
case SDF_ELLIPSE:
// Slightly inaccurate hack, but good enough for classification and allows oval shapes
d = length(p / hs) - 1.f;
// Now we need to correct for the border because the circle was scaled down to a unit
return d * length(hs);
case SDF_BOX:
// Insanity, reduced junction of 3 functions
// If for each axis the axis-aligned distance = D then you can select/clamp each axis separately by doing a max(D, 0) on all dimensions
// Length then does the squareroot transformation.
// The second term is to add back the inner distance which is useful for rendering borders
v = abs(p) - hs;
return length(max(v, 0.f)) + min(max(v.x, v.y), 0.0);
case SDF_ROUND_BOX:
// Modified BOX SDF.
// The half box size is shrunk by R in it's diagonal, but we add radius back into the output to bias the output again
v = abs(p) - (hs - r);
return length(max(v, 0.f)) + min(max(v.x, v.y), 0.0) - r;
default:
return -1.f;
}
}
vec4 blur_sample(sampler2D tex, vec2 coord, vec2 tex_offset)
{
vec2 coords[9];
coords[0] = coord - tex_offset;
coords[1] = coord + vec2(0., -tex_offset.y);
coords[2] = coord + vec2(tex_offset.x, -tex_offset.y);
coords[3] = coord + vec2(-tex_offset.x, 0.);
coords[4] = coord;
coords[5] = coord + vec2(tex_offset.x, 0.);
coords[6] = coord + vec2(-tex_offset.x, tex_offset.y);
coords[7] = coord + vec2(0., tex_offset.y);
coords[8] = coord + tex_offset;
float weights[9] =
{
1., 2., 1.,
2., 4., 2.,
1., 2., 1.
};
vec4 blurred = vec4(0.);
for (int n = 0; n < 9; ++n)
{
blurred += texture(tex, coords[n]) * weights[n];
}
return blurred / 16.f;
}
vec4 sample_image(sampler2D tex, vec2 coord, float blur_strength)
{
vec4 original = texture(tex, coord);
if (blur_strength == 0) return original;
vec2 constraints = 1.f / vec2(640, 360);
vec2 res_offset = 1.f / textureSize(fs0, 0);
vec2 tex_offset = max(res_offset, constraints);
// Sample triangle pattern and average
// TODO: Nicer looking gaussian blur with less sampling
vec4 blur0 = blur_sample(tex, coord + vec2(-res_offset.x, 0.), tex_offset);
vec4 blur1 = blur_sample(tex, coord + vec2(res_offset.x, 0.), tex_offset);
vec4 blur2 = blur_sample(tex, coord + vec2(0., res_offset.y), tex_offset);
vec4 blurred = blur0 + blur1 + blur2;
blurred /= 3.;
return mix(original, blurred, blur_strength);
}
void main()
{
config_t config = unpack_fragment_options();
if (config.clip_fragments)
{
if (gl_FragCoord.x < clip_rect.x || gl_FragCoord.x > clip_rect.z ||
gl_FragCoord.y < clip_rect.y || gl_FragCoord.y > clip_rect.w)
{
discard;
return;
}
}
vec4 diff_color = color;
if (config.use_pulse_glow)
{
diff_color.a *= (sin(timestamp) + 1.f) * 0.5f;
}
if (config.sdf != SDF_DISABLED)
{
const float border_w = sdf_params.w; // Border width
const float d = SDF_fn(config.sdf);
diff_color = SDF_blend(d, border_w, diff_color, sdf_border_color, vec4(0.));
}
switch (config.sampler_mode)
{
default:
case SAMPLER_MODE_NONE:
ocol = diff_color;
break;
case SAMPLER_MODE_FONT2D:
ocol = texture(fs0, tc0).rrrr * diff_color;
break;
case SAMPLER_MODE_FONT3D:
ocol = texture(fs1, vec3(tc0.x, fract(tc0.y), trunc(tc0.y))).rrrr * diff_color;
break;
case SAMPLER_MODE_TEXTURE2D:
#ifdef VULKAN
ocol = sample_image(fs0, tc0, blur_intensity).bgra * diff_color;
#else
ocol = sample_image(fs0, tc0, blur_intensity).rgba * diff_color;
#endif
break;
}
}
)"