Integer num_format fetches now get their scale CPU-side instead of trying to guess in the shader. This is authoritative; no cvar. Both translators now use texture_integer_scale_bits after signs/gamma and before exponent bias. This alone fixes black screens and a bunch of rendering bugs across at least several dozen titles. This new information lives in the updated FormatInfo table, including fixed component widths. Resolve also has two new fixes. 8_8_8_8_GAMMA EDRAM sources can now decode through the PWL curve while still in linear space, before MSAA resolve or format conversion, then re-encoded for gamma destinations. This is also now default enabled via gamma_decode_pwl_resolve and has improved blowout in at least 4 titles, with no obvious regressions thus far. Full resolve can also now pack fixed destinations according to copy_dest_number.
990 lines
44 KiB
Plaintext
990 lines
44 KiB
Plaintext
/**
|
|
******************************************************************************
|
|
* 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_
|