[GPU] Properly handle scaled textures and weights
Fix d3d12 logic wrt scaling weights, add vulkan impelementation for using scaled texture offsets and fix resolution-scaled texture detection
This commit is contained in:
@@ -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));
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <swcache::PrefetchTag tag>
|
||||
void PrefetchTextureBinding(uint32_t fetch_constant_index) const {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user