diff --git a/src/xenia/gpu/d3d12/d3d12_texture_cache.cc b/src/xenia/gpu/d3d12/d3d12_texture_cache.cc index 0c61db7cc..083978c3f 100644 --- a/src/xenia/gpu/d3d12/d3d12_texture_cache.cc +++ b/src/xenia/gpu/d3d12/d3d12_texture_cache.cc @@ -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 || diff --git a/src/xenia/gpu/shaders/resolve.xesli b/src/xenia/gpu/shaders/resolve.xesli index d79a6e6e9..07413f1b5 100644 --- a/src/xenia/gpu/shaders/resolve.xesli +++ b/src/xenia/gpu/shaders/resolve.xesli @@ -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 } diff --git a/src/xenia/gpu/shaders/resolve_fast_32bpp_1x2xmsaa.xesli b/src/xenia/gpu/shaders/resolve_fast_32bpp_1x2xmsaa.xesli index 12b234d4d..d0e85d73c 100644 --- a/src/xenia/gpu/shaders/resolve_fast_32bpp_1x2xmsaa.xesli +++ b/src/xenia/gpu/shaders/resolve_fast_32bpp_1x2xmsaa.xesli @@ -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)); diff --git a/src/xenia/gpu/shaders/resolve_fast_32bpp_4xmsaa.xesli b/src/xenia/gpu/shaders/resolve_fast_32bpp_4xmsaa.xesli index b8cac5dbf..a6b1b68c6 100644 --- a/src/xenia/gpu/shaders/resolve_fast_32bpp_4xmsaa.xesli +++ b/src/xenia/gpu/shaders/resolve_fast_32bpp_4xmsaa.xesli @@ -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)); diff --git a/src/xenia/gpu/shaders/resolve_fast_64bpp_1x2xmsaa.xesli b/src/xenia/gpu/shaders/resolve_fast_64bpp_1x2xmsaa.xesli index de3e095a0..62449803e 100644 --- a/src/xenia/gpu/shaders/resolve_fast_64bpp_1x2xmsaa.xesli +++ b/src/xenia/gpu/shaders/resolve_fast_64bpp_1x2xmsaa.xesli @@ -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)); diff --git a/src/xenia/gpu/shaders/resolve_fast_64bpp_4xmsaa.xesli b/src/xenia/gpu/shaders/resolve_fast_64bpp_4xmsaa.xesli index 91d6dc777..020775f2f 100644 --- a/src/xenia/gpu/shaders/resolve_fast_64bpp_4xmsaa.xesli +++ b/src/xenia/gpu/shaders/resolve_fast_64bpp_4xmsaa.xesli @@ -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)); diff --git a/src/xenia/gpu/shaders/resolve_full_128bpp.xesli b/src/xenia/gpu/shaders/resolve_full_128bpp.xesli index 7b8566184..5935d2eee 100644 --- a/src/xenia/gpu/shaders/resolve_full_128bpp.xesli +++ b/src/xenia/gpu/shaders/resolve_full_128bpp.xesli @@ -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), diff --git a/src/xenia/gpu/shaders/resolve_full_64bpp.xesli b/src/xenia/gpu/shaders/resolve_full_64bpp.xesli index f36b0eec2..beb50c515 100644 --- a/src/xenia/gpu/shaders/resolve_full_64bpp.xesli +++ b/src/xenia/gpu/shaders/resolve_full_64bpp.xesli @@ -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)); diff --git a/src/xenia/gpu/shaders/texture_address.xesli b/src/xenia/gpu/shaders/texture_address.xesli index 98a5003a3..a555c4642 100644 --- a/src/xenia/gpu/shaders/texture_address.xesli +++ b/src/xenia/gpu/shaders/texture_address.xesli @@ -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) { diff --git a/src/xenia/gpu/shaders/texture_load.xesli b/src/xenia/gpu/shaders/texture_load.xesli index 20ed4eb25..f2a28cb59 100644 --- a/src/xenia/gpu/shaders/texture_load.xesli +++ b/src/xenia/gpu/shaders/texture_load.xesli @@ -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_ diff --git a/src/xenia/gpu/shaders/texture_load_128bpb.xesli b/src/xenia/gpu/shaders/texture_load_128bpb.xesli index 483956ab2..ccba3b6b8 100644 --- a/src/xenia/gpu/shaders/texture_load_128bpb.xesli +++ b/src/xenia/gpu/shaders/texture_load_128bpb.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, diff --git a/src/xenia/gpu/shaders/texture_load_16bpb.xesli b/src/xenia/gpu/shaders/texture_load_16bpb.xesli index d005fce14..03cbb0d0b 100644 --- a/src/xenia/gpu/shaders/texture_load_16bpb.xesli +++ b/src/xenia/gpu/shaders/texture_load_16bpb.xesli @@ -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); diff --git a/src/xenia/gpu/shaders/texture_load_32bpb.xesli b/src/xenia/gpu/shaders/texture_load_32bpb.xesli index 987ef600c..f26680874 100644 --- a/src/xenia/gpu/shaders/texture_load_32bpb.xesli +++ b/src/xenia/gpu/shaders/texture_load_32bpb.xesli @@ -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); diff --git a/src/xenia/gpu/shaders/texture_load_32bpb_64bpb.xesli b/src/xenia/gpu/shaders/texture_load_32bpb_64bpb.xesli index 897e80a6c..60a5dba5b 100644 --- a/src/xenia/gpu/shaders/texture_load_32bpb_64bpb.xesli +++ b/src/xenia/gpu/shaders/texture_load_32bpb_64bpb.xesli @@ -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); diff --git a/src/xenia/gpu/shaders/texture_load_64bpb.xesli b/src/xenia/gpu/shaders/texture_load_64bpb.xesli index f118b6a32..ad8520ae8 100644 --- a/src/xenia/gpu/shaders/texture_load_64bpb.xesli +++ b/src/xenia/gpu/shaders/texture_load_64bpb.xesli @@ -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); diff --git a/src/xenia/gpu/shaders/texture_load_8bpb.xesli b/src/xenia/gpu/shaders/texture_load_8bpb.xesli index cc9c1fab0..804a8e15e 100644 --- a/src/xenia/gpu/shaders/texture_load_8bpb.xesli +++ b/src/xenia/gpu/shaders/texture_load_8bpb.xesli @@ -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 diff --git a/src/xenia/gpu/shaders/texture_load_ctx1.cs.xesl b/src/xenia/gpu/shaders/texture_load_ctx1.cs.xesl index 3f809ef33..6c093039b 100644 --- a/src/xenia/gpu/shaders/texture_load_ctx1.cs.xesl +++ b/src/xenia/gpu/shaders/texture_load_ctx1.cs.xesl @@ -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) { diff --git a/src/xenia/gpu/shaders/texture_load_dxn_rg8.cs.xesl b/src/xenia/gpu/shaders/texture_load_dxn_rg8.cs.xesl index dfa888d0d..80aa5a24d 100644 --- a/src/xenia/gpu/shaders/texture_load_dxn_rg8.cs.xesl +++ b/src/xenia/gpu/shaders/texture_load_dxn_rg8.cs.xesl @@ -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); diff --git a/src/xenia/gpu/shaders/texture_load_dxt1_rgba8.cs.xesl b/src/xenia/gpu/shaders/texture_load_dxt1_rgba8.cs.xesl index ca1397da2..5aab74f27 100644 --- a/src/xenia/gpu/shaders/texture_load_dxt1_rgba8.cs.xesl +++ b/src/xenia/gpu/shaders/texture_load_dxt1_rgba8.cs.xesl @@ -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); diff --git a/src/xenia/gpu/shaders/texture_load_dxt3_rgba8.cs.xesl b/src/xenia/gpu/shaders/texture_load_dxt3_rgba8.cs.xesl index 932b4fd95..207030c32 100644 --- a/src/xenia/gpu/shaders/texture_load_dxt3_rgba8.cs.xesl +++ b/src/xenia/gpu/shaders/texture_load_dxt3_rgba8.cs.xesl @@ -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) { diff --git a/src/xenia/gpu/shaders/texture_load_dxt3a.cs.xesl b/src/xenia/gpu/shaders/texture_load_dxt3a.cs.xesl index f0fde477f..bfdb37601 100644 --- a/src/xenia/gpu/shaders/texture_load_dxt3a.cs.xesl +++ b/src/xenia/gpu/shaders/texture_load_dxt3a.cs.xesl @@ -41,7 +41,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); diff --git a/src/xenia/gpu/shaders/texture_load_dxt3aas1111.xesli b/src/xenia/gpu/shaders/texture_load_dxt3aas1111.xesli index 0f4477a8a..57d9e74e1 100644 --- a/src/xenia/gpu/shaders/texture_load_dxt3aas1111.xesli +++ b/src/xenia/gpu/shaders/texture_load_dxt3aas1111.xesli @@ -41,7 +41,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); diff --git a/src/xenia/gpu/shaders/texture_load_dxt5_rgba8.cs.xesl b/src/xenia/gpu/shaders/texture_load_dxt5_rgba8.cs.xesl index c36d291ed..51dfcc080 100644 --- a/src/xenia/gpu/shaders/texture_load_dxt5_rgba8.cs.xesl +++ b/src/xenia/gpu/shaders/texture_load_dxt5_rgba8.cs.xesl @@ -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) { diff --git a/src/xenia/gpu/shaders/texture_load_dxt5a_r8.cs.xesl b/src/xenia/gpu/shaders/texture_load_dxt5a_r8.cs.xesl index 0ab1367d9..d2fb00bf1 100644 --- a/src/xenia/gpu/shaders/texture_load_dxt5a_r8.cs.xesl +++ b/src/xenia/gpu/shaders/texture_load_dxt5a_r8.cs.xesl @@ -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); diff --git a/src/xenia/gpu/texture_address.h b/src/xenia/gpu/texture_address.h index fca47a637..5f1c49d37 100644 --- a/src/xenia/gpu/texture_address.h +++ b/src/xenia/gpu/texture_address.h @@ -168,6 +168,15 @@ constexpr uint32_t kStorageDepthAlignmentElements = // offsetting - more specifically, it's limited by the page size and the macro // tile size, whichever is greater. +constexpr unsigned int kMacroTileWidthLog2 = 5; +constexpr uint32_t kMacroTileWidth = uint32_t(1) << kMacroTileWidthLog2; +constexpr unsigned int kMacroTileHeight2DLog2 = 5; +constexpr uint32_t kMacroTileHeight2D = uint32_t(1) << kMacroTileHeight2DLog2; +constexpr unsigned int kMacroTileHeight3DLog2 = 4; +constexpr uint32_t kMacroTileHeight3D = uint32_t(1) << kMacroTileHeight3DLog2; +constexpr unsigned int kMacroTileDepthLog2 = 2; +constexpr uint32_t kMacroTileDepth = uint32_t(1) << kMacroTileDepthLog2; + template Address TiledCombine(const Address outer_inner_bytes, const uint32_t bank, const uint32_t pipe, const uint32_t y_lsb) { @@ -193,7 +202,10 @@ inline int32_t Tiled2D(const int32_t x, const int32_t y, // the actual storage dimensions. assert_zero(pitch_aligned & (kStoragePitchHeightAlignmentElements - 1)); const int32_t outer_elements = - ((y >> 5) * int32_t(pitch_aligned >> 5) + (x >> 5)) << 6; + ((y >> kMacroTileHeight2DLog2) * + int32_t(pitch_aligned >> kMacroTileWidthLog2) + + (x >> kMacroTileWidthLog2)) + << 6; const int32_t inner_elements = (((y >> 1) & 0b111) << 3) | (x & 0b111); const int32_t outer_inner_bytes = (outer_elements | inner_elements) << bytes_per_element_log2; @@ -217,9 +229,11 @@ inline int64_t Tiled3D(const int32_t x, const int32_t y, const int32_t z, // The absolute of `outer_elements` is below (1024 / 4) * (2048 / 16) * // (16384 / 32) * 128 = 2^31. const int32_t outer_elements = - ((((z >> 2) * (height_aligned >> 4) + (y >> 4)) * - int32_t(pitch_aligned >> 5)) + - (x >> 5)) + ((((z >> kMacroTileDepthLog2) * + (height_aligned >> kMacroTileHeight3DLog2) + + (y >> kMacroTileHeight3DLog2)) * + int32_t(pitch_aligned >> kMacroTileWidthLog2)) + + (x >> kMacroTileWidthLog2)) << 7; const int32_t inner_elements = ((z & 0b11) << 5) | (((y >> 1) & 0b11) << 3) | (x & 0b111); diff --git a/src/xenia/gpu/texture_cache.h b/src/xenia/gpu/texture_cache.h index d19a4c588..131fadca4 100644 --- a/src/xenia/gpu/texture_cache.h +++ b/src/xenia/gpu/texture_cache.h @@ -451,8 +451,7 @@ class TextureCache { uint32_t is_tiled_3d_endian_scale; // Base offset in bytes, resolution-scaled. uint32_t guest_offset; - // For tiled textures - row pitch in blocks, aligned to 32, unscaled. - // For linear textures - row pitch in bytes. + // Unscaled. uint32_t guest_pitch_aligned; // For 3D textures only (ignored otherwise) - aligned to 32, unscaled. uint32_t guest_z_stride_block_rows_aligned; diff --git a/src/xenia/gpu/vulkan/vulkan_texture_cache.cc b/src/xenia/gpu/vulkan/vulkan_texture_cache.cc index 7451a7ab4..7feb04dda 100644 --- a/src/xenia/gpu/vulkan/vulkan_texture_cache.cc +++ b/src/xenia/gpu/vulkan/vulkan_texture_cache.cc @@ -1518,13 +1518,8 @@ bool VulkanTextureCache::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 ||