[GPU] 8_8_8_8_GAMMA resolve update; fix unsigned-biased fetch scaling
8_8_8_8_GAMMA resolve update: This hopefully settles how safe resolves of gamma RTs are handled, because it's almost certainly not settled by destination. It's now believed that gamma sources seen by resolve are always being decoded, and that's a decode that's keyed only on the source format, where every destination is fed linear values, and any encode is now just removed rather than conditional. These sources are re-alised as plain before resolving, which is the same exact idea that preserves float 2_10_10_10 bits through a resolve too. Integer texture fetch scaling update: Unsigned-biased components were using the regular unsigned scale even though the conversion had remapped the sample [0, 1] to [-1, 1]. Scale bits data is now increased from 5 to 6 bits and use the new bit to mark unsigned-biased components. DXBC/SPIR-V use half of the unsigned scale and add a -0.5 offset. This fixes post-processing in 415608B2 and will likely improve other titles w/ EDRAM transports through affected textures, but perhaps not so dramatically.
This commit is contained in:
committed by
Radosław Gliński
parent
888b9ad258
commit
2ddc5ef737
@@ -29,15 +29,12 @@ DEFINE_bool(
|
||||
"GPU");
|
||||
|
||||
DEFINE_bool(
|
||||
resolve_check_number_format, false,
|
||||
resolve_check_number_format, true,
|
||||
"Require the destination number format to match before using fast color "
|
||||
"resolves.\n"
|
||||
"Fast resolves copy the exact EDRAM bits. If a title resolves unsigned "
|
||||
"color data to a signed or integer destination, enabling this forces full "
|
||||
"resolves in the shader so the destination gets repacked instead.\n"
|
||||
"This can fix some garbage shading stemming from format mismatches, but "
|
||||
"it's disabled by default because it can worsen performance in some games "
|
||||
"that realistically don't need it.",
|
||||
"resolves in the shader so the destination gets repacked instead.",
|
||||
"GPU");
|
||||
|
||||
DEFINE_bool(
|
||||
@@ -1372,17 +1369,24 @@ ResolveCopyShaderIndex ResolveInfo::GetCopyShader(
|
||||
ResolveEdramInfo edram_info = is_depth ? depth_edram_info : color_edram_info;
|
||||
bool source_is_64bpp = !is_depth && color_edram_info.format_is_64bpp != 0;
|
||||
// Fast color resolve is a raw copy. If copy_dest_number asks for a different
|
||||
// fixed interpretation, full resolve has to do the repack.
|
||||
if (is_depth || (!copy_dest_info.copy_dest_exp_bias &&
|
||||
xenos::IsSingleCopySampleSelected(
|
||||
copy_dest_coordinate_info.copy_sample_select) &&
|
||||
xenos::IsColorResolveFormatBitwiseEquivalent(
|
||||
xenos::ColorRenderTargetFormat(color_edram_info.format),
|
||||
xenos::ColorFormat(copy_dest_info.copy_dest_format)) &&
|
||||
(!cvars::resolve_check_number_format ||
|
||||
ColorResolveNumberFormatMatches(
|
||||
xenos::ColorFormat(copy_dest_info.copy_dest_format),
|
||||
copy_dest_info.copy_dest_number)))) {
|
||||
// target that'd be decoded to linear by a real hardware resolve, it needs the
|
||||
// full shader conversion. Any title keeping the encoding will re-alias as
|
||||
// 8_8_8_8 before resolving, so any gamma source is always being decoded.
|
||||
bool gamma_decoded_source =
|
||||
!is_depth && color_edram_info.decode_pwl_gamma &&
|
||||
xenos::ColorRenderTargetFormat(color_edram_info.format) ==
|
||||
xenos::ColorRenderTargetFormat::k_8_8_8_8_GAMMA;
|
||||
if (is_depth ||
|
||||
(!gamma_decoded_source && !copy_dest_info.copy_dest_exp_bias &&
|
||||
xenos::IsSingleCopySampleSelected(
|
||||
copy_dest_coordinate_info.copy_sample_select) &&
|
||||
xenos::IsColorResolveFormatBitwiseEquivalent(
|
||||
xenos::ColorRenderTargetFormat(color_edram_info.format),
|
||||
xenos::ColorFormat(copy_dest_info.copy_dest_format)) &&
|
||||
(!cvars::resolve_check_number_format ||
|
||||
ColorResolveNumberFormatMatches(
|
||||
xenos::ColorFormat(copy_dest_info.copy_dest_format),
|
||||
copy_dest_info.copy_dest_number)))) {
|
||||
if (edram_info.msaa_samples >= xenos::MsaaSamples::k4X) {
|
||||
shader = source_is_64bpp ? ResolveCopyShaderIndex::kFast64bpp4xMSAA
|
||||
: ResolveCopyShaderIndex::kFast32bpp4xMSAA;
|
||||
|
||||
@@ -2063,12 +2063,16 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction(
|
||||
// the guest integer range here.
|
||||
uint32_t integer_scale_bits_temp = PushSystemTemp();
|
||||
uint32_t integer_scale_temp = PushSystemTemp();
|
||||
uint32_t integer_scale_flags_temp = PushSystemTemp();
|
||||
dxbc::Dest integer_scale_bits_dest(dxbc::Dest::R(
|
||||
integer_scale_bits_temp, used_result_nonzero_components));
|
||||
dxbc::Src integer_scale_bits_src(dxbc::Src::R(integer_scale_bits_temp));
|
||||
dxbc::Dest integer_scale_dest(
|
||||
dxbc::Dest::R(integer_scale_temp, used_result_nonzero_components));
|
||||
dxbc::Src integer_scale_src(dxbc::Src::R(integer_scale_temp));
|
||||
dxbc::Dest integer_scale_flags_dest(dxbc::Dest::R(
|
||||
integer_scale_flags_temp, used_result_nonzero_components));
|
||||
dxbc::Src integer_scale_flags_src(dxbc::Src::R(integer_scale_flags_temp));
|
||||
dxbc::Src integer_scale_bits_packed = LoadSystemConstant(
|
||||
SystemConstants::Index::kTextureIntegerScaleBits,
|
||||
offsetof(SystemConstants, texture_integer_scale_bits) +
|
||||
@@ -2077,21 +2081,33 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction(
|
||||
// Uniform early out. Zero means leave the sample alone. Only integer
|
||||
// num_format on fixed textures has scale bits.
|
||||
a_.OpIf(true, integer_scale_bits_packed);
|
||||
a_.OpUBFE(integer_scale_bits_dest, dxbc::Src::LU(5),
|
||||
dxbc::Src::LU(0, 5, 10, 15), integer_scale_bits_packed);
|
||||
a_.OpUBFE(integer_scale_bits_dest, dxbc::Src::LU(6),
|
||||
dxbc::Src::LU(0, 6, 12, 18), integer_scale_bits_packed);
|
||||
a_.OpAnd(integer_scale_dest, integer_scale_bits_src, dxbc::Src::LU(0xF));
|
||||
a_.OpIAdd(integer_scale_dest, integer_scale_src, dxbc::Src::LU(1));
|
||||
a_.OpUShR(integer_scale_bits_dest, integer_scale_bits_src,
|
||||
dxbc::Src::LU(4));
|
||||
a_.OpIAdd(integer_scale_dest, integer_scale_src, -integer_scale_bits_src);
|
||||
a_.OpUBFE(integer_scale_flags_dest, dxbc::Src::LU(1), dxbc::Src::LU(4),
|
||||
integer_scale_bits_src);
|
||||
a_.OpIAdd(integer_scale_dest, integer_scale_src,
|
||||
-integer_scale_flags_src);
|
||||
a_.OpIShL(integer_scale_dest, dxbc::Src::LU(1), integer_scale_src);
|
||||
a_.OpIAdd(integer_scale_dest, integer_scale_src, dxbc::Src::LI(-1));
|
||||
a_.OpUToF(integer_scale_dest, integer_scale_src);
|
||||
a_.OpMul(
|
||||
// Unsigned biased samples are already mapped from [0, 1] to [-1, 1], so
|
||||
// use half of the unsigned scale and subtract 0.5 to restore the guest's
|
||||
// integer value.
|
||||
a_.OpUBFE(integer_scale_bits_dest, dxbc::Src::LU(1), dxbc::Src::LU(5),
|
||||
integer_scale_bits_src);
|
||||
a_.OpMovC(integer_scale_flags_dest, integer_scale_bits_src,
|
||||
dxbc::Src::LF(0.5f), dxbc::Src::LF(1.0f));
|
||||
a_.OpMul(integer_scale_dest, integer_scale_src, integer_scale_flags_src);
|
||||
a_.OpMovC(integer_scale_flags_dest, integer_scale_bits_src,
|
||||
dxbc::Src::LF(-0.5f), dxbc::Src::LF(0.0f));
|
||||
a_.OpMAd(
|
||||
dxbc::Dest::R(system_temp_result_, used_result_nonzero_components),
|
||||
dxbc::Src::R(system_temp_result_), integer_scale_src);
|
||||
dxbc::Src::R(system_temp_result_), integer_scale_src,
|
||||
integer_scale_flags_src);
|
||||
a_.OpEndIf();
|
||||
PopSystemTemp(2);
|
||||
PopSystemTemp(3);
|
||||
}
|
||||
if (signs_temp != UINT32_MAX) {
|
||||
PopSystemTemp();
|
||||
|
||||
@@ -690,21 +690,17 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
XeResolveInfo resolve_info, inout_param_xe(float4_xe, pixel)) {
|
||||
// Source gamma is RGB only. 8_8_8_8_GAMMA still stores alpha as ordinary
|
||||
// fixed data, so alpha needs to stay with normal resolve.
|
||||
// A gamma render target is decoded to linear by resolve and doesn't have
|
||||
// an encoding stage, so it can be inferred that linear values are what
|
||||
// every destination stores. Keeping the bytes is done on the render
|
||||
// target side, by re-aliasing the surface as 8_8_8_8 before resolving,
|
||||
// the same model that maintains float 2_10_10_10 bits, which makes it
|
||||
// not a gamma source here either.
|
||||
dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) {
|
||||
XeResolvePWLGammaToLinearRGB(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
void XeResolveEncodePWLGammaDest(
|
||||
XeResolveInfo resolve_info, inout_param_xe(float4_xe, pixel)) {
|
||||
// Only re-encode when the source was PWL gamma and the destination is the
|
||||
// 8_8_8_8 UNORM storage we treat as the same PWL byte stream.
|
||||
dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info) &&
|
||||
XeResolveDestStoresPWLGamma(resolve_info)) {
|
||||
XeResolveLinearToPWLGammaRGB(pixel);
|
||||
}
|
||||
}
|
||||
|
||||
void XeResolveLoad2RGBAColors(
|
||||
param_byte_buffer_xe(xe_resolve_edram) param_next_after_byte_buffer_xe
|
||||
XeResolveInfo resolve_info, uint address_bytes,
|
||||
@@ -761,8 +757,6 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
}
|
||||
pixel_0 *= exp_bias;
|
||||
pixel_1 *= exp_bias;
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_0);
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_1);
|
||||
dont_flatten_xe if (resolve_info.dest_swap) {
|
||||
pixel_0 = pixel_0.bgra;
|
||||
pixel_1 = pixel_1.bgra;
|
||||
@@ -847,10 +841,6 @@ uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) {
|
||||
pixel_1 *= exp_bias;
|
||||
pixel_2 *= exp_bias;
|
||||
pixel_3 *= exp_bias;
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_0);
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_1);
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_2);
|
||||
XeResolveEncodePWLGammaDest(resolve_info, pixel_3);
|
||||
dont_flatten_xe if (resolve_info.dest_swap) {
|
||||
pixel_0 = pixel_0.bgra;
|
||||
pixel_1 = pixel_1.bgra;
|
||||
|
||||
@@ -2346,8 +2346,8 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction(
|
||||
spv::Id scale_bits = builder_->createTriOp(
|
||||
spv::OpBitFieldUExtract, type_uint_,
|
||||
integer_scale_bits_packed,
|
||||
builder_->makeUintConstant(result_component_index * 5),
|
||||
builder_->makeUintConstant(5));
|
||||
builder_->makeUintConstant(result_component_index * 6),
|
||||
builder_->makeUintConstant(6));
|
||||
spv::Id scale_shift = builder_->createBinOp(
|
||||
spv::OpIAdd, type_uint_,
|
||||
builder_->createBinOp(spv::OpBitwiseAnd, type_uint_,
|
||||
@@ -2365,11 +2365,32 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction(
|
||||
builder_->createBinOp(spv::OpShiftLeftLogical, type_uint_,
|
||||
const_uint_1, scale_shift),
|
||||
const_uint_1);
|
||||
// Unsigned biased samples are already mapped [0, 1] to [-1, 1],
|
||||
// so use half of the unsigned scale and subtract 0.5 to restore
|
||||
// the guest's integer value.
|
||||
spv::Id biased = builder_->createBinOp(
|
||||
spv::OpINotEqual, type_bool_,
|
||||
builder_->createTriOp(spv::OpBitFieldUExtract, type_uint_,
|
||||
scale_bits,
|
||||
builder_->makeUintConstant(5),
|
||||
builder_->makeUintConstant(1)),
|
||||
builder_->makeUintConstant(0));
|
||||
spv::Id scale_float = builder_->createNoContractionBinOp(
|
||||
spv::OpFMul, type_float_,
|
||||
builder_->createUnaryOp(spv::OpConvertUToF, type_float_,
|
||||
scale_uint),
|
||||
builder_->createTriOp(spv::OpSelect, type_float_, biased,
|
||||
builder_->makeFloatConstant(0.5f),
|
||||
builder_->makeFloatConstant(1.0f)));
|
||||
scaled_result[result_component_index] =
|
||||
builder_->createNoContractionBinOp(
|
||||
spv::OpFMul, type_float_, result[result_component_index],
|
||||
builder_->createUnaryOp(spv::OpConvertUToF, type_float_,
|
||||
scale_uint));
|
||||
spv::OpFAdd, type_float_,
|
||||
builder_->createNoContractionBinOp(
|
||||
spv::OpFMul, type_float_,
|
||||
result[result_component_index], scale_float),
|
||||
builder_->createTriOp(spv::OpSelect, type_float_, biased,
|
||||
builder_->makeFloatConstant(-0.5f),
|
||||
builder_->makeFloatConstant(0.0f)));
|
||||
}
|
||||
}
|
||||
if_integer_scale.makeEndIf();
|
||||
|
||||
@@ -688,10 +688,10 @@ TextureCache::Texture* TextureCache::FindOrCreateTexture(TextureKey key) {
|
||||
|
||||
// Packs the integer scale the fetch shader reads from the system constant to
|
||||
// undo the host sampler's normalization - the guest wants e.g. [0, 255], not
|
||||
// [0, 1]. 5 bits per output component: bits 0:3 = width - 1, bit 4 = signed.
|
||||
// The scale lands after swizzling, so each output lane walks the host swizzle
|
||||
// back to its source component's width; constant (0/1) lanes, gamma, and
|
||||
// non-fixed formats have nothing to rescale and stay 0.
|
||||
// [0, 1]. 6 bits per output component: bits 0:3 = width - 1, bit 4 = signed,
|
||||
// bit 5 = unsigned-biased. The scale lands after swizzling, so each output lane
|
||||
// walks the host swizzle back to its source component's width; constant (0/1)
|
||||
// lanes, gamma, and non-fixed formats have nothing to rescale and stay 0.
|
||||
uint32_t TextureCache::GetIntegerScaleBits(xenos::TextureFormat guest_format,
|
||||
uint32_t num_format,
|
||||
uint32_t host_swizzle,
|
||||
@@ -721,9 +721,12 @@ uint32_t TextureCache::GetIntegerScaleBits(xenos::TextureFormat guest_format,
|
||||
uint32_t component_scale = uint32_t(width - 1);
|
||||
if (sign == xenos::TextureSign::kSigned) {
|
||||
component_scale |= UINT32_C(1) << 4;
|
||||
// Unsigned-biased: halve the scaled value and apply an extra offset.
|
||||
} else if (sign == xenos::TextureSign::kUnsignedBiased) {
|
||||
component_scale |= UINT32_C(1) << 5;
|
||||
}
|
||||
|
||||
scale_bits |= component_scale << (i * 5);
|
||||
scale_bits |= component_scale << (i * 6);
|
||||
}
|
||||
|
||||
return scale_bits;
|
||||
|
||||
@@ -616,6 +616,10 @@ constexpr bool IsColorResolveFormatBitwiseEquivalent(
|
||||
switch (render_target_format) {
|
||||
case ColorRenderTargetFormat::k_8_8_8_8:
|
||||
// Shaders fetch data copied from k_8_8_8_8_GAMMA with TextureSign::kGamma.
|
||||
// Gamma sources are decoded to linear by real hardware resolve, so with the
|
||||
// decode enabled, GetCopyShader separately excludes all raw copies. Any
|
||||
// title that keeps the encoding to fetch it back with kGamma re-aliases the
|
||||
// surface as k_8_8_8_8.
|
||||
case ColorRenderTargetFormat::k_8_8_8_8_GAMMA:
|
||||
// TODO(Triang3l): Investigate k_8_8_8_8_A.
|
||||
return color_format == ColorFormat::k_8_8_8_8 ||
|
||||
|
||||
Reference in New Issue
Block a user