GPU/HW: Further tweaks to replacement alpha handling

We can't simply clear the alpha channel unconditionally here, because that
would result in any black pixels with zero alpha being transparency-culled.

Instead, we set it to a minimum value (2/255 in case of rounding error, I
don't trust drivers here) so that transparent polygons in the source still
set bit 15 to zero in the framebuffer, but are not transparency-culled.

Silent Hill needs it to be zero, I'm not aware of anything that needs
specific values yet. If it did, we'd need a different dumping technique.
This commit is contained in:
Stenzek 2025-01-23 12:12:40 +10:00
parent 9113a6e6a6
commit df8822760a
No known key found for this signature in database
3 changed files with 44 additions and 34 deletions

View File

@ -732,8 +732,8 @@ std::string GPU_HW_ShaderGen::GenerateBatchFragmentShader(
GPU_HW::BatchRenderMode render_mode, GPUTransparencyMode transparency, GPU_HW::BatchTextureMode texture_mode,
GPUTextureFilter texture_filtering, bool upscaled, bool msaa, bool per_sample_shading, bool uv_limits,
bool force_round_texcoords, bool true_color, bool dithering, bool scaled_dithering, bool disable_color_perspective,
bool interlacing, bool check_mask, bool write_mask_as_depth, bool use_rov, bool use_rov_depth,
bool rov_depth_test, bool rov_depth_write) const
bool interlacing, bool check_mask, bool write_mask_as_depth, bool use_rov, bool use_rov_depth, bool rov_depth_test,
bool rov_depth_write) const
{
DebugAssert(!true_color || !dithering); // Should not be doing dithering+true color.
@ -1834,10 +1834,12 @@ std::string GPU_HW_ShaderGen::GenerateBoxSampleDownsampleFragmentShader(u32 fact
return ss.str();
}
std::string GPU_HW_ShaderGen::GenerateReplacementMergeFragmentShader(bool semitransparent, bool bilinear_filter) const
std::string GPU_HW_ShaderGen::GenerateReplacementMergeFragmentShader(bool replacement, bool semitransparent,
bool bilinear_filter) const
{
std::stringstream ss;
WriteHeader(ss);
DefineMacro(ss, "REPLACEMENT", replacement);
DefineMacro(ss, "SEMITRANSPARENT", semitransparent);
DefineMacro(ss, "BILINEAR_FILTER", bilinear_filter);
DeclareUniformBuffer(ss, {"float4 u_texture_size"}, true);
@ -1864,6 +1866,7 @@ std::string GPU_HW_ShaderGen::GenerateReplacementMergeFragmentShader(bool semitr
// Bilinearly interpolate.
float2 weights = abs(texel_top_left);
float4 color = lerp(lerp(s00, s10, weights.x), lerp(s01, s11, weights.x), weights.y);
float orig_alpha = float(color.a > 0.0);
#if !SEMITRANSPARENT
// Compute alpha from how many texels aren't pixel color 0000h.
@ -1881,10 +1884,12 @@ std::string GPU_HW_ShaderGen::GenerateReplacementMergeFragmentShader(bool semitr
#endif
#else
float4 color = SAMPLE_TEXTURE_LEVEL(samp0, v_tex0, 0.0);
float orig_alpha = color.a;
#endif
o_col0.rgb = color.rgb;
// Alpha processing.
#if REPLACEMENT
#if SEMITRANSPARENT
// Map anything not 255 to 1 for semitransparent, otherwise zero for opaque.
o_col0.a = (color.a <= 0.95f) ? 1.0f : 0.0f;
@ -1892,12 +1897,21 @@ std::string GPU_HW_ShaderGen::GenerateReplacementMergeFragmentShader(bool semitr
#else
// Map anything with an alpha below 0.5 to transparent.
// Leave (0,0,0,0) as 0000 for opaque replacements for cutout alpha.
o_col0.rgb = lerp(o_col0.rgb, float3(0.0, 0.0, 0.0), float(color.a < 0.5));
float alpha = float(color.a >= 0.5);
o_col0.rgb = lerp(float3(0.0, 0.0, 0.0), o_col0.rgb, alpha);
// Clear alpha channel. This is the value for bit15 in the framebuffer.
// We can't simply clear the alpha channel unconditionally here, because that
// would result in any black pixels with zero alpha being transparency-culled.
// Instead, we set it to a minimum value (2/255 in case of rounding error, I
// don't trust drivers here) so that transparent polygons in the source still
// set bit 15 to zero in the framebuffer, but are not transparency-culled.
// Silent Hill needs it to be zero, I'm not aware of anything that needs
// specific values yet. If it did, we'd need a different dumping technique.
o_col0.a = 0.0;
o_col0.a = lerp(0.0, 2.0 / 255.0, alpha);
#endif
#else
// Preserve original bit 15 for non-replacements.
o_col0.a = orig_alpha;
#endif
}
)";

View File

@ -44,7 +44,8 @@ public:
std::string GenerateAdaptiveDownsampleCompositeFragmentShader() const;
std::string GenerateBoxSampleDownsampleFragmentShader(u32 factor) const;
std::string GenerateReplacementMergeFragmentShader(bool semitransparent, bool bilinear_filter) const;
std::string GenerateReplacementMergeFragmentShader(bool replacement, bool semitransparent,
bool bilinear_filter) const;
private:
void WriteColorConversionFunctions(std::stringstream& ss) const;

View File

@ -862,7 +862,7 @@ bool GPUTextureCache::CompilePipelines(Error* error)
std::unique_ptr<GPUShader> fs = g_gpu_device->CreateShader(
GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateReplacementMergeFragmentShader(false, s_state.config.replacement_scale_linear_filter));
shadergen.GenerateReplacementMergeFragmentShader(false, false, s_state.config.replacement_scale_linear_filter));
if (!fs)
return false;
GL_OBJECT_NAME(fs, "Replacement upscale shader");
@ -871,10 +871,8 @@ bool GPUTextureCache::CompilePipelines(Error* error)
return false;
GL_OBJECT_NAME(s_state.replacement_upscale_pipeline, "Replacement upscale pipeline");
if (s_state.config.replacement_scale_linear_filter)
{
fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateReplacementMergeFragmentShader(false, false));
shadergen.GenerateReplacementMergeFragmentShader(true, false, false));
if (!fs)
return false;
GL_OBJECT_NAME(fs, "Replacement draw shader");
@ -882,10 +880,9 @@ bool GPUTextureCache::CompilePipelines(Error* error)
if (!(s_state.replacement_draw_pipeline = g_gpu_device->CreatePipeline(plconfig)))
return false;
GL_OBJECT_NAME(s_state.replacement_draw_pipeline, "Replacement draw pipeline");
}
fs = g_gpu_device->CreateShader(GPUShaderStage::Fragment, shadergen.GetLanguage(),
shadergen.GenerateReplacementMergeFragmentShader(true, false));
shadergen.GenerateReplacementMergeFragmentShader(true, true, false));
if (!fs)
return false;
GL_OBJECT_NAME(fs, "Replacement semitransparent draw shader");
@ -3669,10 +3666,8 @@ void GPUTextureCache::ApplyTextureReplacements(SourceKey key, HashType tex_hash,
g_gpu_device->SetTextureSampler(0, si.texture,
s_state.config.replacement_scale_linear_filter ? g_gpu_device->GetLinearSampler() :
g_gpu_device->GetNearestSampler());
g_gpu_device->SetPipeline(si.invert_alpha ?
s_state.replacement_semitransparent_draw_pipeline.get() :
(s_state.replacement_draw_pipeline ? s_state.replacement_draw_pipeline.get() :
s_state.replacement_upscale_pipeline.get()));
g_gpu_device->SetPipeline(si.invert_alpha ? s_state.replacement_semitransparent_draw_pipeline.get() :
s_state.replacement_draw_pipeline.get());
g_gpu_device->Draw(3, 0);
}