diff --git a/src/xenia/gpu/draw_util.cc b/src/xenia/gpu/draw_util.cc index ae211c527..d39b186e1 100644 --- a/src/xenia/gpu/draw_util.cc +++ b/src/xenia/gpu/draw_util.cc @@ -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; diff --git a/src/xenia/gpu/dxbc_shader_translator_fetch.cc b/src/xenia/gpu/dxbc_shader_translator_fetch.cc index c4cbd78bb..478a87185 100644 --- a/src/xenia/gpu/dxbc_shader_translator_fetch.cc +++ b/src/xenia/gpu/dxbc_shader_translator_fetch.cc @@ -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(); diff --git a/src/xenia/gpu/shaders/resolve.xesli b/src/xenia/gpu/shaders/resolve.xesli index d4b812c79..f2ece4bb3 100644 --- a/src/xenia/gpu/shaders/resolve.xesli +++ b/src/xenia/gpu/shaders/resolve.xesli @@ -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; diff --git a/src/xenia/gpu/spirv_shader_translator_fetch.cc b/src/xenia/gpu/spirv_shader_translator_fetch.cc index 076a32258..5aa54c5b6 100644 --- a/src/xenia/gpu/spirv_shader_translator_fetch.cc +++ b/src/xenia/gpu/spirv_shader_translator_fetch.cc @@ -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(); diff --git a/src/xenia/gpu/texture_cache.cc b/src/xenia/gpu/texture_cache.cc index 027ce4366..8370cc383 100644 --- a/src/xenia/gpu/texture_cache.cc +++ b/src/xenia/gpu/texture_cache.cc @@ -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; diff --git a/src/xenia/gpu/xenos.h b/src/xenia/gpu/xenos.h index 9e1b42fe9..d93dc6fc6 100644 --- a/src/xenia/gpu/xenos.h +++ b/src/xenia/gpu/xenos.h @@ -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 ||