rsx: Add border color correction for hardware SNORM formats

This commit is contained in:
kd-11 2026-02-23 00:23:23 +03:00 committed by kd-11
parent 585305f434
commit 759ef0f5b0
5 changed files with 75 additions and 21 deletions

View file

@ -821,6 +821,14 @@ struct color4_base
a *= rhs;
}
void operator += (const color4_base<T>& rhs)
{
r += rhs.r;
g += rhs.g;
b += rhs.b;
a += rhs.a;
}
constexpr color4_base<T> operator * (const color4_base<T>& rhs) const
{
return { r * rhs.r, g * rhs.g, b * rhs.b, a * rhs.a };

View file

@ -82,11 +82,32 @@ namespace gl
{
// NOTE: In OpenGL, the border texels are processed by the pipeline and will be swizzled by the texture view.
// Therefore, we pass the raw value here, and the texture view will handle the rest for us.
const auto encoded_color = tex.border_color();
const bool sext_conv_required = (sampled_image->format_ex.texel_remap_control & rsx::SEXT_MASK) != 0;
const auto encoded_color = tex.border_color(sext_conv_required);
if (get_parameteri(GL_TEXTURE_BORDER_COLOR) != encoded_color)
{
m_propertiesi[GL_TEXTURE_BORDER_COLOR] = encoded_color;
const auto border_color = rsx::decode_border_color(encoded_color);
auto border_color = rsx::decode_border_color(encoded_color);
if (const auto snorm_mask = tex.argb_signed();
!sext_conv_required && tex.argb_signed()) [[ unlikely ]]
{
// Hardware SNORM is active
ensure(sampled_image->format_ex.hw_SNORM_possible());
// Convert the border color in host space (2N - 1)
// HW does the conversion in integer space as (x - 128) / 127 which introduces a biasing error.
const float bias_v = 128.f / 255.f;
const float scale_v = 255.f / 127.f;
color4f scale{ 1.f }, bias{ 0.f };
if (snorm_mask & 1) { scale.a = scale_v; bias.a = -bias_v; }
if (snorm_mask & 2) { scale.r = scale_v; bias.r = -bias_v; }
if (snorm_mask & 4) { scale.g = scale_v; bias.g = -bias_v; }
if (snorm_mask & 8) { scale.b = scale_v; bias.b = -bias_v; }
border_color = (border_color + bias) * scale;
}
glSamplerParameterfv(sampler_handle, GL_TEXTURE_BORDER_COLOR, border_color.rgba);
}
}

View file

@ -364,11 +364,15 @@ namespace rsx
return dimension() != rsx::texture_dimension::dimension1d ? ((registers[NV4097_SET_TEXTURE_IMAGE_RECT + (m_index * 8)]) & 0xffff) : 1;
}
u32 fragment_texture::border_color() const
u32 fragment_texture::border_color(bool apply_colorspace_remapping) const
{
const u32 raw = registers[NV4097_SET_TEXTURE_BORDER_COLOR + (m_index * 8)];
const u32 sext = argb_signed();
if (!apply_colorspace_remapping) [[ likely ]]
{
return raw;
}
const u32 sext = argb_signed();
if (!sext) [[ likely ]]
{
return raw;
@ -430,9 +434,9 @@ namespace rsx
return (conv & mask) | (raw & ~mask);
}
color4f fragment_texture::remapped_border_color() const
color4f fragment_texture::remapped_border_color(bool apply_colorspace_remapping) const
{
color4f base_color = rsx::decode_border_color(border_color());
color4f base_color = rsx::decode_border_color(border_color(apply_colorspace_remapping));
if (remap() == RSX_TEXTURE_REMAP_IDENTITY)
{
return base_color;
@ -571,14 +575,14 @@ namespace rsx
return dimension() != rsx::texture_dimension::dimension1d ? ((registers[NV4097_SET_VERTEX_TEXTURE_IMAGE_RECT + (m_index * 8)]) & 0xffff) : 1;
}
u32 vertex_texture::border_color() const
u32 vertex_texture::border_color(bool) const
{
return registers[NV4097_SET_VERTEX_TEXTURE_BORDER_COLOR + (m_index * 8)];
}
color4f vertex_texture::remapped_border_color() const
color4f vertex_texture::remapped_border_color(bool) const
{
return rsx::decode_border_color(border_color());
return rsx::decode_border_color(border_color(false));
}
u16 vertex_texture::depth() const

View file

@ -80,8 +80,8 @@ namespace rsx
u16 height() const;
// Border Color
u32 border_color() const;
color4f remapped_border_color() const;
u32 border_color(bool apply_colorspace_remapping = false) const;
color4f remapped_border_color(bool apply_colorspace_remapping = false) const;
u16 depth() const;
u32 pitch() const;
@ -136,8 +136,8 @@ namespace rsx
u16 height() const;
// Border Color
u32 border_color() const;
color4f remapped_border_color() const;
u32 border_color(bool = false) const;
color4f remapped_border_color(bool = false) const;
u16 depth() const;
u32 pitch() const;

View file

@ -274,11 +274,11 @@ void VKGSRender::load_texture_env()
return false;
};
auto get_border_color = [&](const rsx::Texture auto& tex)
auto get_border_color = [&](const rsx::Texture auto& tex, bool remap_colorspace)
{
return m_device->get_custom_border_color_support().require_border_color_remap
? tex.remapped_border_color()
: rsx::decode_border_color(tex.border_color());
? tex.remapped_border_color(remap_colorspace)
: rsx::decode_border_color(tex.border_color(remap_colorspace));
};
std::lock_guard lock(m_sampler_mutex);
@ -394,9 +394,30 @@ void VKGSRender::load_texture_env()
// NOTE: In vulkan, the border color can bypass the sample swizzle stage.
// Check the device properties to determine whether to pre-swizzle the colors or not.
const auto border_color = rsx::is_border_clamped_texture(tex)
? vk::border_color_t(get_border_color(tex))
: vk::border_color_t(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK);
const bool sext_conv_required = (sampler_state->format_ex.texel_remap_control & rsx::texture_control_bits::SEXT_MASK) != 0;
vk::border_color_t border_color(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK);
if (rsx::is_border_clamped_texture(tex))
{
auto color_value = get_border_color(tex, sext_conv_required);
if (const auto snorm_mask = tex.argb_signed();
!sext_conv_required && snorm_mask)
{
// Convert the border color in host space (2N - 1)
// HW does the conversion in integer space as (x - 128) / 127 which introduces a biasing error.
const float bias_v = 128.f / 255.f;
const float scale_v = 255.f / 127.f;
color4f scale{ 1.f }, bias{ 0.f };
if (snorm_mask & 1) { scale.a = scale_v; bias.a = -bias_v; }
if (snorm_mask & 2) { scale.r = scale_v; bias.r = -bias_v; }
if (snorm_mask & 4) { scale.g = scale_v; bias.g = -bias_v; }
if (snorm_mask & 8) { scale.b = scale_v; bias.b = -bias_v; }
color_value = (color_value + bias) * scale;
}
border_color = color_value;
}
// Check if non-point filtering can even be used on this format
bool can_sample_linear;
@ -404,7 +425,7 @@ void VKGSRender::load_texture_env()
{
// Most PS3-like formats can be linearly filtered without problem
// Exclude textures that require SNORM conversion however
can_sample_linear = (sampler_state->format_ex.texel_remap_control & rsx::texture_control_bits::SEXT_MASK) == 0;
can_sample_linear = !sext_conv_required;
}
else if (sampler_state->format_class != rsx::classify_format(texture_format) &&
(texture_format == CELL_GCM_TEXTURE_A8R8G8B8 || texture_format == CELL_GCM_TEXTURE_D8R8G8B8))
@ -554,7 +575,7 @@ void VKGSRender::load_texture_env()
// NOTE: In vulkan, the border color can bypass the sample swizzle stage.
// Check the device properties to determine whether to pre-swizzle the colors or not.
const auto border_color = is_border_clamped_texture(tex)
? vk::border_color_t(get_border_color(tex))
? vk::border_color_t(get_border_color(tex, false))
: vk::border_color_t(VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK);
if (vs_sampler_handles[i] &&