diff --git a/src/xenia/gpu/dxbc_shader_translator_fetch.cc b/src/xenia/gpu/dxbc_shader_translator_fetch.cc index aac480b19..cde7812b0 100644 --- a/src/xenia/gpu/dxbc_shader_translator_fetch.cc +++ b/src/xenia/gpu/dxbc_shader_translator_fetch.cc @@ -959,64 +959,19 @@ void DxbcShaderTranslator::ProcessTextureFetchInstruction( // would be useless to get weights that will always be zero. // Need unnormalized coordinates. + // Resolution scale is NOT applied for GetTextureWeights - weights are + // calculated from fractional parts of coordinates which should be in guest + // texel space, not host texel space. Games expect weights based on guest + // texture dimensions. bool coord_operand_temp_pushed = false; dxbc::Src coord_operand = LoadOperand(instr.operands[0], used_result_nonzero_components, coord_operand_temp_pushed); dxbc::Src coord_src(coord_operand); - // If needed, apply the resolution scale to the width / height and the - // unnormalized coordinates. - uint32_t resolution_scaled_result_components = - used_result_nonzero_components & revert_resolution_scale_axes; - uint32_t resolution_scaled_coord_components = - instr.attributes.unnormalized_coordinates - ? resolution_scaled_result_components - : 0b0000; - uint32_t resolution_scaled_size_components = - size_needed_components & resolution_scaled_result_components; - if (resolution_scaled_coord_components || - resolution_scaled_size_components) { - if (resolution_scaled_coord_components && - (coord_src.type_ != dxbc::OperandType::kTemp || - coord_src.index_1d_.index_ != system_temp_result_)) { - // Use system_temp_result_ as a temporary for conditionally - // resolution-scaled coordinates. - a_.OpMov( - dxbc::Dest::R(system_temp_result_, used_result_nonzero_components), - coord_src); - coord_src = dxbc::Src::R(system_temp_result_); - } - // Using system_temp_result_.w as a temporary for the flag indicating - // whether the texture was resolved - not involved in coordinate - // calculations. - assert_zero(used_result_nonzero_components & 0b1000); - a_.OpAnd(dxbc::Dest::R(system_temp_result_, 0b1000), - LoadSystemConstant(SystemConstants::Index::kTexturesResolved, - offsetof(SystemConstants, textures_resolved), - dxbc::Src::kXXXX), - dxbc::Src::LU(uint32_t(1) << tfetch_index)); - a_.OpIf(true, dxbc::Src::R(system_temp_result_, dxbc::Src::kWWWW)); - // The texture is resolved - scale the coordinates and the size. - dxbc::Src resolution_scale_src( - dxbc::Src::LF(float(draw_resolution_scale_x_), - float(draw_resolution_scale_y_), 1.0f, 1.0f)); - if (resolution_scaled_coord_components) { - a_.OpMul(dxbc::Dest::R(system_temp_result_, - resolution_scaled_coord_components), - coord_src, resolution_scale_src); - } - if (resolution_scaled_size_components) { - a_.OpMul(dxbc::Dest::R(size_and_is_3d_temp, - resolution_scaled_size_components), - dxbc::Src::R(size_and_is_3d_temp), resolution_scale_src); - } - a_.OpEndIf(); - } uint32_t offsets_needed = offsets_not_zero & used_result_nonzero_components; if (!instr.attributes.unnormalized_coordinates || offsets_needed) { // Using system_temp_result_ as a temporary for coordinate denormalization - // and offsetting. May already contain the coordinates loaded if - // resolution scaling was applied to the coordinates. + // and offsetting. coord_src = dxbc::Src::R(system_temp_result_); dxbc::Dest coord_dest( dxbc::Dest::R(system_temp_result_, used_result_nonzero_components)); diff --git a/src/xenia/gpu/spirv_shader_translator.cc b/src/xenia/gpu/spirv_shader_translator.cc index 810c7afd1..bdbda1fd5 100644 --- a/src/xenia/gpu/spirv_shader_translator.cc +++ b/src/xenia/gpu/spirv_shader_translator.cc @@ -264,6 +264,8 @@ void SpirvShaderTranslator::StartTranslation() { offsetof(SystemConstants, texture_swizzled_signs), type_uint4_array_2}, {"texture_swizzles", offsetof(SystemConstants, texture_swizzles), type_uint4_array_4}, + {"textures_resolved", offsetof(SystemConstants, textures_resolved), + type_uint_}, {"alpha_test_reference", offsetof(SystemConstants, alpha_test_reference), type_float_}, {"edram_32bpp_tile_pitch_dwords_scaled", diff --git a/src/xenia/gpu/spirv_shader_translator.h b/src/xenia/gpu/spirv_shader_translator.h index d4890d7de..d61885c91 100644 --- a/src/xenia/gpu/spirv_shader_translator.h +++ b/src/xenia/gpu/spirv_shader_translator.h @@ -34,7 +34,7 @@ class SpirvShaderTranslator : public ShaderTranslator { // TODO(Triang3l): Change to 0xYYYYMMDD once it's out of the rapid // prototyping stage (easier to do small granular updates with an // incremental counter). - static constexpr uint32_t kVersion = 6; + static constexpr uint32_t kVersion = 7; enum class DepthStencilMode : uint32_t { kNoModifiers, @@ -202,6 +202,10 @@ class SpirvShaderTranslator : public ShaderTranslator { // swizzles for 2 texture fetch constants (in bits 0:11 and 12:23). uint32_t texture_swizzles[16]; + // Whether the contents of each texture in fetch constants comes from a + // resolve operation (bit per texture, 32 textures max). + uint32_t textures_resolved; + float alpha_test_reference; uint32_t edram_32bpp_tile_pitch_dwords_scaled; uint32_t edram_depth_base_dwords_scaled; @@ -821,6 +825,7 @@ class SpirvShaderTranslator : public ShaderTranslator { kSystemConstantPointScreenDiameterToNdcRadius, kSystemConstantTextureSwizzledSigns, kSystemConstantTextureSwizzles, + kSystemConstantTexturesResolved, kSystemConstantAlphaTestReference, kSystemConstantEdram32bppTilePitchDwordsScaled, kSystemConstantEdramDepthBaseDwordsScaled, diff --git a/src/xenia/gpu/spirv_shader_translator_fetch.cc b/src/xenia/gpu/spirv_shader_translator_fetch.cc index 618631c06..0cb910279 100644 --- a/src/xenia/gpu/spirv_shader_translator_fetch.cc +++ b/src/xenia/gpu/spirv_shader_translator_fetch.cc @@ -16,6 +16,7 @@ #include "third_party/glslang/SPIRV/GLSL.std.450.h" #include "xenia/base/assert.h" #include "xenia/base/math.h" +#include "xenia/gpu/render_target_cache.h" #include "xenia/gpu/spirv_compatibility.h" namespace xe { @@ -1025,6 +1026,56 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( } } + // Check if this texture is from a resolution-scaled resolve operation. + // This affects both size and offset calculations. + spv::Id is_texture_resolved = spv::NoResult; + if (cvars::draw_resolution_scaled_texture_offsets && + (draw_resolution_scale_x_ > 1 || draw_resolution_scale_y_ > 1)) { + // Load textures_resolved from system constants. + id_vector_temp_.clear(); + id_vector_temp_.push_back( + builder_->makeIntConstant(kSystemConstantTexturesResolved)); + spv::Id textures_resolved = + builder_->createLoad(builder_->createAccessChain( + spv::StorageClassUniform, + uniform_system_constants_, id_vector_temp_), + spv::NoPrecision); + // Check if this texture is resolved: + // (textures_resolved >> fetch_constant_index) & 1 + assert_true(fetch_constant_index < 32); + is_texture_resolved = builder_->createBinOp( + spv::OpINotEqual, type_bool_, + builder_->createBinOp( + spv::OpBitwiseAnd, type_uint_, textures_resolved, + builder_->makeUintConstant(UINT32_C(1) << fetch_constant_index)), + const_uint_0_); + } + + // Scale the size for resolution-scaled textures. + // When a texture is from a resolve operation (scaled), its actual host + // dimensions are larger than the guest dimensions in the fetch constant. + // The size must be scaled so that coordinate normalization and offset + // calculations use the correct host dimensions. + if (is_texture_resolved != spv::NoResult && size_needed_components) { + if (size[0] != spv::NoResult && draw_resolution_scale_x_ > 1) { + spv::Id scaled_size_x = builder_->createNoContractionBinOp( + spv::OpFMul, type_float_, size[0], + builder_->makeFloatConstant(float(draw_resolution_scale_x_))); + size[0] = + builder_->createTriOp(spv::OpSelect, type_float_, + is_texture_resolved, scaled_size_x, size[0]); + } + if (size[1] != spv::NoResult && draw_resolution_scale_y_ > 1) { + spv::Id scaled_size_y = builder_->createNoContractionBinOp( + spv::OpFMul, type_float_, size[1], + builder_->makeFloatConstant(float(draw_resolution_scale_y_))); + size[1] = + builder_->createTriOp(spv::OpSelect, type_float_, + is_texture_resolved, scaled_size_y, size[1]); + } + // Z size is not scaled (depth/layers don't change with resolution). + } + // FIXME(Triang3l): Mip lerp factor needs to be calculated, and the // coordinate lerp factors should be calculated at the mip level texels // would be sampled from. That would require some way of calculating the @@ -1105,6 +1156,11 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( // kTextureFetch or kGetTextureComputedLod. // Normalize the XY coordinates, and apply the offset. + // When a texture is from a resolution-scaled resolve, offsets are in + // guest texels but the size is in host texels. We need to scale offsets + // to compensate: + // - For normalized coords: coord + (offset * scale) / size_scaled + // - For unnormalized coords: (coord + offset) * scale / size_scaled for (uint32_t i = 0; i <= uint32_t(instr.dimension != xenos::FetchOpDimension::k1D); ++i) { @@ -1118,15 +1174,49 @@ void SpirvShaderTranslator::ProcessTextureFetchInstruction( coordinate_ref = builder_->createNoContractionBinOp( spv::OpFAdd, type_float_, coordinate_ref, component_offset); } + // For resolution-scaled textures with unnormalized coords, we need + // to scale the coordinate (which now includes offset) before + // dividing by the scaled size. This ensures: + // (coord + offset) * scale / size_scaled = (coord + offset) / + // guest_size + if (is_texture_resolved != spv::NoResult && + ((i == 0 && draw_resolution_scale_x_ > 1) || + (i == 1 && draw_resolution_scale_y_ > 1))) { + float scale = (i == 0) ? float(draw_resolution_scale_x_) + : float(draw_resolution_scale_y_); + spv::Id scaled_coord = builder_->createNoContractionBinOp( + spv::OpFMul, type_float_, coordinate_ref, + builder_->makeFloatConstant(scale)); + coordinate_ref = builder_->createTriOp( + spv::OpSelect, type_float_, is_texture_resolved, scaled_coord, + coordinate_ref); + } assert_true(size_component != spv::NoResult); coordinate_ref = builder_->createNoContractionBinOp( spv::OpFDiv, type_float_, coordinate_ref, size_component); } else { if (component_offset != spv::NoResult) { assert_true(size_component != spv::NoResult); + // For resolution-scaled textures with normalized coords, scale the + // offset before normalizing. This ensures: + // coord + (offset * scale) / size_scaled = coord + offset / + // guest_size + spv::Id effective_offset = component_offset; + if (is_texture_resolved != spv::NoResult && + ((i == 0 && draw_resolution_scale_x_ > 1) || + (i == 1 && draw_resolution_scale_y_ > 1))) { + float scale = (i == 0) ? float(draw_resolution_scale_x_) + : float(draw_resolution_scale_y_); + spv::Id scaled_offset = builder_->createNoContractionBinOp( + spv::OpFMul, type_float_, component_offset, + builder_->makeFloatConstant(scale)); + effective_offset = builder_->createTriOp( + spv::OpSelect, type_float_, is_texture_resolved, + scaled_offset, component_offset); + } spv::Id component_offset_normalized = builder_->createNoContractionBinOp( - spv::OpFDiv, type_float_, component_offset, size_component); + spv::OpFDiv, type_float_, effective_offset, size_component); coordinate_ref = builder_->createNoContractionBinOp( spv::OpFAdd, type_float_, coordinate_ref, component_offset_normalized); diff --git a/src/xenia/gpu/texture_cache.h b/src/xenia/gpu/texture_cache.h index 381adbb3f..a66e3af75 100644 --- a/src/xenia/gpu/texture_cache.h +++ b/src/xenia/gpu/texture_cache.h @@ -143,8 +143,13 @@ class TextureCache { if (!binding) { return false; } - return (binding->texture && binding->texture->IsResolved()) || - (binding->texture_signed && binding->texture_signed->IsResolved()); + // Check if the texture is a resolution-scaled resolve target, not just + // any resolved texture. Only scaled textures need coordinate adjustment. + // Must check the texture's key, not binding->key, because scaled_resolve + // is set in FindOrCreateTexture which takes the key by value. + return (binding->texture && binding->texture->key().scaled_resolve) || + (binding->texture_signed && + binding->texture_signed->key().scaled_resolve); } template void PrefetchTextureBinding(uint32_t fetch_constant_index) const { diff --git a/src/xenia/gpu/vulkan/vulkan_command_processor.cc b/src/xenia/gpu/vulkan/vulkan_command_processor.cc index fb3378bd1..fa1f39d00 100644 --- a/src/xenia/gpu/vulkan/vulkan_command_processor.cc +++ b/src/xenia/gpu/vulkan/vulkan_command_processor.cc @@ -4151,6 +4151,21 @@ void VulkanCommandProcessor::UpdateSystemConstantValues( } } + // Textures resolved - which textures are from resolve operations (scaled). + { + uint32_t textures_resolved = 0; + uint32_t textures_remaining = used_texture_mask; + uint32_t texture_index; + while (xe::bit_scan_forward(textures_remaining, &texture_index)) { + textures_remaining &= ~(UINT32_C(1) << texture_index); + textures_resolved |= + uint32_t(texture_cache_->IsActiveTextureResolved(texture_index)) + << texture_index; + } + dirty |= system_constants_.textures_resolved != textures_resolved; + system_constants_.textures_resolved = textures_resolved; + } + // Alpha test. dirty |= system_constants_.alpha_test_reference != rb_alpha_ref; system_constants_.alpha_test_reference = rb_alpha_ref;