Merge commit '9535e610b0996e0b29d19d6a2ad788edba7c2c63' into edge

This commit is contained in:
Herman S.
2026-01-15 10:28:52 +09:00
27 changed files with 324 additions and 255 deletions
+2 -7
View File
@@ -1791,13 +1791,8 @@ bool D3D12TextureCache::LoadTextureDataFromResidentMemoryImpl(Texture& texture,
}
const texture_util::TextureGuestLayout::Level& level_guest_layout =
is_base ? guest_layout.base : guest_layout.mips[level];
uint32_t level_guest_pitch = level_guest_layout.row_pitch_bytes;
if (texture_key.tiled) {
// Shaders expect pitch in blocks for tiled textures.
level_guest_pitch /= bytes_per_block;
assert_zero(level_guest_pitch & (xenos::kTextureTileWidthHeight - 1));
}
load_constants.guest_pitch_aligned = level_guest_pitch;
load_constants.guest_pitch_aligned =
level_guest_layout.row_pitch_bytes / bytes_per_block;
load_constants.guest_z_stride_block_rows_aligned =
level_guest_layout.z_slice_stride_block_rows;
assert_true(dimension != xenos::DataDimension::k3D ||
+46 -32
View File
@@ -79,8 +79,8 @@ struct XeResolveInfo {
uint dest_format;
float dest_exp_bias_factor;
bool dest_swap;
uint dest_row_pitch_aligned_shr5;
uint dest_slice_pitch_aligned_shr4;
uint dest_row_pitch_macro_tiles;
uint dest_slice_pitch_3d_macro_tiles;
uint2_xe dest_xy_offset_scaled;
uint sample_select;
uint dest_base;
@@ -132,10 +132,11 @@ XeResolveInfo XeResolveGetInfo(param_push_consts_xe) {
(int(dest_info) << (32 - (16 + 6)) >> (32 - 6) << 23) +
float_bits_to_int_xe(1.0f));
resolve_info.dest_swap = (dest_info & (1u << 24u)) != 0u;
resolve_info.dest_row_pitch_aligned_shr5 =
resolve_info.dest_row_pitch_macro_tiles =
dest_coordinate_info & ((1u << 10u) - 1u);
resolve_info.dest_slice_pitch_aligned_shr4 =
((dest_coordinate_info >> 10u) & ((1u << 10u) - 1u)) << 1u;
resolve_info.dest_slice_pitch_3d_macro_tiles =
((dest_coordinate_info >> 10u) & ((1u << 10u) - 1u)) <<
(5 - XENOS_TEXTURE_MACRO_TILE_HEIGHT_3D_LOG2);
resolve_info.dest_xy_offset_scaled =
(((uint_x2_xe(dest_coordinate_info) >> uint2_xe(20u, 24u)) &
((1u << 4u) - 1u)) <<
@@ -158,41 +159,54 @@ uint XeResolveEdramPixelStrideInts(XeResolveInfo resolve_info) {
}
#ifndef XE_RESOLVE_CLEAR
uint XeResolveDestPixelAddress(XeResolveInfo resolve_info, uint2_xe p,
uint bpp_log2) {
p += resolve_info.dest_xy_offset_scaled;
uint XeResolveDestPixelAddress(const XeResolveInfo resolve_info,
uint2_xe host_position,
const uint bytes_per_element_log2) {
host_position += resolve_info.dest_xy_offset_scaled;
uint address;
uint2_xe guest_position = host_position;
#ifdef XE_RESOLVE_RESOLUTION_SCALED
address = XeTextureScaledTiledOffset(
resolve_info.dest_is_array, uint3_xe(p, resolve_info.dest_slice),
resolve_info.dest_row_pitch_aligned_shr5,
resolve_info.dest_slice_pitch_aligned_shr4, bpp_log2,
resolve_info.resolution_scale);
#else
dont_flatten_xe if (resolve_info.dest_is_array) {
address = uint(XenosTextureAddressTiled3D(
int3_xe(uint3_xe(p, resolve_info.dest_slice)),
resolve_info.dest_row_pitch_aligned_shr5,
resolve_info.dest_slice_pitch_aligned_shr4,
bpp_log2));
} else {
address = uint(XenosTextureAddressTiled2D(
int2_xe(p), resolve_info.dest_row_pitch_aligned_shr5, bpp_log2));
}
address += resolve_info.dest_base;
const XeniaTextureResolutionScaledAddressing
resolution_scaled_addressing =
XeniaTextureGetResolutionScaledAddressing(
host_position.xy, resolve_info.resolution_scale,
bytes_per_element_log2);
guest_position = resolution_scaled_addressing.guest_group_origin;
#endif
dont_flatten_xe if (resolve_info.dest_is_array) {
address = uint(XenosTextureTiledAddress3D(
int3_xe(uint3_xe(guest_position, resolve_info.dest_slice)),
resolve_info.dest_row_pitch_macro_tiles,
resolve_info.dest_slice_pitch_3d_macro_tiles,
bytes_per_element_log2));
} else {
address = uint(XenosTextureTiledAddress2D(
int2_xe(guest_position), resolve_info.dest_row_pitch_macro_tiles,
bytes_per_element_log2));
}
#ifdef XE_RESOLVE_RESOLUTION_SCALED
address = address * (resolve_info.resolution_scale.x *
resolve_info.resolution_scale.y) +
resolution_scaled_addressing.host_byte_offset_in_guest_group;
#endif
address += resolve_info.dest_base;
return address;
}
// Offset of the beginning of the odd R32G32/R32G32B32A32 store address from
// the address of the even store.
uint XeResolveDestRightConsecutiveBlocksOffset(uint x, uint bpp_log2,
uint2_xe resolution_scale) {
// XOR to apply to the byte address to flip the bits corresponding to the
// given X coordinate bits within the macro tile width, or with resolution
// scaling, within XeniaTextureResolutionScaledGroupElements.x.
// Addition is recommended instead of XOR if the bits are known to be 0 in the
// original address, so the host GPU driver can optimize it into a constant
// store offset if available in the host hardware shader instruction set
// architecture.
uint XeResolveLocalXAddressXor(const uint x,
const uint bytes_per_element_log2) {
#ifdef XE_RESOLVE_RESOLUTION_SCALED
return XeTextureScaledRightSubUnitOffsetInConsecutivePair(
x, bpp_log2, resolution_scale);
return x << bytes_per_element_log2;
#else
return XeTextureTiledOddConsecutiveBlocksOffset(bpp_log2);
return uint(XenosTextureTiledAddressXInMacroXor(int(x),
bytes_per_element_log2));
#endif
}
@@ -66,8 +66,7 @@ entry_inputs_end_code_begin_compute_xe
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap32(pixels_0123, resolve_info.dest_endian_128));
dest_address += XeResolveDestRightConsecutiveBlocksOffset(
pixel_index.x, 2u, resolve_info.resolution_scale) >> 4u;
dest_address += XeResolveLocalXAddressXor(4u, 2u) >> 4u;
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap32(pixels_4567, resolve_info.dest_endian_128));
@@ -84,8 +84,7 @@ entry_inputs_end_code_begin_compute_xe
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap32(pixels_0123, resolve_info.dest_endian_128));
dest_address += XeResolveDestRightConsecutiveBlocksOffset(
pixel_index.x, 2u, resolve_info.resolution_scale) >> 4u;
dest_address += XeResolveLocalXAddressXor(4u, 2u) >> 4u;
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap32(pixels_4567, resolve_info.dest_endian_128));
@@ -66,8 +66,7 @@ entry_inputs_end_code_begin_compute_xe
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap64(pixels_01, resolve_info.dest_endian_128));
dest_address += XeResolveDestRightConsecutiveBlocksOffset(
pixel_index.x, 3u, resolve_info.resolution_scale) >> 4u;
dest_address += XeResolveLocalXAddressXor(2u, 3u) >> 4u;
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap64(pixels_23, resolve_info.dest_endian_128));
@@ -70,8 +70,7 @@ entry_inputs_end_code_begin_compute_xe
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap64(pixels_01, resolve_info.dest_endian_128));
dest_address += XeResolveDestRightConsecutiveBlocksOffset(
pixel_index.x, 3u, resolve_info.resolution_scale) >> 4u;
dest_address += XeResolveLocalXAddressXor(2u, 3u) >> 4u;
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap64(pixels_23, resolve_info.dest_endian_128));
@@ -62,8 +62,7 @@ entry_inputs_end_code_begin_compute_xe
xe_resolve_dest, dest_address,
XeEndianSwap128(float_bits_to_uint_xe(pixel_0),
resolve_info.dest_endian_128));
dest_address += XeResolveDestRightConsecutiveBlocksOffset(
pixel_index.x, 4u, resolve_info.resolution_scale) >> 4u;
dest_address += XeResolveLocalXAddressXor(1u, 4u) >> 4u;
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap128(float_bits_to_uint_xe(pixel_1),
@@ -62,8 +62,7 @@ entry_inputs_end_code_begin_compute_xe
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap64(packed_01, resolve_info.dest_endian_128));
dest_address += XeResolveDestRightConsecutiveBlocksOffset(
pixel_index.x, 3u, resolve_info.resolution_scale) >> 4u;
dest_address += XeResolveLocalXAddressXor(2u, 3u) >> 4u;
array_buffer_store_xe(
xe_resolve_dest, dest_address,
XeEndianSwap64(packed_23, resolve_info.dest_endian_128));
+165 -116
View File
@@ -12,7 +12,12 @@
#include "../../ui/shaders/xesl.xesli"
int XenosTextureAddressTiledCombine(const int outer_inner_bytes, const int bank,
#define XENOS_TEXTURE_MACRO_TILE_WIDTH_LOG2 5
#define XENOS_TEXTURE_MACRO_TILE_HEIGHT_2D_LOG2 5
#define XENOS_TEXTURE_MACRO_TILE_HEIGHT_3D_LOG2 4
#define XENOS_TEXTURE_MACRO_TILE_DEPTH_LOG2 2
int XenosTextureTiledAddressCombine(const int outer_inner_bytes, const int bank,
const int pipe, const int y_lsb) {
return (y_lsb << 4) | (pipe << 6) | (bank << 11) | (outer_inner_bytes & 0xF) |
(((outer_inner_bytes >> 4) & 0x1) << 5) |
@@ -20,26 +25,31 @@ int XenosTextureAddressTiledCombine(const int outer_inner_bytes, const int bank,
(outer_inner_bytes >> 8 << 12);
}
int XenosTextureAddressTiled2D(const int2_xe p, const uint pitch_aligned_shr5,
int XenosTextureTiledAddress2D(const int2_xe p, const uint pitch_macro_tiles,
const uint bytes_per_element_log2) {
const int outer_elements =
((p.y >> 5) * int(pitch_aligned_shr5) + (p.x >> 5)) << 6;
((p.y >> XENOS_TEXTURE_MACRO_TILE_HEIGHT_2D_LOG2) *
int(pitch_macro_tiles) +
(p.x >> XENOS_TEXTURE_MACRO_TILE_WIDTH_LOG2))
<< 6;
const int inner_elements = (((p.y >> 1) & 0x7) << 3) | (p.x & 0x7);
const int outer_inner_bytes =
(outer_elements | inner_elements) << bytes_per_element_log2;
const int bank = (p.y >> 4) & 0x1;
const int pipe = ((p.x >> 3) & 0x3) ^ (((p.y >> 3) & 0x1) << 1);
return XenosTextureAddressTiledCombine(outer_inner_bytes, bank, pipe,
return XenosTextureTiledAddressCombine(outer_inner_bytes, bank, pipe,
p.y & 1);
}
int XenosTextureAddressTiled3D(const int3_xe p, const uint pitch_aligned_shr5,
const uint height_aligned_shr4,
int XenosTextureTiledAddress3D(const int3_xe p, const uint pitch_macro_tiles,
const uint height_macro_tiles,
const uint bytes_per_element_log2) {
const int outer_elements =
((((p.z >> 2) * int(height_aligned_shr4) + (p.y >> 4)) *
int(pitch_aligned_shr5)) +
(p.x >> 5))
((((p.z >> XENOS_TEXTURE_MACRO_TILE_DEPTH_LOG2) *
int(height_macro_tiles) +
(p.y >> XENOS_TEXTURE_MACRO_TILE_HEIGHT_3D_LOG2)) *
int(pitch_macro_tiles)) +
(p.x >> XENOS_TEXTURE_MACRO_TILE_WIDTH_LOG2))
<< 7;
const int inner_elements =
((p.z & 0x3) << 5) | (((p.y >> 1) & 0x3) << 3) | (p.x & 0x7);
@@ -47,122 +57,161 @@ int XenosTextureAddressTiled3D(const int3_xe p, const uint pitch_aligned_shr5,
(outer_elements | inner_elements) << bytes_per_element_log2;
const int bank = ((p.y >> 3) ^ (p.z >> 2)) & 0x1;
const int pipe = ((p.x >> 3) & 0x3) ^ (bank << 1);
return XenosTextureAddressTiledCombine(outer_inner_bytes, bank, pipe,
return XenosTextureTiledAddressCombine(outer_inner_bytes, bank, pipe,
p.y & 1);
}
// Log2 of the number of blocks always laid out consecutively in memory along
// the horizontal axis.
uint XeTextureTiledConsecutiveBlocksLog2(uint bpb_log2) {
// 1bpb and 2bpb - 8.
// 4bpb - 4.
// 8bpb - 2.
// 16bpb - 1.
return min(4u - bpb_log2, 3u);
// XOR to apply to the tiled address to flip the bits corresponding to the given
// X coordinate bits within the width of a macro tile.
// Note that in a tiled address, bit 7 is X[4] ^ Y[3] ^ Z[2], not X[4] alone.
int XenosTextureTiledAddressXInMacroXor(const int x,
const uint bytes_per_element_log2) {
return XenosTextureTiledAddressCombine((x & 0x7) << bytes_per_element_log2,
0, (x >> 3) & 0x3, 0);
}
// Odd sequences of consecutive blocks along the horizontal axis are placed at a
// fixed offset in memory from the preceding even ones. Returns the distance
// between the beginnings of the even and its corresponding odd sequences.
uint XeTextureTiledOddConsecutiveBlocksOffset(uint bpb_log2) {
return bpb_log2 >= 2u ? 32u : 64u;
// The lowest bits of an element index within a micro tile are X[2:0].
// In a tiled address, the bit 4 is always Y[0].
// However, the bits [3:0] are the lower bits of the micro tile element index
// times the number of bytes per element.
// Because of this, a number of elements, that depends on the count of bytes per
// element, along the X axis (aligned to this amount) is stored consecutively in
// guest memory:
// - 1bpe: 8 elements (8 bytes - limited by address bit 3 being Y[1] for 1bpe).
// - 2bpe: 8 elements (16 bytes - limited by address bit 4 always being Y[0]).
// - 4bpe: 4 elements.
// - 8bpe: 2 elements.
// - 16bpe: 1 element.
// This makes it possible to access multiple elements in a single row using
// 8-byte or (for >= 2bpe) 16-byte loads and stores, and that's particularly
// useful when transferring texture data between tiled and linear storage.
// With resolution scaling, one scaled group of bytes in guest addresses
// corresponds to `scale.x * scale.y` groups of the same size on the host.
//
// A single group contains a full rectangular region of elements. This means
// that, for instance, if the Y[1] tiled address bit is within the group size,
// Y[0] must be within it too, so division is enough to go from host to guest
// coordinates for the origin of the group.
//
// The address of the guest group on the host is the guest tiled address of its
// origin in guest coordinates multiplied by `scale.x * scale.y`.
//
// Within a guest group, the addressing of elements is controlled by the host.
// Specifically, host groups are arranged in a guest group as block-linear
// column-major (for storage locality along both axes), and elements in a host
// group are laid out as linear row-major (guest tiling therefore is applied
// only to whole guest groups, not within them, for simplicity).
//
// Addressing with resolution scaling is not intended to allow for
// reinterpretation of resolution-scaled data between different numbers of bits
// per element. Rather, it's designed for simple and efficient access on the
// host, primarily when copying between tiled and linear storage, and to reduce
// the differences in shader logic between unscaled and scaled data.
//
// However, the groups are still small enough to preserve most of the tiling
// properties on a macro level, most importantly the possibility to resolve
// EDRAM render target regions to textures at different destination offsets.
//
// Also, the group sizes are selected to make resolution scaling calculations
// work the same for 2D and 3D textures, and also mostly position-independent -
// in particular, the bit 7 of a tiled address, which depends on Y[3] and Z[2],
// is never within the group size. This allows, for example, for downsampling of
// resolution-scaled data in a memory range to be done with the number of bytes
// per pixel being the only needed metadata.
//
// A common pattern in Xenia is copying multiple 8-byte or (for >= 2bpe) 16-byte
// sequences of consecutive elements along the X axis in a single shader
// invocation, by computing the tiled address once and merely flipping X bits in
// it.
//
// With the resolution scaling group size being no larger than 2^7 bytes, it may
// contain guest X bits [3:0] for <= 4bpe, [1:0] for 8bpe, and [0] for 16bpe
// (note that though X[3] always goes to address[6], for 8bpe, X[2] is
// address[8], so a group can't be wider than 4 elements, and similarly for X[1]
// for 16bpe).
//
// Given these requirements, the group sizes are chosen as follows:
// - 1bpe - lower 7 bits of an unscaled address are X0, X1, X2, Y1, Y0, Y2, X3:
// - Group width: 2^4 elements (maximum within 7 bits), or 2^4 bytes.
// - Group height: 2^3 elements (Y[2:0] between X[3:0]).
// - 2bpe - lower 7 bits of an unscaled address are 0, X0, X1, X2, Y0, Y1, X3:
// - Group width: 2^4 elements (maximum within 7 bits), or 2^5 bytes.
// - Group height: 2^2 elements (Y[1:0] between X[3:0]).
// - 4bpe - lower 7 bits of an unscaled address are 0, 0, X0, X1, Y0, X2, X3:
// - Group width: 2^4 elements (maximum within 7 bits), or 2^6 bytes.
// - Group height: 2^1 elements (Y[0] between X[3:0]).
// - 8bpe - lower 7 bits of an unscaled address are 0, 0, 0, X0, Y0, X1, X3:
// - Group width: 2^2 elements (X[2] is beyond 7 bits), or 2^5 bytes.
// - Group height: 2^1 elements (Y[0] between X[1:0]).
// - 16bpe - lower 7 bits of an unscaled address are 0, 0, 0, 0, Y0, X0, X3:
// - Group width: 2^1 elements (X[2:1] is beyond 7 bits), or 2^5 bytes.
// - Group height: 2^1 elements (Y[0] below X[0]).
//
// 2^6 bytes copied per invocation is likely to be optimal, as that consumes 16
// 32-bit VGPRs, out of a total of 24 (1024 / 40 rounded down to 4) available
// without VGPR usage becoming a theoretical occupancy limit on AMD GCN
// (although the occupancy of copy shaders is likely to be limited by memory
// accesses instead anyway).
//
// Note that with the given group sizes, as well as with elements in a host
// group stored as row-major, for 1bpe, 16x1 host elements are stored
// consecutively with resolution scaling (even though in guest tiling, only 8x1
// elements are), so they can be accessed via one 16-byte operation rather than
// two 8-byte ones.
// Expected to be called for a compile-time constant.
uint2_xe XeniaTextureResolutionScaledGroupElementsLog2(
const uint bytes_per_element_log2) {
// Based on the tiled address properties, see the comment above for details.
return uint2_xe(
bytes_per_element_log2 >= 3u ? 5u - bytes_per_element_log2 : 4u,
3u - min(bytes_per_element_log2, 2u));
}
// For shaders to be able to copy multiple horizontally adjacent pixels in the
// same way regardless of the resolution scale chosen, scaling is done at Nx1
// granularity where N matches the number of pixels that are consecutive with
// guest tiling, rather than within individual guest pixels:
// - 1bpp - 8x1 host pixels (can copy via R32G32_UINT)
// - 2bpp - 8x1 host pixels (can copy via R32G32B32A32_UINT)
// - 4bpp - 4x1 host pixels
// - 8bpp - 2x1 host pixels
// - 16bpp - 1x1 host pixels
// For better access locality, because compute shaders in Xenia usually have 2D
// thread groups, host Nx1 sub-units are scaled within guest Nx1 units in a
// column-major way.
// So, for example, in a 2bpp texture with 2x2 resolution scale, 16 guest bytes,
// or 64 host bytes, contain:
// - 16 host bytes - 8x1 top-left portion
// - 16 host bytes - 8x1 bottom-left portion
// - 16 host bytes - 8x1 top-right portion
// - 16 host bytes - 8x1 bottom-right portion
// This function is used only for non-negative positions within a texture, so
// for simplicity, especially of the division involved, assuming everything is
// unsigned.
uint XeTextureScaledTiledOffset(bool is_3d, uint3_xe p, uint pitch_aligned_shr5,
uint height_aligned_shr4, uint bpb_log2,
uint2_xe scale) {
uint unit_width_log2 = XeTextureTiledConsecutiveBlocksLog2(bpb_log2);
// Global host X coordinate in host Nx1 sub-units.
uint x_subunits = p.x >> unit_width_log2;
// Global guest XY coordinate in guest Nx1 units.
uint2_xe xy_unit_guest = uint2_xe(x_subunits, p.y) / scale;
// Global guest XYZ coordinate of the beginning of the Nx1 unit.
uint3_xe unit_guest_origin =
uint3_xe(xy_unit_guest.x << unit_width_log2, xy_unit_guest.y, p.z);
// Global guest linear address of the beginning of Nx1 unit in bytes.
uint unit_guest_address;
dont_flatten_xe if (is_3d) {
unit_guest_address = uint(XenosTextureAddressTiled3D(
int3_xe(unit_guest_origin), pitch_aligned_shr5, height_aligned_shr4,
bpb_log2));
} else {
unit_guest_address = uint(XenosTextureAddressTiled2D(
int2_xe(unit_guest_origin.xy), pitch_aligned_shr5, bpb_log2));
}
// Unit-local host XY index of the host Nx1 sub-unit.
// Also see XeTextureScaledRightSubUnitOffsetInConsecutivePair for common
// subexpression elimination information as this remainder calculation is done
// there too.
uint2_xe unit_subunit = uint2_xe(x_subunits, p.y) - xy_unit_guest * scale;
// Combine:
// - Guest global unit address.
// - Host unit-local sub-unit index.
// - Host pixel within a sub-unit (if the offset is requested at a smaller
// granularity than a whole sub-unit).
return unit_guest_address * (scale.x * scale.y) +
((((unit_subunit.x * scale.y + unit_subunit.y) << unit_width_log2) +
(p.x & ((1u << unit_width_log2) - 1u)))
<< bpb_log2);
}
struct XeniaTextureResolutionScaledAddressing {
uint2_xe guest_group_origin;
uint host_byte_offset_in_guest_group;
};
// Offset of the beginning of next host sub-unit along the horizontal axis
// within a pair of guest units.
// x must be a multiple of 1 << (XeTextureTiledConsecutiveBlocksLog2 + 1) - to
// go from one pair of consecutive blocks to another, full tiled offset
// recalculation is required.
uint XeTextureScaledRightSubUnitOffsetInConsecutivePair(uint x, uint bpb_log2,
uint2_xe scale) {
uint right_sub_unit_offset_columns;
uint tiled_consecutive_offset =
XeTextureTiledOddConsecutiveBlocksOffset(bpb_log2);
dont_flatten_xe if (scale.x > 1u) {
uint subunit_width_log2 = XeTextureTiledConsecutiveBlocksLog2(bpb_log2);
uint subunit_size_log2 = subunit_width_log2 + bpb_log2;
// While % can be used here to take the modulo, for better common
// subexpression elimination between this function and
// XeTextureScaledTiledOffset when both are used, taking the remainder the
// same way.
uint x_subunits = x >> subunit_width_log2;
uint unit_subunit_x = x_subunits - (x_subunits / scale.x) * scale.x;
if (unit_subunit_x + 1u == scale.x) {
// The next host sub-unit is in the other, odd guest unit.
right_sub_unit_offset_columns = tiled_consecutive_offset * scale.x -
(unit_subunit_x << subunit_size_log2);
} else {
// The next host sub-unit is in the same guest unit.
right_sub_unit_offset_columns = 1u << subunit_size_log2;
}
} else {
right_sub_unit_offset_columns = tiled_consecutive_offset;
}
// The layout of sub-units within one unit is column-major.
return right_sub_unit_offset_columns * scale.y;
}
XeniaTextureResolutionScaledAddressing
XeniaTextureGetResolutionScaledAddressing(const uint2_xe position,
const uint2_xe resolution_scale,
const uint bytes_per_element_log2) {
XeniaTextureResolutionScaledAddressing addressing;
int XeTextureGuestLinearOffset(int3_xe p, uint pitch, uint height_aligned,
uint bpb) {
return p.x * int(bpb) + (p.z * int(height_aligned) + p.y) * int(pitch);
const uint2_xe group_elements_log2 =
XeniaTextureResolutionScaledGroupElementsLog2(bytes_per_element_log2);
const uint2_xe host_group_id_in_texture = position >> group_elements_log2;
const uint2_xe guest_group_id_in_texture =
host_group_id_in_texture / resolution_scale;
const uint2_xe host_group_id_in_guest_group =
host_group_id_in_texture - resolution_scale * guest_group_id_in_texture;
addressing.guest_group_origin =
guest_group_id_in_texture << group_elements_log2;
// Host groups are stored as column-major in a guest group, but this can be
// changed freely.
const uint host_group_index_in_guest_group =
host_group_id_in_guest_group.x * resolution_scale.y +
host_group_id_in_guest_group.y;
// Shifts are expanded rather than chained because the number of bytes per
// element, and thus also the group size, are expected to be compile-time
// constants, so this is expected to be combined using GPU bitfield insert
// instructions.
const uint group_width_bytes_log2 =
group_elements_log2.x + bytes_per_element_log2;
const uint2_xe position_in_host_group =
position & ((uint_x2_xe(1u) << group_elements_log2) - 1u);
addressing.host_byte_offset_in_guest_group =
(host_group_index_in_guest_group <<
(group_width_bytes_log2 + group_elements_log2.y)) |
(position_in_host_group.y << group_width_bytes_log2) |
(position_in_host_group.x << bytes_per_element_log2);
return addressing;
}
int XeTextureHostLinearOffset(int3_xe p, uint pitch, uint height, uint bpb) {
+64 -49
View File
@@ -30,8 +30,7 @@ push_const_begin_xe(b0, space0)
uint xe_texture_load_is_tiled_3d_endian_scale;
// Base offset in bytes, resolution-scaled.
uint xe_texture_load_guest_offset;
// For tiled textures - row pitch in guest blocks, aligned to 32, unscaled.
// For linear textures - row pitch in bytes.
// Unscaled.
uint xe_texture_load_guest_pitch_aligned;
// For 3D textures only (ignored otherwise) - aligned to 32, unscaled.
uint xe_texture_load_guest_z_stride_block_rows_aligned;
@@ -97,57 +96,73 @@ XeTextureLoadInfo XeTextureLoadGetInfo(param_push_consts_xe) {
return load_info;
}
// bpb and bpb_log2 are separate because bpb may be not a power of 2 (like 96).
uint XeTextureLoadGuestBlockOffset(XeTextureLoadInfo load_info,
uint3_xe block_index, uint bpb,
uint bpb_log2) {
#ifdef XE_TEXTURE_LOAD_RESOLUTION_SCALED
// Only resolved textures can be resolution-scaled, and resolving is only
// possible to a tiled destination.
return load_info.guest_offset +
XeTextureScaledTiledOffset(
load_info.is_3d, block_index, load_info.guest_pitch_aligned >> 5,
load_info.guest_z_stride_block_rows_aligned >> 4, bpb_log2,
load_info.resolution_scale);
#else
int block_offset_guest;
dont_flatten_xe if (load_info.is_tiled) {
dont_flatten_xe if (load_info.is_3d) {
block_offset_guest = XenosTextureAddressTiled3D(
int3_xe(block_index), load_info.guest_pitch_aligned >> 5,
load_info.guest_z_stride_block_rows_aligned >> 4, bpb_log2);
} else {
block_offset_guest = XenosTextureAddressTiled2D(
int2_xe(block_index.xy), load_info.guest_pitch_aligned >> 5,
bpb_log2);
}
uint XeTextureLoadSourceAddress(const XeTextureLoadInfo load_info,
const uint3_xe host_position,
const uint bytes_per_element_log2) {
uint address;
uint3_xe guest_position = host_position;
#ifdef XE_TEXTURE_LOAD_RESOLUTION_SCALED
const XeniaTextureResolutionScaledAddressing resolution_scaled_addressing =
XeniaTextureGetResolutionScaledAddressing(host_position.xy,
load_info.resolution_scale,
bytes_per_element_log2);
guest_position.xy = resolution_scaled_addressing.guest_group_origin;
#else
dont_flatten_xe if (!load_info.is_tiled) {
address = (guest_position.x +
load_info.guest_pitch_aligned *
(guest_position.y +
load_info.guest_z_stride_block_rows_aligned *
guest_position.z)) <<
bytes_per_element_log2;
} else
#endif
{
dont_flatten_xe if (load_info.is_3d) {
address = uint(XenosTextureTiledAddress3D(
int3_xe(guest_position),
load_info.guest_pitch_aligned >> XENOS_TEXTURE_MACRO_TILE_WIDTH_LOG2,
load_info.guest_z_stride_block_rows_aligned >>
XENOS_TEXTURE_MACRO_TILE_HEIGHT_3D_LOG2,
bytes_per_element_log2));
} else {
block_offset_guest = XeTextureGuestLinearOffset(
int3_xe(block_index), load_info.guest_pitch_aligned,
load_info.guest_z_stride_block_rows_aligned, bpb);
address = uint(XenosTextureTiledAddress2D(
int2_xe(guest_position.xy),
load_info.guest_pitch_aligned >> XENOS_TEXTURE_MACRO_TILE_WIDTH_LOG2,
bytes_per_element_log2));
}
return uint(int(load_info.guest_offset) + block_offset_guest);
#endif
}
#ifdef XE_TEXTURE_LOAD_RESOLUTION_SCALED
address =
address * (load_info.resolution_scale.x * load_info.resolution_scale.y) +
resolution_scaled_addressing.host_byte_offset_in_guest_group;
#endif
address += load_info.guest_offset;
return address;
}
// Offset of the beginning of the odd R32G32/R32G32B32A32 load address from the
// address of the even load, for power-of-two-sized textures.
uint XeTextureLoadRightConsecutiveBlocksOffset(XeTextureLoadInfo load_info,
uint block_x, uint bpb_log2) {
#ifdef XE_TEXTURE_LOAD_RESOLUTION_SCALED
return XeTextureScaledRightSubUnitOffsetInConsecutivePair(
block_x, bpb_log2, load_info.resolution_scale);
#else
uint offset;
uint consecutive_blocks_log2 =
XeTextureTiledConsecutiveBlocksLog2(bpb_log2);
dont_flatten_xe if (load_info.is_tiled) {
offset = XeTextureTiledOddConsecutiveBlocksOffset(bpb_log2);
} else {
offset = 1u << (consecutive_blocks_log2 + bpb_log2);
}
return offset;
#endif
// XOR to apply to the byte address to flip the bits corresponding to the given
// X coordinate bits within:
// - Resolution-scaled tiled: XeniaTextureResolutionScaledGroupElements.x;
// - Unscaled tiled: macro tile width;
// - Linear: 256 bytes.
// Addition is recommended instead of XOR if the bits are known to be 0 in the
// original address, so the host GPU driver can optimize it into a constant load
// offset if available in the host hardware shader instruction set architecture.
uint XeTextureLoadLocalXAddressXor(const uint x,
const uint bytes_per_element_log2,
const bool is_tiled) {
uint x_address_xor;
#ifndef XE_TEXTURE_LOAD_RESOLUTION_SCALED
dont_flatten_xe if (is_tiled) {
x_address_xor = uint(
XenosTextureTiledAddressXInMacroXor(int(x), bytes_per_element_log2));
} else
#endif
{
x_address_xor = x << bytes_per_element_log2;
}
return x_address_xor;
}
#endif // XENIA_GPU_SHADERS_TEXTURE_LOAD_XESLI_
@@ -35,7 +35,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.size_blocks.y, 16u) +
load_info.host_offset) >> 4u);
uint block_offset_guest =
XeTextureLoadGuestBlockOffset(load_info, block_index, 16u, 4u) >> 4u;
XeTextureLoadSourceAddress(load_info, block_index, 4u) >> 4u;
array_buffer_store_xe(
xe_texture_load_dest, block_offset_host,
XeEndianSwap32(array_buffer_load_xe(xe_texture_load_source,
@@ -43,8 +43,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.endian_32));
++block_offset_host;
block_offset_guest +=
XeTextureLoadRightConsecutiveBlocksOffset(load_info, block_index.x, 4u) >>
4u;
XeTextureLoadLocalXAddressXor(1u, 4u, load_info.is_tiled) >> 4u;
array_buffer_store_xe(
xe_texture_load_dest, block_offset_host,
XeEndianSwap32(array_buffer_load_xe(xe_texture_load_source,
@@ -36,7 +36,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.size_blocks.y, 2u) +
load_info.host_offset) >> 4u);
uint block_offset_guest =
XeTextureLoadGuestBlockOffset(load_info, block_index, 2u, 1u) >> 4u;
XeTextureLoadSourceAddress(load_info, block_index, 1u) >> 4u;
uint4_xe guest_blocks = XeEndianSwap16(
array_buffer_load_xe(xe_texture_load_source, block_offset_guest),
load_info.endian_32);
@@ -44,8 +44,7 @@ entry_inputs_end_code_begin_compute_xe
XE_TEXTURE_LOAD_16BPB_TRANSFORM(guest_blocks));
++block_offset_host;
block_offset_guest +=
XeTextureLoadRightConsecutiveBlocksOffset(load_info, block_index.x, 1u) >>
4u;
XeTextureLoadLocalXAddressXor(8u, 1u, load_info.is_tiled) >> 4u;
guest_blocks = XeEndianSwap16(
array_buffer_load_xe(xe_texture_load_source, block_offset_guest),
load_info.endian_32);
@@ -36,7 +36,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.size_blocks.y, 4u) +
load_info.host_offset) >> 4u);
uint block_offset_guest =
XeTextureLoadGuestBlockOffset(load_info, block_index, 4u, 2u) >> 4u;
XeTextureLoadSourceAddress(load_info, block_index, 2u) >> 4u;
uint4_xe guest_blocks = XeEndianSwap32(
array_buffer_load_xe(xe_texture_load_source, block_offset_guest),
load_info.endian_32);
@@ -44,8 +44,7 @@ entry_inputs_end_code_begin_compute_xe
XE_TEXTURE_LOAD_32BPB_TRANSFORM(guest_blocks));
++block_offset_host;
block_offset_guest +=
XeTextureLoadRightConsecutiveBlocksOffset(load_info, block_index.x, 2u) >>
4u;
XeTextureLoadLocalXAddressXor(4u, 2u, load_info.is_tiled) >> 4u;
guest_blocks = XeEndianSwap32(
array_buffer_load_xe(xe_texture_load_source, block_offset_guest),
load_info.endian_32);
@@ -37,7 +37,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.size_blocks.y, 8u) +
load_info.host_offset) >> 4u);
uint block_offset_guest =
XeTextureLoadGuestBlockOffset(load_info, block_index, 4u, 2u) >> 4u;
XeTextureLoadSourceAddress(load_info, block_index, 2u) >> 4u;
uint4_xe guest_blocks = XeEndianSwap32(
array_buffer_load_xe(xe_texture_load_source, block_offset_guest),
load_info.endian_32);
@@ -46,8 +46,7 @@ entry_inputs_end_code_begin_compute_xe
array_buffer_store_xe(xe_texture_load_dest, block_offset_host, block_0);
array_buffer_store_xe(xe_texture_load_dest, block_offset_host + 1u, block_1);
block_offset_guest +=
XeTextureLoadRightConsecutiveBlocksOffset(load_info, block_index.x, 2u) >>
4u;
XeTextureLoadLocalXAddressXor(4u, 2u, load_info.is_tiled) >> 4u;
guest_blocks = XeEndianSwap32(
array_buffer_load_xe(xe_texture_load_source, block_offset_guest),
load_info.endian_32);
@@ -36,7 +36,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.size_blocks.y, 8u) +
load_info.host_offset) >> 4u);
uint block_offset_guest =
XeTextureLoadGuestBlockOffset(load_info, block_index, 8u, 3u) >> 4u;
XeTextureLoadSourceAddress(load_info, block_index, 3u) >> 4u;
uint4_xe guest_blocks = XeEndianSwap32(
array_buffer_load_xe(xe_texture_load_source, block_offset_guest),
load_info.endian_32);
@@ -44,8 +44,7 @@ entry_inputs_end_code_begin_compute_xe
XE_TEXTURE_LOAD_64BPB_TRANSFORM(guest_blocks));
++block_offset_host;
block_offset_guest +=
XeTextureLoadRightConsecutiveBlocksOffset(load_info, block_index.x, 3u) >>
4u;
XeTextureLoadLocalXAddressXor(2u, 3u, load_info.is_tiled) >> 4u;
guest_blocks = XeEndianSwap32(
array_buffer_load_xe(xe_texture_load_source, block_offset_guest),
load_info.endian_32);
@@ -35,7 +35,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.size_blocks.y, 1u) +
load_info.host_offset) >> 4u);
uint block_offset_guest =
XeTextureLoadGuestBlockOffset(load_info, block_index, 1u, 0u) >> 3u;
XeTextureLoadSourceAddress(load_info, block_index, 0u) >> 3u;
array_buffer_store_xe(
xe_texture_load_dest, block_offset_host,
uint4_xe(
@@ -43,8 +43,7 @@ entry_inputs_end_code_begin_compute_xe
array_buffer_load_xe(
xe_texture_load_source,
block_offset_guest +
(XeTextureLoadRightConsecutiveBlocksOffset(load_info,
block_index.x, 0u)
(XeTextureLoadLocalXAddressXor(8u, 0u, load_info.is_tiled)
>> 3u))));
}
entry_code_end_compute_xe
@@ -53,7 +53,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.host_offset) >> 4u);
uint elements_pitch_host = load_info.host_pitch >> 4u;
uint block_offset_guest =
XeTextureLoadGuestBlockOffset(load_info, block_index, 8u, 3u) >> 4u;
XeTextureLoadSourceAddress(load_info, block_index, 3u) >> 4u;
uint i;
unroll_xe for (i = 0u; i < 2u; ++i) {
if (i != 0u) {
@@ -39,7 +39,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.host_offset) >> 4u);
uint elements_pitch_host = load_info.host_pitch >> 4u;
uint block_offset_guest =
XeTextureLoadGuestBlockOffset(load_info, block_index, 16u, 4u) >> 4u;
XeTextureLoadSourceAddress(load_info, block_index, 4u) >> 4u;
uint4_xe block_0 = XeEndianSwap32(
array_buffer_load_xe(xe_texture_load_source, block_offset_guest),
load_info.endian_32);
@@ -39,7 +39,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.host_offset) >> 4u);
uint elements_pitch_host = load_info.host_pitch >> 4u;
uint block_offset_guest =
XeTextureLoadGuestBlockOffset(load_info, block_index, 8u, 3u) >> 4u;
XeTextureLoadSourceAddress(load_info, block_index, 3u) >> 4u;
uint4_xe blocks_01 = XeEndianSwap32(
array_buffer_load_xe(xe_texture_load_source, block_offset_guest),
load_info.endian_32);
@@ -39,7 +39,7 @@ entry_inputs_end_code_begin_compute_xe
load_info.host_offset) >> 4u);
uint elements_pitch_host = load_info.host_pitch >> 4u;
uint block_offset_guest =
XeTextureLoadGuestBlockOffset(load_info, block_index, 16u, 4u) >> 4u;
XeTextureLoadSourceAddress(load_info, block_index, 4u) >> 4u;
uint i;
unroll_xe for (i = 0u; i < 2u; ++i) {
if (i != 0u) {

Some files were not shown because too many files have changed in this diff Show More