/** ****************************************************************************** * 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_RESOLVE_XESLI_ #define XENIA_GPU_SHADERS_RESOLVE_XESLI_ #include "edram.xesli" #include "pixel_formats.xesli" #include "texture_address.xesli" #define kXenosCopySampleSelect_0 0u #define kXenosCopySampleSelect_1 1u #define kXenosCopySampleSelect_2 2u #define kXenosCopySampleSelect_3 3u #define kXenosCopySampleSelect_01 4u #define kXenosCopySampleSelect_23 5u #define kXenosCopySampleSelect_0123 6u push_const_begin_xe(b0, space0) #ifdef XE_RESOLVE_CLEAR uint2_xe xe_resolve_clear_value; #endif // xe::gpu::draw_util::ResolveEdramInfo. uint xe_resolve_edram_info; // xe::gpu::draw_util::ResolveCoordinateInfo. uint xe_resolve_coordinate_info; #ifndef XE_RESOLVE_CLEAR // Sanitized RB_COPY_DEST_INFO. uint xe_resolve_dest_info; // xe::gpu::draw_util::ResolveCopyDestCoordinateInfo. uint xe_resolve_dest_coordinate_info; #ifndef XE_RESOLVE_RESOLUTION_SCALED uint xe_resolve_dest_base; #endif #endif push_const_end_xe #define XE_RESOLVE_PUSH_CONST_BINDING push_const_binding_xe(buffer(0)) #ifndef XE_RESOLVE_CLEAR #if XE_RESOLVE_COPY_EDRAM_BYTE_BUFFER_ALIGNMENT == 4 #define XE_RESOLVE_COPY_EDRAM_BYTE_BUFFER_DECLARE_MACRO \ byte_buffer_align4_declare_xe #elif XE_RESOLVE_COPY_EDRAM_BYTE_BUFFER_ALIGNMENT == 8 #define XE_RESOLVE_COPY_EDRAM_BYTE_BUFFER_DECLARE_MACRO \ byte_buffer_align8_declare_xe #elif XE_RESOLVE_COPY_EDRAM_BYTE_BUFFER_ALIGNMENT == 16 #define XE_RESOLVE_COPY_EDRAM_BYTE_BUFFER_DECLARE_MACRO \ byte_buffer_align16_declare_xe #endif #ifdef XE_RESOLVE_COPY_EDRAM_BYTE_BUFFER_DECLARE_MACRO XE_RESOLVE_COPY_EDRAM_BYTE_BUFFER_DECLARE_MACRO(xe_resolve_edram, set=0, binding=0, t0, space0) #endif #define XE_RESOLVE_COPY_EDRAM_BINDING \ byte_buffer_binding_xe(xe_resolve_edram, buffer(2)) #endif struct XeResolveInfo { uint edram_pitch_tiles; uint edram_msaa_samples; // Always false for non-one-to-one resolve. bool edram_is_depth; uint edram_base_tiles; uint edram_format; uint edram_format_ints_log2; bool decode_pwl_gamma; uint2_xe resolution_scale; uint2_xe half_pixel_offset_fill_source; uint2_xe edram_offset_scaled; uint width_div_8_scaled; #ifdef XE_RESOLVE_CLEAR uint2_xe clear_value; #else uint dest_endian_128; bool dest_is_array; uint dest_slice; uint dest_format; uint dest_num_format; float dest_exp_bias_factor; bool dest_swap; 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; #endif // XE_RESOLVE_CLEAR }; XeResolveInfo XeResolveGetInfo(param_push_consts_xe) { XeResolveInfo resolve_info; uint edram_info = push_const_xe(xe_resolve_edram_info); uint coordinate_info = push_const_xe(xe_resolve_coordinate_info); resolve_info.edram_pitch_tiles = edram_info & ((1u << 10u) - 1u); resolve_info.edram_msaa_samples = (edram_info >> 10u) & ((1u << 2u) - 1u); resolve_info.edram_is_depth = (edram_info & (1u << 12u)) != 0u; resolve_info.edram_base_tiles = (edram_info >> 13u) & ((1u << 11u) - 1u); resolve_info.edram_format = (edram_info >> 24u) & ((1u << 4u) - 1u); resolve_info.edram_format_ints_log2 = (edram_info >> 28u) & 1u; resolve_info.decode_pwl_gamma = (edram_info & (1u << 30u)) != 0u; #ifdef XE_RESOLVE_RESOLUTION_SCALED resolve_info.resolution_scale = (uint_x2_xe(coordinate_info) >> uint2_xe(16u, 19u)) & 7u; if ((edram_info & (1u << 29u)) != 0u) { resolve_info.half_pixel_offset_fill_source = resolve_info.resolution_scale >> 1u; } else { resolve_info.half_pixel_offset_fill_source = uint_x2_xe(0u); } #else resolve_info.resolution_scale = uint_x2_xe(1u); resolve_info.half_pixel_offset_fill_source = uint_x2_xe(0u); #endif resolve_info.edram_offset_scaled = (((uint_x2_xe(coordinate_info) >> uint2_xe(0u, 4u)) & ((uint_x2_xe(1u) << uint2_xe(4u, 1u)) - 1u)) << 3u) * resolve_info.resolution_scale; resolve_info.width_div_8_scaled = ((coordinate_info >> 5u) & ((1u << 11u) - 1u)) * resolve_info.resolution_scale.x; #ifdef XE_RESOLVE_CLEAR resolve_info.clear_value = push_const_xe(xe_resolve_clear_value); #else uint dest_info = push_const_xe(xe_resolve_dest_info); uint dest_coordinate_info = push_const_xe(xe_resolve_dest_coordinate_info); resolve_info.dest_endian_128 = dest_info & ((1u << 3u) - 1u); resolve_info.dest_is_array = (dest_info & (1u << 3u)) != 0u; resolve_info.dest_slice = (dest_info >> 4u) & ((1u << 3u) - 1u); resolve_info.dest_format = (dest_info >> 7u) & ((1u << 6u) - 1u); resolve_info.dest_num_format = (dest_info >> 13u) & ((1u << 3u) - 1u); resolve_info.dest_exp_bias_factor = int_bits_to_float_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_macro_tiles = dest_coordinate_info & ((1u << 10u) - 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)) << 3u) * resolve_info.resolution_scale; resolve_info.sample_select = (dest_coordinate_info >> 28u) & ((1u << 3u) - 1u); #ifndef XE_RESOLVE_RESOLUTION_SCALED resolve_info.dest_base = push_const_xe(xe_resolve_dest_base); #else resolve_info.dest_base = 0; #endif #endif // XE_RESOLVE_CLEAR return resolve_info; } uint XeResolveEdramPixelStrideBytes(XeResolveInfo resolve_info) { return 4u << (resolve_info.edram_format_ints_log2 + uint(resolve_info.edram_msaa_samples >= kXenosMsaaSamples_4X)); } #ifndef XE_RESOLVE_CLEAR uint XeResolveDestPixelAddress(const XeResolveInfo resolve_info, uint2_xe host_position, const uint bytes_per_block_log2) { host_position += resolve_info.dest_xy_offset_scaled; uint address; uint2_xe guest_position = host_position; #ifdef XE_RESOLVE_RESOLUTION_SCALED const XeniaTextureResolutionScaledAddressing resolution_scaled_addressing = XeniaTextureGetResolutionScaledAddressing( host_position.xy, resolve_info.resolution_scale, 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_block_log2)); } else { address = uint(XenosTextureTiledAddress2D( int2_xe(guest_position), resolve_info.dest_row_pitch_macro_tiles, bytes_per_block_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; } // 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 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_block_log2) { #ifdef XE_RESOLVE_RESOLUTION_SCALED return x << bytes_per_block_log2; #else return uint(XenosTextureTiledAddressXInMacroXor(int(x), bytes_per_block_log2)); #endif } #define kXenosCopySampleSelect_0 0u #define kXenosCopySampleSelect_1 1u #define kXenosCopySampleSelect_2 2u #define kXenosCopySampleSelect_3 3u #define kXenosCopySampleSelect_01 4u #define kXenosCopySampleSelect_23 5u #define kXenosCopySampleSelect_0123 6u uint XeResolveFirstSampleIndex(uint sample_select) { uint sample_index; if (sample_select <= kXenosCopySampleSelect_3) { sample_index = sample_select; } else if (sample_select == kXenosCopySampleSelect_23) { sample_index = 2u; } else { sample_index = 0u; } return sample_index; } // Offset to the first sample to participate in averaging (or the sample to be // copied if not averaging). uint XeResolveColorCopySourcePixelAddressBytesYHalfPixelOffsetFilling( XeResolveInfo resolve_info, uint2_xe pixel_index) { return XeEdramOffsetBytes( uint2_xe(pixel_index.x, max(pixel_index.y, resolve_info.half_pixel_offset_fill_source.y)) + resolve_info.edram_offset_scaled, resolve_info.edram_base_tiles, true, resolve_info.edram_pitch_tiles, resolve_info.edram_msaa_samples, false, resolve_info.edram_format_ints_log2, XeResolveFirstSampleIndex(resolve_info.sample_select), resolve_info.resolution_scale); } // Not using arrays for multi-pixel function arguments because indexable temps // are generated for them by FXC, that may be compiled unoptimally by the host // GPU driver. void XeResolveUnpack32bpp2Samples( uint2_xe packed, uint format, out_param_xe(float4_xe, sample_0), out_param_xe(float4_xe, sample_1)) { switch (format) { case kXenosColorRenderTargetFormat_8_8_8_8: case kXenosColorRenderTargetFormat_8_8_8_8_GAMMA: sample_0 = XeUnpackR8G8B8A8UNorm(packed.x); sample_1 = XeUnpackR8G8B8A8UNorm(packed.y); break; case kXenosColorRenderTargetFormat_2_10_10_10: case kXenosColorRenderTargetFormat_2_10_10_10_AS_10_10_10_10: sample_0 = XeUnpackR10G10B10A2UNorm(packed.x); sample_1 = XeUnpackR10G10B10A2UNorm(packed.y); break; case kXenosColorRenderTargetFormat_2_10_10_10_FLOAT: case kXenosColorRenderTargetFormat_2_10_10_10_FLOAT_AS_16_16_16_16: sample_0 = XeUnpackR10G10B10A2Float(packed.x); sample_1 = XeUnpackR10G10B10A2Float(packed.y); break; case kXenosColorRenderTargetFormat_16_16: sample_0 = float4_xe(XeUnpackR16G16Edram(packed.x), 0.0f, 0.0f); sample_1 = float4_xe(XeUnpackR16G16Edram(packed.y), 0.0f, 0.0f); break; case kXenosColorRenderTargetFormat_16_16_FLOAT: sample_0 = float4_xe(unpack_half_2x16_xe(packed.x), 0.0f, 0.0f); sample_1 = float4_xe(unpack_half_2x16_xe(packed.y), 0.0f, 0.0f); break; default: // Treat as 32_FLOAT. sample_0 = float2_xe(uint_bits_to_float_xe(packed.x), 0.0f).xyyy; sample_1 = float2_xe(uint_bits_to_float_xe(packed.y), 0.0f).xyyy; break; } } void XeResolveUnpack32bpp4Samples( uint4_xe packed, uint format, out_param_xe(float4_xe, sample_0), out_param_xe(float4_xe, sample_1), out_param_xe(float4_xe, sample_2), out_param_xe(float4_xe, sample_3)) { switch (format) { case kXenosColorRenderTargetFormat_8_8_8_8: case kXenosColorRenderTargetFormat_8_8_8_8_GAMMA: sample_0 = XeUnpackR8G8B8A8UNorm(packed.x); sample_1 = XeUnpackR8G8B8A8UNorm(packed.y); sample_2 = XeUnpackR8G8B8A8UNorm(packed.z); sample_3 = XeUnpackR8G8B8A8UNorm(packed.w); break; case kXenosColorRenderTargetFormat_2_10_10_10: case kXenosColorRenderTargetFormat_2_10_10_10_AS_10_10_10_10: sample_0 = XeUnpackR10G10B10A2UNorm(packed.x); sample_1 = XeUnpackR10G10B10A2UNorm(packed.y); sample_2 = XeUnpackR10G10B10A2UNorm(packed.z); sample_3 = XeUnpackR10G10B10A2UNorm(packed.w); break; case kXenosColorRenderTargetFormat_2_10_10_10_FLOAT: case kXenosColorRenderTargetFormat_2_10_10_10_FLOAT_AS_16_16_16_16: sample_0 = XeUnpackR10G10B10A2Float(packed.x); sample_1 = XeUnpackR10G10B10A2Float(packed.y); sample_2 = XeUnpackR10G10B10A2Float(packed.z); sample_3 = XeUnpackR10G10B10A2Float(packed.w); break; case kXenosColorRenderTargetFormat_16_16: sample_0 = float4_xe(XeUnpackR16G16Edram(packed.x), 0.0f, 0.0f); sample_1 = float4_xe(XeUnpackR16G16Edram(packed.y), 0.0f, 0.0f); sample_2 = float4_xe(XeUnpackR16G16Edram(packed.z), 0.0f, 0.0f); sample_3 = float4_xe(XeUnpackR16G16Edram(packed.w), 0.0f, 0.0f); break; case kXenosColorRenderTargetFormat_16_16_FLOAT: sample_0 = float4_xe(unpack_half_2x16_xe(packed.x), 0.0f, 0.0f); sample_1 = float4_xe(unpack_half_2x16_xe(packed.y), 0.0f, 0.0f); sample_2 = float4_xe(unpack_half_2x16_xe(packed.z), 0.0f, 0.0f); sample_3 = float4_xe(unpack_half_2x16_xe(packed.w), 0.0f, 0.0f); break; default: // Treat as 32_FLOAT. sample_0 = float2_xe(uint_bits_to_float_xe(packed.x), 0.0f).xyyy; sample_1 = float2_xe(uint_bits_to_float_xe(packed.y), 0.0f).xyyy; sample_2 = float2_xe(uint_bits_to_float_xe(packed.z), 0.0f).xyyy; sample_3 = float2_xe(uint_bits_to_float_xe(packed.w), 0.0f).xyyy; break; } } void XeResolveUnpack32bpp8RedSamples( uint4_xe packed_0123, uint4_xe packed_4567, uint format, bool swap, out_param_xe(float4_xe, samples_0123), out_param_xe(float4_xe, samples_4567)) { switch (format) { case kXenosColorRenderTargetFormat_8_8_8_8: case kXenosColorRenderTargetFormat_8_8_8_8_GAMMA: { uint shift = swap ? 16u : 0u; samples_0123 = XeUnpackR8UNormX4(packed_0123 >> shift); samples_4567 = XeUnpackR8UNormX4(packed_4567 >> shift); } break; case kXenosColorRenderTargetFormat_2_10_10_10: case kXenosColorRenderTargetFormat_2_10_10_10_AS_10_10_10_10: { uint shift = swap ? 20u : 0u; samples_0123 = XeUnpackR10UNormX4(packed_0123 >> shift); samples_4567 = XeUnpackR10UNormX4(packed_4567 >> shift); } break; case kXenosColorRenderTargetFormat_2_10_10_10_FLOAT: case kXenosColorRenderTargetFormat_2_10_10_10_FLOAT_AS_16_16_16_16: { uint shift = swap ? 20u : 0u; samples_0123 = XeUnpackR10FloatX4(packed_0123 >> shift); samples_4567 = XeUnpackR10FloatX4(packed_4567 >> shift); } break; case kXenosColorRenderTargetFormat_16_16: samples_0123 = XeUnpackR16EdramX4(packed_0123); samples_4567 = XeUnpackR16EdramX4(packed_4567); break; case kXenosColorRenderTargetFormat_16_16_FLOAT: samples_0123.x = unpack_half_2x16_xe(packed_0123.x).x; samples_0123.y = unpack_half_2x16_xe(packed_0123.y).x; samples_0123.z = unpack_half_2x16_xe(packed_0123.z).x; samples_0123.w = unpack_half_2x16_xe(packed_0123.w).x; samples_4567.x = unpack_half_2x16_xe(packed_4567.x).x; samples_4567.y = unpack_half_2x16_xe(packed_4567.y).x; samples_4567.z = unpack_half_2x16_xe(packed_4567.z).x; samples_4567.w = unpack_half_2x16_xe(packed_4567.w).x; break; default: // Treat as 32_FLOAT. samples_0123 = uint_bits_to_float_xe(packed_0123); samples_4567 = uint_bits_to_float_xe(packed_4567); break; } } void XeResolveUnpack64bpp2Samples( uint4_xe packed, uint format, out_param_xe(float4_xe, sample_0), out_param_xe(float4_xe, sample_1)) { switch (format) { case kXenosColorRenderTargetFormat_16_16_16_16: sample_0 = XeUnpackR16G16B16A16Edram(packed.xy); sample_1 = XeUnpackR16G16B16A16Edram(packed.zw); break; case kXenosColorRenderTargetFormat_16_16_16_16_FLOAT: sample_0.xy = unpack_half_2x16_xe(packed.x); sample_0.zw = unpack_half_2x16_xe(packed.y); sample_1.xy = unpack_half_2x16_xe(packed.z); sample_1.zw = unpack_half_2x16_xe(packed.w); break; default: // Treat as 32_32_FLOAT. sample_0 = float4_xe(uint_bits_to_float_xe(packed.xy), 0.0f, 0.0f); sample_1 = float4_xe(uint_bits_to_float_xe(packed.zw), 0.0f, 0.0f); break; } } void XeResolveUnpack64bpp4Samples( uint4_xe packed_01, uint4_xe packed_23, uint format, out_param_xe(float4_xe, sample_0), out_param_xe(float4_xe, sample_1), out_param_xe(float4_xe, sample_2), out_param_xe(float4_xe, sample_3)) { switch (format) { case kXenosColorRenderTargetFormat_16_16_16_16: sample_0 = XeUnpackR16G16B16A16Edram(packed_01.xy); sample_1 = XeUnpackR16G16B16A16Edram(packed_01.zw); sample_2 = XeUnpackR16G16B16A16Edram(packed_23.xy); sample_3 = XeUnpackR16G16B16A16Edram(packed_23.zw); break; case kXenosColorRenderTargetFormat_16_16_16_16_FLOAT: sample_0.xy = unpack_half_2x16_xe(packed_01.x); sample_0.zw = unpack_half_2x16_xe(packed_01.y); sample_1.xy = unpack_half_2x16_xe(packed_01.z); sample_1.zw = unpack_half_2x16_xe(packed_01.w); sample_2.xy = unpack_half_2x16_xe(packed_23.x); sample_2.zw = unpack_half_2x16_xe(packed_23.y); sample_3.xy = unpack_half_2x16_xe(packed_23.z); sample_3.zw = unpack_half_2x16_xe(packed_23.w); break; default: // Treat as 32_32_FLOAT. sample_0 = float4_xe(uint_bits_to_float_xe(packed_01.xy), 0.0f, 0.0f); sample_1 = float4_xe(uint_bits_to_float_xe(packed_01.zw), 0.0f, 0.0f); sample_2 = float4_xe(uint_bits_to_float_xe(packed_23.xy), 0.0f, 0.0f); sample_3 = float4_xe(uint_bits_to_float_xe(packed_23.zw), 0.0f, 0.0f); break; } } void XeResolveUnpack64bpp8RedUnswappedSamples( uint4_xe packed_0123, uint4_xe packed_4567, uint format, out_param_xe(float4_xe, samples_0123), out_param_xe(float4_xe, samples_4567)) { switch (format) { case kXenosColorRenderTargetFormat_16_16_16_16: samples_0123 = XeUnpackR16EdramX4(packed_0123); samples_4567 = XeUnpackR16EdramX4(packed_4567); break; case kXenosColorRenderTargetFormat_16_16_16_16_FLOAT: samples_0123.x = unpack_half_2x16_xe(packed_0123.x).x; samples_0123.y = unpack_half_2x16_xe(packed_0123.y).x; samples_0123.z = unpack_half_2x16_xe(packed_0123.z).x; samples_0123.w = unpack_half_2x16_xe(packed_0123.w).x; samples_4567.x = unpack_half_2x16_xe(packed_4567.x).x; samples_4567.y = unpack_half_2x16_xe(packed_4567.y).x; samples_4567.z = unpack_half_2x16_xe(packed_4567.z).x; samples_4567.w = unpack_half_2x16_xe(packed_4567.w).x; break; default: // Treat as 32_32_FLOAT. samples_0123 = uint_bits_to_float_xe(packed_0123); samples_4567 = uint_bits_to_float_xe(packed_4567); break; } } #if XE_RESOLVE_COPY_EDRAM_BYTE_BUFFER_ALIGNMENT == 4 void XeResolveLoad2RGBAUnswappedPixelSamplesFromRaw( param_byte_buffer_xe(xe_resolve_edram) param_next_after_byte_buffer_xe uint sample_address_bytes, uint pixel_stride_bytes, uint format_ints_log2, uint format, out_param_xe(float4_xe, pixel_0), out_param_xe(float4_xe, pixel_1)) { dont_flatten_xe if (format_ints_log2 != 0u) { uint4_xe packed; dont_flatten_xe if (pixel_stride_bytes == 8u) { packed = byte_buffer_align4_load16u_xe( xe_resolve_edram, sample_address_bytes); } else { packed.xy = byte_buffer_align4_load8u_xe( xe_resolve_edram, sample_address_bytes); packed.zw = byte_buffer_align4_load8u_xe( xe_resolve_edram, sample_address_bytes + pixel_stride_bytes); } XeResolveUnpack64bpp2Samples(packed, format, pixel_0, pixel_1); } else { uint2_xe packed; dont_flatten_xe if (pixel_stride_bytes == 4u) { packed = byte_buffer_align4_load8u_xe( xe_resolve_edram, sample_address_bytes); } else { packed.x = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes); packed.y = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + pixel_stride_bytes); } XeResolveUnpack32bpp2Samples(packed, format, pixel_0, pixel_1); } } void XeResolveLoad4RGBAUnswappedPixelSamplesFromRaw( param_byte_buffer_xe(xe_resolve_edram) param_next_after_byte_buffer_xe uint sample_address_bytes, uint pixel_stride_bytes, uint format_ints_log2, uint format, out_param_xe(float4_xe, pixel_0), out_param_xe(float4_xe, pixel_1), out_param_xe(float4_xe, pixel_2), out_param_xe(float4_xe, pixel_3)) { dont_flatten_xe if (format_ints_log2 != 0u) { uint4_xe packed_01, packed_23; dont_flatten_xe if (pixel_stride_bytes == 8u) { packed_01 = byte_buffer_align4_load16u_xe( xe_resolve_edram, sample_address_bytes); packed_23 = byte_buffer_align4_load16u_xe( xe_resolve_edram, sample_address_bytes + 16u); } else { packed_01.xy = byte_buffer_align4_load8u_xe( xe_resolve_edram, sample_address_bytes); packed_01.zw = byte_buffer_align4_load8u_xe( xe_resolve_edram, sample_address_bytes + pixel_stride_bytes); packed_23.xy = byte_buffer_align4_load8u_xe( xe_resolve_edram, sample_address_bytes + 2u * pixel_stride_bytes); packed_23.zw = byte_buffer_align4_load8u_xe( xe_resolve_edram, sample_address_bytes + 3u * pixel_stride_bytes); } XeResolveUnpack64bpp4Samples(packed_01, packed_23, format, pixel_0, pixel_1, pixel_2, pixel_3); } else { uint4_xe packed; dont_flatten_xe if (pixel_stride_bytes == 4u) { packed = byte_buffer_align4_load16u_xe( xe_resolve_edram, sample_address_bytes); } else { packed.x = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes); packed.y = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + pixel_stride_bytes); packed.z = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + 2u * pixel_stride_bytes); packed.w = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + 3u * pixel_stride_bytes); } XeResolveUnpack32bpp4Samples(packed, format, pixel_0, pixel_1, pixel_2, pixel_3); } } // For red/blue swapping for 64bpp, pre-add 4 to sample_address_bytes. void XeResolveLoad8RedPixelSamplesFromRaw( param_byte_buffer_xe(xe_resolve_edram) param_next_after_byte_buffer_xe uint sample_address_bytes, uint pixel_stride_bytes, uint format_ints_log2, uint format, bool swap_32bpp, out_param_xe(float4_xe, pixels_0123), out_param_xe(float4_xe, pixels_4567)) { uint4_xe packed_0123, packed_4567; dont_flatten_xe if (pixel_stride_bytes == 4u) { packed_0123 = byte_buffer_align4_load16u_xe( xe_resolve_edram, sample_address_bytes); packed_4567 = byte_buffer_align4_load16u_xe( xe_resolve_edram, sample_address_bytes + 16u); } else { packed_0123.x = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes); packed_0123.y = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + pixel_stride_bytes); packed_0123.z = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + 2u * pixel_stride_bytes); packed_0123.w = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + 3u * pixel_stride_bytes); packed_4567.x = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + 4u * pixel_stride_bytes); packed_4567.y = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + 5u * pixel_stride_bytes); packed_4567.z = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + 6u * pixel_stride_bytes); packed_4567.w = byte_buffer_align4_load4_xe( xe_resolve_edram, sample_address_bytes + 7u * pixel_stride_bytes); } dont_flatten_xe if (format_ints_log2 != 0u) { XeResolveUnpack64bpp8RedUnswappedSamples(packed_0123, packed_4567, format, pixels_0123, pixels_4567); } else { XeResolveUnpack32bpp8RedSamples(packed_0123, packed_4567, format, swap_32bpp, pixels_0123, pixels_4567); } } // Gamma RTs store linear 10 bit color through an 8 bit PWL curve. // Host sRGB is not a substitute. Some titles appear overexposed if full // 8_8_8_8_GAMMA resolves average the encoded bytes directly (5345085D, // 45410934), so we decode before MSAA averaging and encode again only // for compatible 8_8_8_8 destinations. float XePWLGammaToLinear(float gamma) { gamma = saturate_xe(gamma); float scale; float offset; if (gamma >= 96.0f / 255.0f) { if (gamma >= 192.0f / 255.0f) { scale = 8.0f / 1024.0f; offset = -1024.0f; } else { scale = 4.0f / 1024.0f; offset = -256.0f; } } else { if (gamma >= 64.0f / 255.0f) { scale = 2.0f / 1024.0f; offset = -64.0f; } else { scale = 1.0f / 1024.0f; offset = 0.0f; } } float linear_value = gamma * (255.0f * 1024.0f) * scale + offset; linear_value += trunc(linear_value * scale); return linear_value * (1.0f / 1023.0f); } float3_xe XePWLGammaToLinear3(float3_xe gamma) { return float3_xe(XePWLGammaToLinear(gamma.x), XePWLGammaToLinear(gamma.y), XePWLGammaToLinear(gamma.z)); } float4_xe XePWLGammaToLinear4(float4_xe gamma) { return float4_xe(XePWLGammaToLinear(gamma.x), XePWLGammaToLinear(gamma.y), XePWLGammaToLinear(gamma.z), XePWLGammaToLinear(gamma.w)); } float XeLinearToPWLGamma(float linear_value) { linear_value = saturate_xe(linear_value); float scale; float offset; if (linear_value >= 128.0f / 1023.0f) { if (linear_value >= 512.0f / 1023.0f) { scale = 1023.0f / 8.0f; offset = 128.0f / 255.0f; } else { scale = 1023.0f / 4.0f; offset = 64.0f / 255.0f; } } else { if (linear_value >= 64.0f / 1023.0f) { scale = 1023.0f / 2.0f; offset = 32.0f / 255.0f; } else { scale = 1023.0f; offset = 0.0f; } } return trunc(linear_value * scale) * (1.0f / 255.0f) + offset; } float3_xe XeLinearToPWLGamma3(float3_xe linear_value) { return float3_xe(XeLinearToPWLGamma(linear_value.x), XeLinearToPWLGamma(linear_value.y), XeLinearToPWLGamma(linear_value.z)); } bool XeResolveSourceUsesPWLGamma(XeResolveInfo resolve_info) { return resolve_info.decode_pwl_gamma && resolve_info.edram_format == kXenosColorRenderTargetFormat_8_8_8_8_GAMMA; } bool XeResolveDestStoresPWLGamma(XeResolveInfo resolve_info) { // Resolve constants don't carry a destination gamma bit. Treat an // unsigned 8_8_8_8 destination as PWL gamma storage. bool dest_number_is_unorm = resolve_info.dest_num_format == kXenosSurfaceNumberFormat_UnsignedRepeatingFraction; bool dest_is_8888 = resolve_info.dest_format == kXenosFormat_8_8_8_8 || resolve_info.dest_format == kXenosFormat_8_8_8_8_A || resolve_info.dest_format == kXenosFormat_8_8_8_8_AS_16_16_16_16; return dest_number_is_unorm && dest_is_8888; } void XeResolvePWLGammaToLinearRGB(inout_param_xe(float4_xe, pixel)) { pixel.rgb = XePWLGammaToLinear3(pixel.rgb); } void XeResolveLinearToPWLGammaRGB(inout_param_xe(float4_xe, pixel)) { pixel.rgb = XeLinearToPWLGamma3(pixel.rgb); } void XeResolveDecodePWLGammaSource( XeResolveInfo resolve_info, inout_param_xe(float4_xe, pixel)) { // Source gamma is RGB only. 8_8_8_8_GAMMA still stores alpha as ordinary // fixed data, so alpha needs to stay with normal resolve. dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) { XeResolvePWLGammaToLinearRGB(pixel); } } void XeResolveEncodePWLGammaDest( XeResolveInfo resolve_info, inout_param_xe(float4_xe, pixel)) { // Only re-encode when the source was PWL gamma and the destination is the // 8_8_8_8 UNORM storage we treat as the same PWL byte stream. dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info) && XeResolveDestStoresPWLGamma(resolve_info)) { XeResolveLinearToPWLGammaRGB(pixel); } } void XeResolveLoad2RGBAColors( param_byte_buffer_xe(xe_resolve_edram) param_next_after_byte_buffer_xe XeResolveInfo resolve_info, uint address_bytes, out_param_xe(float4_xe, pixel_0), out_param_xe(float4_xe, pixel_1)) { uint pixel_stride_bytes = XeResolveEdramPixelStrideBytes(resolve_info); XeResolveLoad2RGBAUnswappedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, pixel_0, pixel_1); XeResolveDecodePWLGammaSource(resolve_info, pixel_0); XeResolveDecodePWLGammaSource(resolve_info, pixel_1); float exp_bias = resolve_info.dest_exp_bias_factor; dont_flatten_xe if (resolve_info.sample_select >= kXenosCopySampleSelect_01) { uint tile_row_stride_bytes = 4u * 80u * resolve_info.resolution_scale.x; exp_bias *= 0.5f; float4_xe msaa_resolve_pixel_0, msaa_resolve_pixel_1; XeResolveLoad2RGBAUnswappedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes + tile_row_stride_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, msaa_resolve_pixel_0, msaa_resolve_pixel_1); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1); pixel_0 += msaa_resolve_pixel_0; pixel_1 += msaa_resolve_pixel_1; dont_flatten_xe if (resolve_info.sample_select >= kXenosCopySampleSelect_0123) { uint sample_stride_bytes = 4u << resolve_info.edram_format_ints_log2; exp_bias *= 0.5f; XeResolveLoad2RGBAUnswappedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes + sample_stride_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, msaa_resolve_pixel_0, msaa_resolve_pixel_1); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1); pixel_0 += msaa_resolve_pixel_0; pixel_1 += msaa_resolve_pixel_1; XeResolveLoad2RGBAUnswappedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes + tile_row_stride_bytes + sample_stride_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, msaa_resolve_pixel_0, msaa_resolve_pixel_1); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1); pixel_0 += msaa_resolve_pixel_0; pixel_1 += msaa_resolve_pixel_1; } } pixel_0 *= exp_bias; pixel_1 *= exp_bias; XeResolveEncodePWLGammaDest(resolve_info, pixel_0); XeResolveEncodePWLGammaDest(resolve_info, pixel_1); dont_flatten_xe if (resolve_info.dest_swap) { pixel_0 = pixel_0.bgra; pixel_1 = pixel_1.bgra; } } void XeResolveLoad4RGBAColors( param_byte_buffer_xe(xe_resolve_edram) param_next_after_byte_buffer_xe XeResolveInfo resolve_info, uint address_bytes, out_param_xe(float4_xe, pixel_0), out_param_xe(float4_xe, pixel_1), out_param_xe(float4_xe, pixel_2), out_param_xe(float4_xe, pixel_3)) { uint pixel_stride_bytes = XeResolveEdramPixelStrideBytes(resolve_info); XeResolveLoad4RGBAUnswappedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, pixel_0, pixel_1, pixel_2, pixel_3); XeResolveDecodePWLGammaSource(resolve_info, pixel_0); XeResolveDecodePWLGammaSource(resolve_info, pixel_1); XeResolveDecodePWLGammaSource(resolve_info, pixel_2); XeResolveDecodePWLGammaSource(resolve_info, pixel_3); float exp_bias = resolve_info.dest_exp_bias_factor; dont_flatten_xe if (resolve_info.sample_select >= kXenosCopySampleSelect_01) { uint tile_row_stride_bytes = 4u * 80u * resolve_info.resolution_scale.x; exp_bias *= 0.5f; float4_xe msaa_resolve_pixel_0; float4_xe msaa_resolve_pixel_1; float4_xe msaa_resolve_pixel_2; float4_xe msaa_resolve_pixel_3; XeResolveLoad4RGBAUnswappedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes + tile_row_stride_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, msaa_resolve_pixel_0, msaa_resolve_pixel_1, msaa_resolve_pixel_2, msaa_resolve_pixel_3); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_2); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_3); pixel_0 += msaa_resolve_pixel_0; pixel_1 += msaa_resolve_pixel_1; pixel_2 += msaa_resolve_pixel_2; pixel_3 += msaa_resolve_pixel_3; dont_flatten_xe if (resolve_info.sample_select >= kXenosCopySampleSelect_0123) { uint sample_stride_bytes = 4u << resolve_info.edram_format_ints_log2; exp_bias *= 0.5f; XeResolveLoad4RGBAUnswappedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes + sample_stride_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, msaa_resolve_pixel_0, msaa_resolve_pixel_1, msaa_resolve_pixel_2, msaa_resolve_pixel_3); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_2); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_3); pixel_0 += msaa_resolve_pixel_0; pixel_1 += msaa_resolve_pixel_1; pixel_2 += msaa_resolve_pixel_2; pixel_3 += msaa_resolve_pixel_3; XeResolveLoad4RGBAUnswappedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes + tile_row_stride_bytes + sample_stride_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, msaa_resolve_pixel_0, msaa_resolve_pixel_1, msaa_resolve_pixel_2, msaa_resolve_pixel_3); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_0); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_1); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_2); XeResolveDecodePWLGammaSource(resolve_info, msaa_resolve_pixel_3); pixel_0 += msaa_resolve_pixel_0; pixel_1 += msaa_resolve_pixel_1; pixel_2 += msaa_resolve_pixel_2; pixel_3 += msaa_resolve_pixel_3; } } pixel_0 *= exp_bias; pixel_1 *= exp_bias; pixel_2 *= exp_bias; pixel_3 *= exp_bias; XeResolveEncodePWLGammaDest(resolve_info, pixel_0); XeResolveEncodePWLGammaDest(resolve_info, pixel_1); XeResolveEncodePWLGammaDest(resolve_info, pixel_2); XeResolveEncodePWLGammaDest(resolve_info, pixel_3); dont_flatten_xe if (resolve_info.dest_swap) { pixel_0 = pixel_0.bgra; pixel_1 = pixel_1.bgra; pixel_2 = pixel_2.bgra; pixel_3 = pixel_3.bgra; } } void XeResolveLoad8RedColors( param_byte_buffer_xe(xe_resolve_edram) param_next_after_byte_buffer_xe XeResolveInfo resolve_info, uint address_bytes, out_param_xe(float4_xe, pixels_0123), out_param_xe(float4_xe, pixels_4567)) { uint pixel_stride_bytes = XeResolveEdramPixelStrideBytes(resolve_info); if (resolve_info.dest_swap && resolve_info.edram_format_ints_log2 != 0u) { // Likely want to load the blue part from the right half for 64bpp. address_bytes += 4u; } XeResolveLoad8RedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, resolve_info.dest_swap, pixels_0123, pixels_4567); dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) { pixels_0123 = XePWLGammaToLinear4(pixels_0123); pixels_4567 = XePWLGammaToLinear4(pixels_4567); } float exp_bias = resolve_info.dest_exp_bias_factor; dont_flatten_xe if (resolve_info.sample_select >= kXenosCopySampleSelect_01) { uint tile_row_stride_bytes = 4u * 80u * resolve_info.resolution_scale.x; exp_bias *= 0.5f; float4_xe msaa_resolve_pixels_0123, msaa_resolve_pixels_4567; XeResolveLoad8RedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes + tile_row_stride_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, resolve_info.dest_swap, msaa_resolve_pixels_0123, msaa_resolve_pixels_4567); dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) { msaa_resolve_pixels_0123 = XePWLGammaToLinear4(msaa_resolve_pixels_0123); msaa_resolve_pixels_4567 = XePWLGammaToLinear4(msaa_resolve_pixels_4567); } pixels_0123 += msaa_resolve_pixels_0123; pixels_4567 += msaa_resolve_pixels_4567; dont_flatten_xe if (resolve_info.sample_select >= kXenosCopySampleSelect_0123) { uint sample_stride_bytes = 4u << resolve_info.edram_format_ints_log2; exp_bias *= 0.5f; XeResolveLoad8RedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes + sample_stride_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, resolve_info.dest_swap, msaa_resolve_pixels_0123, msaa_resolve_pixels_4567); dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) { msaa_resolve_pixels_0123 = XePWLGammaToLinear4(msaa_resolve_pixels_0123); msaa_resolve_pixels_4567 = XePWLGammaToLinear4(msaa_resolve_pixels_4567); } pixels_0123 += msaa_resolve_pixels_0123; pixels_4567 += msaa_resolve_pixels_4567; XeResolveLoad8RedPixelSamplesFromRaw( pass_byte_buffer_xe(xe_resolve_edram) pass_next_after_byte_buffer_xe address_bytes + tile_row_stride_bytes + sample_stride_bytes, pixel_stride_bytes, resolve_info.edram_format_ints_log2, resolve_info.edram_format, resolve_info.dest_swap, msaa_resolve_pixels_0123, msaa_resolve_pixels_4567); dont_flatten_xe if (XeResolveSourceUsesPWLGamma(resolve_info)) { msaa_resolve_pixels_0123 = XePWLGammaToLinear4(msaa_resolve_pixels_0123); msaa_resolve_pixels_4567 = XePWLGammaToLinear4(msaa_resolve_pixels_4567); } pixels_0123 += msaa_resolve_pixels_0123; pixels_4567 += msaa_resolve_pixels_4567; } } pixels_0123 *= exp_bias; pixels_4567 *= exp_bias; } #endif // XE_RESOLVE_COPY_EDRAM_BYTE_BUFFER_ALIGNMENT == 4 uint4_xe XeResolveSwapRedBlue_8_8_8_8(uint4_xe pixels) { return (pixels & ~0xFF00FFu) | ((pixels & 0xFFu) << 16u) | ((pixels >> 16u) & 0xFFu); } uint4_xe XeResolveSwapRedBlue_2_10_10_10(uint4_xe pixels) { return (pixels & ~0x3FF003FF) | ((pixels & 0x3FFu) << 20u) | ((pixels >> 20u) & 0x3FFu); } void XeResolveSwap8PixelsRedBlue32bpp( XeResolveInfo resolve_info, inout_param_xe(uint4_xe, pixels_0123), inout_param_xe(uint4_xe, pixels_4567)) { dont_flatten_xe if (resolve_info.dest_swap) { switch (resolve_info.edram_format) { case kXenosColorRenderTargetFormat_8_8_8_8: case kXenosColorRenderTargetFormat_8_8_8_8_GAMMA: pixels_0123 = XeResolveSwapRedBlue_8_8_8_8(pixels_0123); pixels_4567 = XeResolveSwapRedBlue_8_8_8_8(pixels_4567); break; case kXenosColorRenderTargetFormat_2_10_10_10: case kXenosColorRenderTargetFormat_2_10_10_10_FLOAT: case kXenosColorRenderTargetFormat_2_10_10_10_AS_10_10_10_10: case kXenosColorRenderTargetFormat_2_10_10_10_FLOAT_AS_16_16_16_16: pixels_0123 = XeResolveSwapRedBlue_2_10_10_10(pixels_0123); pixels_4567 = XeResolveSwapRedBlue_2_10_10_10(pixels_4567); break; } } } void XeResolveSwap4PixelsRedBlue64bpp( XeResolveInfo resolve_info, inout_param_xe(uint4_xe, pixels_01), inout_param_xe(uint4_xe, pixels_23)) { dont_flatten_xe if (resolve_info.dest_swap) { dont_flatten_xe if (resolve_info.edram_format == kXenosColorRenderTargetFormat_16_16_16_16 || resolve_info.edram_format == kXenosColorRenderTargetFormat_16_16_16_16_FLOAT) { pixels_01 = (pixels_01 & ~0xFFFFu) | (pixels_01.yxwz & 0xFFFFu); pixels_23 = (pixels_23 & ~0xFFFFu) | (pixels_23.yxwz & 0xFFFFu); } } } #endif #endif // XENIA_GPU_SHADERS_RESOLVE_XESLI_