/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2022 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_GPU_SHADERS_TEXTURE_LOAD_XESLI_ #define XENIA_GPU_SHADERS_TEXTURE_LOAD_XESLI_ #include "endian.xesli" #include "texture_address.xesli" // 128 threads per group (the maximum wave size supported by DXIL and SPIR-V, // and the minimum required number of threads per group on Vulkan), laid out as // 4x32 (32 texels along Y per group - one guest tile) - starting with 64x32 // blocks (2x1 guest tiles) per group for 8bpb / 16bpb, and smaller for larger // block sizes. Since the mip tail is packed in 32x / x32 storage, there's no // need for the Y group size smaller than 32 - 8x16, for instance, would result // in 128x16 blocks per group for 8bpb / 16bpb, and for a 32x32 mip tail, there // would be two groups rather than one, for a total of 128x32 blocks - 75% of // the work will be wasted rather than 50% with one 64x32-block group. #define xesl_localSize_x 4 #define xesl_localSize_y 32 #define xesl_localSize_z 1 xesl_constantBuffer_begin(xe_texture_load_constants, set=2, binding=0, 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. 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; // - std140 vector boundary - // If this is a packed mip tail, this is aligned to tile dimensions. // Resolution-scaled. xesl_uint3 xe_texture_load_size_blocks; // Base offset in bytes. uint xe_texture_load_host_offset; // - std140 vector boundary - uint xe_texture_load_host_pitch; uint xe_texture_load_height_texels; xesl_constantBuffer_end(xe_texture_load_constants) #define XE_TEXTURE_LOAD_CONSTANT_BUFFER_BINDING \ xesl_constantBuffer_binding(xe_texture_load_constants, buffer(0)) struct XeTextureLoadInfo { bool is_tiled; bool is_3d; uint endian_32; xesl_uint2 resolution_scale; uint guest_offset; uint guest_pitch_aligned; uint guest_z_stride_block_rows_aligned; xesl_uint3 size_blocks; uint host_offset; uint host_pitch; uint height_texels; }; XeTextureLoadInfo XeTextureLoadGetInfo( xesl_function_param_constantBuffer(xe_texture_load_constants)) { XeTextureLoadInfo load_info; uint is_tiled_3d_endian_scale = xesl_constant( xe_texture_load_constants, xe_texture_load_is_tiled_3d_endian_scale); #ifdef XE_TEXTURE_LOAD_RESOLUTION_SCALED // Only resolved textures can be resolution-scaled, and resolving is only // possible to a tiled destination. load_info.is_tiled = true; #else load_info.is_tiled = (is_tiled_3d_endian_scale & 1u) != 0u; #endif load_info.is_3d = (is_tiled_3d_endian_scale & (1u << 1u)) != 0u; load_info.endian_32 = (is_tiled_3d_endian_scale >> 2u) & 3u; #ifdef XE_TEXTURE_LOAD_RESOLUTION_SCALED load_info.resolution_scale = ((xesl_uint_x2(is_tiled_3d_endian_scale)) >> xesl_uint2(4u, 6u)) & 3u; #else load_info.resolution_scale = xesl_uint2(1u, 1u); #endif load_info.guest_offset = xesl_constant(xe_texture_load_constants, xe_texture_load_guest_offset); load_info.guest_pitch_aligned = xesl_constant( xe_texture_load_constants, xe_texture_load_guest_pitch_aligned); load_info.guest_z_stride_block_rows_aligned = xesl_constant(xe_texture_load_constants, xe_texture_load_guest_z_stride_block_rows_aligned); load_info.size_blocks = xesl_constant(xe_texture_load_constants, xe_texture_load_size_blocks); load_info.host_offset = xesl_constant(xe_texture_load_constants, xe_texture_load_host_offset); load_info.host_pitch = xesl_constant(xe_texture_load_constants, xe_texture_load_host_pitch); load_info.height_texels = xesl_constant(xe_texture_load_constants, xe_texture_load_height_texels); 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, xesl_uint3 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, load_info.guest_z_stride_block_rows_aligned, bpb_log2, load_info.resolution_scale); #else int block_offset_guest; xesl_dont_flatten if (load_info.is_tiled) { xesl_dont_flatten if (load_info.is_3d) { block_offset_guest = XeTextureTiledOffset3D( xesl_int3(block_index), load_info.guest_pitch_aligned, load_info.guest_z_stride_block_rows_aligned, bpb_log2); } else { block_offset_guest = XeTextureTiledOffset2D( xesl_int2(block_index.xy), load_info.guest_pitch_aligned, bpb_log2); } } else { block_offset_guest = XeTextureGuestLinearOffset( xesl_int3(block_index), load_info.guest_pitch_aligned, load_info.guest_z_stride_block_rows_aligned, bpb); } return uint(int(load_info.guest_offset) + block_offset_guest); #endif } // 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); xesl_dont_flatten if (load_info.is_tiled) { offset = XeTextureTiledOddConsecutiveBlocksOffset(bpb_log2); } else { offset = 1u << (consecutive_blocks_log2 + bpb_log2); } return offset; #endif } #endif // XENIA_GPU_SHADERS_TEXTURE_LOAD_XESLI_