|
|
|
@ -49,6 +49,8 @@ layout (std140) uniform shader_data {
|
|
|
|
|
int alphatest_ref;
|
|
|
|
|
float depth_scale;
|
|
|
|
|
float depth_offset;
|
|
|
|
|
float shadow_bias_constant;
|
|
|
|
|
float shadow_bias_linear;
|
|
|
|
|
int scissor_x1;
|
|
|
|
|
int scissor_y1;
|
|
|
|
|
int scissor_x2;
|
|
|
|
@ -220,6 +222,12 @@ PicaFSConfig PicaFSConfig::BuildFromRegs(const Pica::Regs& regs) {
|
|
|
|
|
state.proctex.lut_filter = regs.texturing.proctex_lut.filter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state.shadow_rendering = regs.framebuffer.output_merger.fragment_operation_mode ==
|
|
|
|
|
Pica::FramebufferRegs::FragmentOperationMode::Shadow;
|
|
|
|
|
|
|
|
|
|
state.shadow_texture_orthographic = regs.texturing.shadow.orthographic != 0;
|
|
|
|
|
state.shadow_texture_bias = regs.texturing.shadow.bias << 1;
|
|
|
|
|
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -300,10 +308,9 @@ static std::string SampleTexture(const PicaFSConfig& config, unsigned texture_un
|
|
|
|
|
case TexturingRegs::TextureConfig::TextureCube:
|
|
|
|
|
return "texture(tex_cube, vec3(texcoord0, texcoord0_w))";
|
|
|
|
|
case TexturingRegs::TextureConfig::Shadow2D:
|
|
|
|
|
return "shadowTexture(texcoord0, texcoord0_w)";
|
|
|
|
|
case TexturingRegs::TextureConfig::ShadowCube:
|
|
|
|
|
NGLOG_CRITICAL(HW_GPU, "Unhandled shadow texture");
|
|
|
|
|
UNIMPLEMENTED();
|
|
|
|
|
return "vec4(1.0)"; // stubbed to avoid rendering with wrong shadow
|
|
|
|
|
return "shadowTextureCube(texcoord0, texcoord0_w)";
|
|
|
|
|
default:
|
|
|
|
|
LOG_CRITICAL(HW_GPU, "Unhandled texture type %x",
|
|
|
|
|
static_cast<int>(state.texture0_type));
|
|
|
|
@ -1181,7 +1188,13 @@ float ProcTexNoiseCoef(vec2 x) {
|
|
|
|
|
std::string GenerateFragmentShader(const PicaFSConfig& config, bool separable_shader) {
|
|
|
|
|
const auto& state = config.state;
|
|
|
|
|
|
|
|
|
|
std::string out = "#version 330 core\n";
|
|
|
|
|
std::string out = R"(
|
|
|
|
|
#version 330 core
|
|
|
|
|
#extension GL_ARB_shader_image_load_store : enable
|
|
|
|
|
#extension GL_ARB_shader_image_size : enable
|
|
|
|
|
#define ALLOW_SHADOW (defined(GL_ARB_shader_image_load_store) && defined(GL_ARB_shader_image_size))
|
|
|
|
|
)";
|
|
|
|
|
|
|
|
|
|
if (separable_shader) {
|
|
|
|
|
out += "#extension GL_ARB_separate_shader_objects : enable\n";
|
|
|
|
|
}
|
|
|
|
@ -1204,6 +1217,16 @@ uniform samplerBuffer proctex_color_map;
|
|
|
|
|
uniform samplerBuffer proctex_alpha_map;
|
|
|
|
|
uniform samplerBuffer proctex_lut;
|
|
|
|
|
uniform samplerBuffer proctex_diff_lut;
|
|
|
|
|
|
|
|
|
|
#if ALLOW_SHADOW
|
|
|
|
|
layout(r32ui) uniform readonly uimage2D shadow_texture_px;
|
|
|
|
|
layout(r32ui) uniform readonly uimage2D shadow_texture_nx;
|
|
|
|
|
layout(r32ui) uniform readonly uimage2D shadow_texture_py;
|
|
|
|
|
layout(r32ui) uniform readonly uimage2D shadow_texture_ny;
|
|
|
|
|
layout(r32ui) uniform readonly uimage2D shadow_texture_pz;
|
|
|
|
|
layout(r32ui) uniform readonly uimage2D shadow_texture_nz;
|
|
|
|
|
layout(r32ui) uniform uimage2D shadow_buffer;
|
|
|
|
|
#endif
|
|
|
|
|
)";
|
|
|
|
|
|
|
|
|
|
out += UniformBlockDef;
|
|
|
|
@ -1248,6 +1271,147 @@ vec4 byteround(vec4 x) {
|
|
|
|
|
return round(x * 255.0) * (1.0 / 255.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if ALLOW_SHADOW
|
|
|
|
|
|
|
|
|
|
uvec2 DecodeShadow(uint pixel) {
|
|
|
|
|
return uvec2(pixel >> 8, pixel & 0xFFu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint EncodeShadow(uvec2 pixel) {
|
|
|
|
|
return (pixel.x << 8) | pixel.y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float CompareShadow(uint pixel, uint z) {
|
|
|
|
|
uvec2 p = DecodeShadow(pixel);
|
|
|
|
|
return mix(float(p.y) * (1.0 / 255.0), 0.0, p.x <= z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float SampleShadow2D(ivec2 uv, uint z) {
|
|
|
|
|
if (any(bvec4( lessThan(uv, ivec2(0)), greaterThanEqual(uv, imageSize(shadow_texture_px)) )))
|
|
|
|
|
return 1.0;
|
|
|
|
|
return CompareShadow(imageLoad(shadow_texture_px, uv).x, z);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float mix2(vec4 s, vec2 a) {
|
|
|
|
|
vec2 t = mix(s.xy, s.zw, a.yy);
|
|
|
|
|
return mix(t.x, t.y, a.x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec4 shadowTexture(vec2 uv, float w) {
|
|
|
|
|
)";
|
|
|
|
|
if (!config.state.shadow_texture_orthographic) {
|
|
|
|
|
out += "uv /= w;";
|
|
|
|
|
}
|
|
|
|
|
out += "uint z = uint(max(0, int(min(abs(w), 1.0) * 0xFFFFFF) - " +
|
|
|
|
|
std::to_string(state.shadow_texture_bias) + "));";
|
|
|
|
|
out += R"(
|
|
|
|
|
vec2 coord = vec2(imageSize(shadow_texture_px)) * uv - vec2(0.5);
|
|
|
|
|
vec2 coord_floor = floor(coord);
|
|
|
|
|
vec2 f = coord - coord_floor;
|
|
|
|
|
ivec2 i = ivec2(coord_floor);
|
|
|
|
|
vec4 s = vec4(
|
|
|
|
|
SampleShadow2D(i , z),
|
|
|
|
|
SampleShadow2D(i + ivec2(1, 0), z),
|
|
|
|
|
SampleShadow2D(i + ivec2(0, 1), z),
|
|
|
|
|
SampleShadow2D(i + ivec2(1, 1), z));
|
|
|
|
|
return vec4(mix2(s, f));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec4 shadowTextureCube(vec2 uv, float w) {
|
|
|
|
|
ivec2 size = imageSize(shadow_texture_px);
|
|
|
|
|
vec3 c = vec3(uv, w);
|
|
|
|
|
vec3 a = abs(c);
|
|
|
|
|
if (a.x > a.y && a.x > a.z) {
|
|
|
|
|
w = a.x;
|
|
|
|
|
uv = -c.zy;
|
|
|
|
|
if (c.x < 0.0) uv.x = -uv.x;
|
|
|
|
|
} else if (a.y > a.z) {
|
|
|
|
|
w = a.y;
|
|
|
|
|
uv = c.xz;
|
|
|
|
|
if (c.y < 0.0) uv.y = -uv.y;
|
|
|
|
|
} else {
|
|
|
|
|
w = a.z;
|
|
|
|
|
uv = -c.xy;
|
|
|
|
|
if (c.z > 0.0) uv.x = -uv.x;
|
|
|
|
|
}
|
|
|
|
|
)";
|
|
|
|
|
out += "uint z = uint(max(0, int(min(w, 1.0) * 0xFFFFFF) - " +
|
|
|
|
|
std::to_string(state.shadow_texture_bias) + "));";
|
|
|
|
|
out += R"(
|
|
|
|
|
vec2 coord = vec2(size) * (uv / w * vec2(0.5) + vec2(0.5)) - vec2(0.5);
|
|
|
|
|
vec2 coord_floor = floor(coord);
|
|
|
|
|
vec2 f = coord - coord_floor;
|
|
|
|
|
ivec2 i00 = ivec2(coord_floor);
|
|
|
|
|
ivec2 i10 = i00 + ivec2(1, 0);
|
|
|
|
|
ivec2 i01 = i00 + ivec2(0, 1);
|
|
|
|
|
ivec2 i11 = i00 + ivec2(1, 1);
|
|
|
|
|
ivec2 cmin = ivec2(0), cmax = size - ivec2(1, 1);
|
|
|
|
|
i00 = clamp(i00, cmin, cmax);
|
|
|
|
|
i10 = clamp(i10, cmin, cmax);
|
|
|
|
|
i01 = clamp(i01, cmin, cmax);
|
|
|
|
|
i11 = clamp(i11, cmin, cmax);
|
|
|
|
|
uvec4 pixels;
|
|
|
|
|
// This part should have been refactored into functions,
|
|
|
|
|
// but many drivers don't like passing uimage2D as parameters
|
|
|
|
|
if (a.x > a.y && a.x > a.z) {
|
|
|
|
|
if (c.x > 0.0)
|
|
|
|
|
pixels = uvec4(
|
|
|
|
|
imageLoad(shadow_texture_px, i00).r,
|
|
|
|
|
imageLoad(shadow_texture_px, i10).r,
|
|
|
|
|
imageLoad(shadow_texture_px, i01).r,
|
|
|
|
|
imageLoad(shadow_texture_px, i11).r);
|
|
|
|
|
else
|
|
|
|
|
pixels = uvec4(
|
|
|
|
|
imageLoad(shadow_texture_nx, i00).r,
|
|
|
|
|
imageLoad(shadow_texture_nx, i10).r,
|
|
|
|
|
imageLoad(shadow_texture_nx, i01).r,
|
|
|
|
|
imageLoad(shadow_texture_nx, i11).r);
|
|
|
|
|
} else if (a.y > a.z) {
|
|
|
|
|
if (c.y > 0.0)
|
|
|
|
|
pixels = uvec4(
|
|
|
|
|
imageLoad(shadow_texture_py, i00).r,
|
|
|
|
|
imageLoad(shadow_texture_py, i10).r,
|
|
|
|
|
imageLoad(shadow_texture_py, i01).r,
|
|
|
|
|
imageLoad(shadow_texture_py, i11).r);
|
|
|
|
|
else
|
|
|
|
|
pixels = uvec4(
|
|
|
|
|
imageLoad(shadow_texture_ny, i00).r,
|
|
|
|
|
imageLoad(shadow_texture_ny, i10).r,
|
|
|
|
|
imageLoad(shadow_texture_ny, i01).r,
|
|
|
|
|
imageLoad(shadow_texture_ny, i11).r);
|
|
|
|
|
} else {
|
|
|
|
|
if (c.z > 0.0)
|
|
|
|
|
pixels = uvec4(
|
|
|
|
|
imageLoad(shadow_texture_pz, i00).r,
|
|
|
|
|
imageLoad(shadow_texture_pz, i10).r,
|
|
|
|
|
imageLoad(shadow_texture_pz, i01).r,
|
|
|
|
|
imageLoad(shadow_texture_pz, i11).r);
|
|
|
|
|
else
|
|
|
|
|
pixels = uvec4(
|
|
|
|
|
imageLoad(shadow_texture_nz, i00).r,
|
|
|
|
|
imageLoad(shadow_texture_nz, i10).r,
|
|
|
|
|
imageLoad(shadow_texture_nz, i01).r,
|
|
|
|
|
imageLoad(shadow_texture_nz, i11).r);
|
|
|
|
|
}
|
|
|
|
|
vec4 s = vec4(
|
|
|
|
|
CompareShadow(pixels.x, z),
|
|
|
|
|
CompareShadow(pixels.y, z),
|
|
|
|
|
CompareShadow(pixels.z, z),
|
|
|
|
|
CompareShadow(pixels.w, z));
|
|
|
|
|
return vec4(mix2(s, f));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
vec4 shadowTexture(vec2 uv, float w) {
|
|
|
|
|
return vec4(1.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vec4 shadowTextureCube(vec2 uv, float w) {
|
|
|
|
|
return vec4(1.0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
)";
|
|
|
|
|
|
|
|
|
|
if (config.state.proctex.enable)
|
|
|
|
@ -1331,9 +1495,38 @@ vec4 secondary_fragment_color = vec4(0.0);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out += "gl_FragDepth = depth;\n";
|
|
|
|
|
// Round the final fragment color to maintain the PICA's 8 bits of precision
|
|
|
|
|
out += "color = byteround(last_tex_env_out);\n";
|
|
|
|
|
if (state.shadow_rendering) {
|
|
|
|
|
out += R"(
|
|
|
|
|
#if ALLOW_SHADOW
|
|
|
|
|
uint d = uint(clamp(depth, 0.0, 1.0) * 0xFFFFFF);
|
|
|
|
|
uint s = uint(last_tex_env_out.g * 0xFF);
|
|
|
|
|
ivec2 image_coord = ivec2(gl_FragCoord.xy);
|
|
|
|
|
|
|
|
|
|
uint old = imageLoad(shadow_buffer, image_coord).x;
|
|
|
|
|
uint new;
|
|
|
|
|
uint old2;
|
|
|
|
|
do {
|
|
|
|
|
old2 = old;
|
|
|
|
|
|
|
|
|
|
uvec2 ref = DecodeShadow(old);
|
|
|
|
|
if (d < ref.x) {
|
|
|
|
|
if (s == 0u) {
|
|
|
|
|
ref.x = d;
|
|
|
|
|
} else {
|
|
|
|
|
s = uint(float(s) / (shadow_bias_constant + shadow_bias_linear * float(d) / float(ref.x)));
|
|
|
|
|
ref.y = min(s, ref.y);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
new = EncodeShadow(ref);
|
|
|
|
|
|
|
|
|
|
} while ((old = imageAtomicCompSwap(shadow_buffer, image_coord, old, new)) != old2);
|
|
|
|
|
#endif // ALLOW_SHADOW
|
|
|
|
|
)";
|
|
|
|
|
} else {
|
|
|
|
|
out += "gl_FragDepth = depth;\n";
|
|
|
|
|
// Round the final fragment color to maintain the PICA's 8 bits of precision
|
|
|
|
|
out += "color = byteround(last_tex_env_out);\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out += "}";
|
|
|
|
|
|
|
|
|
|