From 88f95a8bd6e2e41c188693b0b4dc5e1d6157b212 Mon Sep 17 00:00:00 2001 From: Triang3l Date: Thu, 15 Jan 2026 11:56:45 +0300 Subject: [PATCH] [GPU] Change "element" name back to "block" in texture addressing The 32_32_32_FLOAT format seems to be vertex-only, so it looks like there can't be storage elements smaller than a single texel. So, use a more precise name that can't be confused with "picture element" (pixel) or "texture element" (texel) that represents a single logical pixel rather than a storage block of pixels. --- src/xenia/gpu/draw_util.cc | 4 +- src/xenia/gpu/shaders/resolve.xesli | 17 ++- src/xenia/gpu/shaders/texture_address.xesli | 132 ++++++++++---------- src/xenia/gpu/shaders/texture_load.xesli | 18 +-- src/xenia/gpu/texture_address.h | 87 ++++++------- 5 files changed, 124 insertions(+), 134 deletions(-) diff --git a/src/xenia/gpu/draw_util.cc b/src/xenia/gpu/draw_util.cc index 94e2abe37..9d54e2afc 100644 --- a/src/xenia/gpu/draw_util.cc +++ b/src/xenia/gpu/draw_util.cc @@ -1005,12 +1005,12 @@ bool GetResolveInfo(const RegisterFile& regs, const Memory& memory, auto rb_copy_dest_pitch = regs.Get(); const uint32_t copy_dest_pitch_aligned = xe::align(rb_copy_dest_pitch.copy_dest_pitch, - texture_address::kStoragePitchHeightAlignmentElements); + texture_address::kStoragePitchHeightAlignmentBlocks); info_out.copy_dest_coordinate_info.pitch_aligned_div_32 = copy_dest_pitch_aligned >> 5; const uint32_t copy_dest_height_aligned = xe::align(rb_copy_dest_pitch.copy_dest_height, - texture_address::kStoragePitchHeightAlignmentElements); + texture_address::kStoragePitchHeightAlignmentBlocks); info_out.copy_dest_coordinate_info.height_aligned_div_32 = copy_dest_height_aligned >> 5; const FormatInfo& dest_format_info = *FormatInfo::Get(dest_format); diff --git a/src/xenia/gpu/shaders/resolve.xesli b/src/xenia/gpu/shaders/resolve.xesli index 18e64d6e1..397bb7281 100644 --- a/src/xenia/gpu/shaders/resolve.xesli +++ b/src/xenia/gpu/shaders/resolve.xesli @@ -161,7 +161,7 @@ uint XeResolveEdramPixelStrideInts(XeResolveInfo resolve_info) { #ifndef XE_RESOLVE_CLEAR uint XeResolveDestPixelAddress(const XeResolveInfo resolve_info, uint2_xe host_position, - const uint bytes_per_element_log2) { + const uint bytes_per_block_log2) { host_position += resolve_info.dest_xy_offset_scaled; uint address; uint2_xe guest_position = host_position; @@ -170,19 +170,18 @@ uint XeResolveEdramPixelStrideInts(XeResolveInfo resolve_info) { resolution_scaled_addressing = XeniaTextureGetResolutionScaledAddressing( host_position.xy, resolve_info.resolution_scale, - bytes_per_element_log2); + bytes_per_block_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)); + resolve_info.dest_slice_pitch_3d_macro_tiles, bytes_per_block_log2)); } else { address = uint(XenosTextureTiledAddress2D( int2_xe(guest_position), resolve_info.dest_row_pitch_macro_tiles, - bytes_per_element_log2)); + bytes_per_block_log2)); } #ifdef XE_RESOLVE_RESOLUTION_SCALED address = address * (resolve_info.resolution_scale.x * @@ -195,18 +194,18 @@ uint XeResolveEdramPixelStrideInts(XeResolveInfo resolve_info) { // 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. + // scaling, within XeniaTextureResolutionScaledGroupBlocks.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) { + const uint bytes_per_block_log2) { #ifdef XE_RESOLVE_RESOLUTION_SCALED - return x << bytes_per_element_log2; + return x << bytes_per_block_log2; #else return uint(XenosTextureTiledAddressXInMacroXor(int(x), - bytes_per_element_log2)); + bytes_per_block_log2)); #endif } diff --git a/src/xenia/gpu/shaders/texture_address.xesli b/src/xenia/gpu/shaders/texture_address.xesli index a555c4642..3b5851540 100644 --- a/src/xenia/gpu/shaders/texture_address.xesli +++ b/src/xenia/gpu/shaders/texture_address.xesli @@ -26,15 +26,15 @@ int XenosTextureTiledAddressCombine(const int outer_inner_bytes, const int bank, } int XenosTextureTiledAddress2D(const int2_xe p, const uint pitch_macro_tiles, - const uint bytes_per_element_log2) { - const int outer_elements = + const uint bytes_per_block_log2) { + const int outer_blocks = ((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 inner_blocks = (((p.y >> 1) & 0x7) << 3) | (p.x & 0x7); + const int outer_inner_bytes = (outer_blocks | inner_blocks) + << bytes_per_block_log2; const int bank = (p.y >> 4) & 0x1; const int pipe = ((p.x >> 3) & 0x3) ^ (((p.y >> 3) & 0x1) << 1); return XenosTextureTiledAddressCombine(outer_inner_bytes, bank, pipe, @@ -43,18 +43,18 @@ int XenosTextureTiledAddress2D(const int2_xe p, const uint pitch_macro_tiles, 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 = + const uint bytes_per_block_log2) { + const int outer_blocks = ((((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 = + const int inner_blocks = ((p.z & 0x3) << 5) | (((p.y >> 1) & 0x3) << 3) | (p.x & 0x7); - const int outer_inner_bytes = - (outer_elements | inner_elements) << bytes_per_element_log2; + const int outer_inner_bytes = (outer_blocks | inner_blocks) + << bytes_per_block_log2; const int bank = ((p.y >> 3) ^ (p.z >> 2)) & 0x1; const int pipe = ((p.x >> 3) & 0x3) ^ (bank << 1); return XenosTextureTiledAddressCombine(outer_inner_bytes, bank, pipe, @@ -65,49 +65,49 @@ int XenosTextureTiledAddress3D(const int3_xe p, const uint pitch_macro_tiles, // 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, + const uint bytes_per_block_log2) { + return XenosTextureTiledAddressCombine((x & 0x7) << bytes_per_block_log2, 0, (x >> 3) & 0x3, 0); } -// The lowest bits of an element index within a micro tile are X[2:0]. +// The lowest bits of an block 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 +// However, the bits [3:0] are the lower bits of the micro tile block index +// times the number of bytes per block. +// Because of this, a number of blocks, that depends on the count of bytes per +// block, 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. +// - 1bpe: 8 blocks (8 bytes - limited by address bit 3 being Y[1] for 1bpe). +// - 2bpe: 8 blocks (16 bytes - limited by address bit 4 always being Y[0]). +// - 4bpe: 4 blocks. +// - 8bpe: 2 blocks. +// - 16bpe: 1 block. +// This makes it possible to access multiple blocks 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 +// A single group contains a full rectangular region of blocks. 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 +// Within a guest group, the addressing of blocks is controlled by the host. +// Specifically, host groups are arranged in a guest group as group-linear +// column-major (for storage locality along both axes), and blocks 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. +// reinterpretation of resolution-scaled data between different numbers of bytes +// per block. 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 @@ -121,32 +121,32 @@ int XenosTextureTiledAddressXInMacroXor(const int x, // 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 +// sequences of consecutive blocks 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] +// address[8], so a group can't be wider than 4 blocks, 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]). +// - Group width: 2^4 blocks (maximum within 7 bits), or 2^4 bytes. +// - Group height: 2^3 blocks (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]). +// - Group width: 2^4 blocks (maximum within 7 bits), or 2^5 bytes. +// - Group height: 2^2 blocks (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]). +// - Group width: 2^4 blocks (maximum within 7 bits), or 2^6 bytes. +// - Group height: 2^1 blocks (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]). +// - Group width: 2^2 blocks (X[2] is beyond 7 bits), or 2^5 bytes. +// - Group height: 2^1 blocks (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]). +// - Group width: 2^1 blocks (X[2:1] is beyond 7 bits), or 2^5 bytes. +// - Group height: 2^1 blocks (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 @@ -154,19 +154,17 @@ int XenosTextureTiledAddressXInMacroXor(const int x, // (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. +// Note that with the given group sizes, as well as with blocks in a host group +// stored as row-major, for 1bpe, 16x1 host blocks are stored consecutively with +// resolution scaling (even though in guest tiling, only 8x1 blocks 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) { +uint2_xe XeniaTextureResolutionScaledGroupBlocksLog2( + const uint bytes_per_block_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)); + return uint2_xe(bytes_per_block_log2 >= 3u ? 5u - bytes_per_block_log2 : 4u, + 3u - min(bytes_per_block_log2, 2u)); } struct XeniaTextureResolutionScaledAddressing { @@ -177,20 +175,20 @@ struct XeniaTextureResolutionScaledAddressing { XeniaTextureResolutionScaledAddressing XeniaTextureGetResolutionScaledAddressing(const uint2_xe position, const uint2_xe resolution_scale, - const uint bytes_per_element_log2) { + const uint bytes_per_block_log2) { XeniaTextureResolutionScaledAddressing addressing; - const uint2_xe group_elements_log2 = - XeniaTextureResolutionScaledGroupElementsLog2(bytes_per_element_log2); + const uint2_xe group_blocks_log2 = + XeniaTextureResolutionScaledGroupBlocksLog2(bytes_per_block_log2); - const uint2_xe host_group_id_in_texture = position >> group_elements_log2; + const uint2_xe host_group_id_in_texture = position >> group_blocks_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; + guest_group_id_in_texture << group_blocks_log2; // Host groups are stored as column-major in a guest group, but this can be // changed freely. @@ -198,18 +196,18 @@ XeniaTextureGetResolutionScaledAddressing(const uint2_xe position, 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 + // block, 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; + group_blocks_log2.x + bytes_per_block_log2; const uint2_xe position_in_host_group = - position & ((uint_x2_xe(1u) << group_elements_log2) - 1u); + position & ((uint_x2_xe(1u) << group_blocks_log2) - 1u); addressing.host_byte_offset_in_guest_group = (host_group_index_in_guest_group << - (group_width_bytes_log2 + group_elements_log2.y)) | + (group_width_bytes_log2 + group_blocks_log2.y)) | (position_in_host_group.y << group_width_bytes_log2) | - (position_in_host_group.x << bytes_per_element_log2); + (position_in_host_group.x << bytes_per_block_log2); return addressing; } diff --git a/src/xenia/gpu/shaders/texture_load.xesli b/src/xenia/gpu/shaders/texture_load.xesli index f2a28cb59..cc1b3664e 100644 --- a/src/xenia/gpu/shaders/texture_load.xesli +++ b/src/xenia/gpu/shaders/texture_load.xesli @@ -98,14 +98,14 @@ XeTextureLoadInfo XeTextureLoadGetInfo(param_push_consts_xe) { uint XeTextureLoadSourceAddress(const XeTextureLoadInfo load_info, const uint3_xe host_position, - const uint bytes_per_element_log2) { + const uint bytes_per_block_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); + bytes_per_block_log2); guest_position.xy = resolution_scaled_addressing.guest_group_origin; #else dont_flatten_xe if (!load_info.is_tiled) { @@ -114,7 +114,7 @@ uint XeTextureLoadSourceAddress(const XeTextureLoadInfo load_info, (guest_position.y + load_info.guest_z_stride_block_rows_aligned * guest_position.z)) << - bytes_per_element_log2; + bytes_per_block_log2; } else #endif { @@ -124,12 +124,12 @@ uint XeTextureLoadSourceAddress(const XeTextureLoadInfo load_info, 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)); + bytes_per_block_log2)); } else { address = uint(XenosTextureTiledAddress2D( int2_xe(guest_position.xy), load_info.guest_pitch_aligned >> XENOS_TEXTURE_MACRO_TILE_WIDTH_LOG2, - bytes_per_element_log2)); + bytes_per_block_log2)); } } #ifdef XE_TEXTURE_LOAD_RESOLUTION_SCALED @@ -143,24 +143,24 @@ uint XeTextureLoadSourceAddress(const XeTextureLoadInfo load_info, // XOR to apply to the byte address to flip the bits corresponding to the given // X coordinate bits within: -// - Resolution-scaled tiled: XeniaTextureResolutionScaledGroupElements.x; +// - Resolution-scaled tiled: XeniaTextureResolutionScaledGroupBlocks.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 uint bytes_per_block_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)); + XenosTextureTiledAddressXInMacroXor(int(x), bytes_per_block_log2)); } else #endif { - x_address_xor = x << bytes_per_element_log2; + x_address_xor = x << bytes_per_block_log2; } return x_address_xor; } diff --git a/src/xenia/gpu/texture_address.h b/src/xenia/gpu/texture_address.h index 5f1c49d37..8b36854be 100644 --- a/src/xenia/gpu/texture_address.h +++ b/src/xenia/gpu/texture_address.h @@ -37,19 +37,12 @@ namespace texture_address { // must not access the data in the padding after aligning to 32x32 // (resulting in 1280x736) via the guest CPU memory mappings. -// "Element" refers to a storage unit used in guest addressing calculations: -// block for compressed or otherwise block-based formats, pixel for other -// formats. -// TODO(Triang3l): Research 32_32_32_FLOAT format ("Expand3x" in AddrLib terms) -// texture storage. Should storage dimensions be rounded assuming that one -// element is one component rather than the whole pixel? - // Level pitch (or width for non-base mip levels), height, and depth (for 3D // textures - stacked 2D textures use the exact array layer count for the stride // at every level, and cubemaps are normally stored with a stride of 6 faces) // are rounded up by the hardware for the purposes of calculating strides of // subresources and of macro tiles within a subresource to: -// - 32x32 (2D) or 32x32x4 (3D) elements. This is applicable to both tiled and +// - 32x32 (2D) or 32x32x4 (3D) blocks. This is applicable to both tiled and // linear textures. Note that for 3D, this is larger than a macro tile // (32x16x4). // - Power of two, for non-base mip levels. @@ -69,12 +62,12 @@ namespace texture_address { // Another scenario where the product of the aligned dimensions may not be an // accurate estimate (but in this case too small rather than too large) is when // the width exceeds the pitch. -constexpr unsigned int kStoragePitchHeightAlignmentElementsLog2 = 5; -constexpr uint32_t kStoragePitchHeightAlignmentElements = - uint32_t(1) << kStoragePitchHeightAlignmentElementsLog2; -constexpr unsigned int kStorageDepthAlignmentElementsLog2 = 2; -constexpr uint32_t kStorageDepthAlignmentElements = - uint32_t(1) << kStorageDepthAlignmentElementsLog2; +constexpr unsigned int kStoragePitchHeightAlignmentBlocksLog2 = 5; +constexpr uint32_t kStoragePitchHeightAlignmentBlocks = + uint32_t(1) << kStoragePitchHeightAlignmentBlocksLog2; +constexpr unsigned int kStorageDepthAlignmentBlocksLog2 = 2; +constexpr uint32_t kStorageDepthAlignmentBlocks = + uint32_t(1) << kStorageDepthAlignmentBlocksLog2; // 2D tiling from: // https://github.com/BinomialLLC/crunch/blob/36479bc697be19168daafbf15f47f3c60ccec004/inc/crn_decomp.h#L4107 @@ -105,31 +98,31 @@ constexpr uint32_t kStorageDepthAlignmentElements = // [4] = `y & 1` (Y least significant bit) // [3:0] = `outer_inner_bytes[3:0]` (pipe interleave lower bits) // -// The "outer_inner" part is scaled by the number of bytes per element: -// `outer_inner_bytes = outer_inner_elements << log2(bytes_per_element)` -// where `outer_inner_elements` is a sum (or bitwise OR) of two terms: -// - `outer_elements`: Origin of the 32x32 (2D) or 32x16x4 (3D) macro tile -// within the subresource. -// Macro tiles are treated as laid out block-linearly in the calculation of -// this part, with Z being the most significant term, Y in between, and X +// The "outer_inner" part is scaled by the number of bytes per block: +// `outer_inner_bytes = outer_inner_blocks << log2(bytes_per_block)` +// where `outer_inner_blocks` is a sum (or bitwise OR) of two terms: +// - `outer_blocks`: Origin of the 32x32 (2D) or 32x16x4 (3D) macro tile within +// the subresource. +// Macro tiles are treated as laid out macro-tile-linearly in the calculation +// of this part, with Z being the most significant term, Y in between, and X // being the least significant. // 2D: // (x[:5] + ceil(pitch / 32) * y[:5]) << 6 // 3D: // (x[:5] + ceil(pitch / 32) * (y[:4] + 2 * ceil(height / 32) * z[:2])) << 7 -// - `inner_elements`: Offset of 1x2x1 elements within a 8x16 (2D) or 8x8x4 -// micro tile. +// - `inner_blocks`: Offset of 1x2x1 blocks within a 8x16 (2D) or 8x8x4 micro +// tile. // Though the LSB of Y always goes to the bit 4 of the memory address, within // a micro tile, {X, Y[:1], Z} can be treated as linear in the calculation of // this part, with Z being the most significant, Y in between, and X being the // least significant. // 2D: -// - inner_elements[5:3] = y[3:1] -// - inner_elements[2:0] = x[2:0] +// - inner_blocks[5:3] = y[3:1] +// - inner_blocks[2:0] = x[2:0] // 3D: -// - inner_elements[6:5] = z[1:0] -// - inner_elements[4:3] = y[2:1] -// - inner_elements[2:0] = x[2:0] +// - inner_blocks[6:5] = z[1:0] +// - inner_blocks[4:3] = y[2:1] +// - inner_blocks[2:0] = x[2:0] // // However, the "outer/inner" part doesn't include the offset of the micro tile // within the macro tile: X[4:3], and Y[4] (2D) or Y[3] (3D). These are encoded @@ -191,54 +184,54 @@ Address TiledCombine(const Address outer_inner_bytes, const uint32_t bank, } // The absolute of the return value is below 2^31 (for 16384x8192 with 16 bytes -// per element, though it's not even clear if the hardware allows pitches -// greater than 8192, the maximum texture size, but the pitch field in a texture -// fetch constant is 14 bits wide). +// per block, though it's not even clear if the hardware allows pitches greater +// than 8192, the maximum texture size, but the pitch field in a texture fetch +// constant is 14 bits wide). inline int32_t Tiled2D(const int32_t x, const int32_t y, const uint32_t pitch_aligned, - const unsigned int bytes_per_element_log2) { + const unsigned int bytes_per_block_log2) { // Expecting that all the needed rounding (not only to 32x32, but also to a // power of two for mips) is done before the call so this function works with // the actual storage dimensions. - assert_zero(pitch_aligned & (kStoragePitchHeightAlignmentElements - 1)); - const int32_t outer_elements = + assert_zero(pitch_aligned & (kStoragePitchHeightAlignmentBlocks - 1)); + const int32_t outer_blocks = ((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; + const int32_t inner_blocks = (((y >> 1) & 0b111) << 3) | (x & 0b111); + const int32_t outer_inner_bytes = (outer_blocks | inner_blocks) + << bytes_per_block_log2; const uint32_t bank = (y >> 4) & 0b1; const uint32_t pipe = ((x >> 3) & 0b11) ^ (((y >> 3) & 0b1) << 1); return TiledCombine(outer_inner_bytes, bank, pipe, y & 1); } // The absolute of the return value is below 2^39 (for 16384x2048x1024 with 16 -// bytes per element). This also means that 32 (more precisely, 27) bits is -// always enough for the page index within a subresource. +// bytes per block). This also means that 32 (more precisely, 27) bits is always +// enough for the page index within a subresource. inline int64_t Tiled3D(const int32_t x, const int32_t y, const int32_t z, const uint32_t pitch_aligned, const uint32_t height_aligned, - const unsigned int bytes_per_element_log2) { + const unsigned int bytes_per_block_log2) { // Expecting that all the needed rounding (not only to 32x32x4, but also to a // power of two for mips) is done before the call so this function works with // the actual storage dimensions. - assert_zero(pitch_aligned & (kStoragePitchHeightAlignmentElements - 1)); - assert_zero(height_aligned & (kStoragePitchHeightAlignmentElements - 1)); - // The absolute of `outer_elements` is below (1024 / 4) * (2048 / 16) * + assert_zero(pitch_aligned & (kStoragePitchHeightAlignmentBlocks - 1)); + assert_zero(height_aligned & (kStoragePitchHeightAlignmentBlocks - 1)); + // The absolute of `outer_blocks` is below (1024 / 4) * (2048 / 16) * // (16384 / 32) * 128 = 2^31. - const int32_t outer_elements = + const int32_t outer_blocks = ((((z >> kMacroTileDepthLog2) * (height_aligned >> kMacroTileHeight3DLog2) + (y >> kMacroTileHeight3DLog2)) * int32_t(pitch_aligned >> kMacroTileWidthLog2)) + (x >> kMacroTileWidthLog2)) << 7; - const int32_t inner_elements = + const int32_t inner_blocks = ((z & 0b11) << 5) | (((y >> 1) & 0b11) << 3) | (x & 0b111); - const int64_t outer_inner_bytes = int64_t(outer_elements | inner_elements) - << bytes_per_element_log2; + const int64_t outer_inner_bytes = int64_t(outer_blocks | inner_blocks) + << bytes_per_block_log2; const uint32_t bank = ((y >> 3) ^ (z >> 2)) & 0b1; const uint32_t pipe = ((x >> 3) & 0b11) ^ (bank << 1); return TiledCombine(outer_inner_bytes, bank, pipe, y & 1);