968 lines
42 KiB
Plaintext
968 lines
42 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_PIXEL_FORMATS_XESLI_
|
|
#define XENIA_GPU_SHADERS_PIXEL_FORMATS_XESLI_
|
|
|
|
#include "../../ui/shaders/xesl.xesli"
|
|
|
|
#define kXenosFormat_1_REVERSE 0u
|
|
#define kXenosFormat_1 1u
|
|
#define kXenosFormat_8 2u
|
|
#define kXenosFormat_1_5_5_5 3u
|
|
#define kXenosFormat_5_6_5 4u
|
|
#define kXenosFormat_6_5_5 5u
|
|
#define kXenosFormat_8_8_8_8 6u
|
|
#define kXenosFormat_2_10_10_10 7u
|
|
#define kXenosFormat_8_A 8u
|
|
#define kXenosFormat_8_B 9u
|
|
#define kXenosFormat_8_8 10u
|
|
#define kXenosFormat_Cr_Y1_Cb_Y0_REP 11u
|
|
#define kXenosFormat_Y1_Cr_Y0_Cb_REP 12u
|
|
#define kXenosFormat_16_16_EDRAM 13u
|
|
#define kXenosFormat_8_8_8_8_A 14u
|
|
#define kXenosFormat_4_4_4_4 15u
|
|
#define kXenosFormat_10_11_11 16u
|
|
#define kXenosFormat_11_11_10 17u
|
|
#define kXenosFormat_DXT1 18u
|
|
#define kXenosFormat_DXT2_3 19u
|
|
#define kXenosFormat_DXT4_5 20u
|
|
#define kXenosFormat_16_16_16_16_EDRAM 21u
|
|
#define kXenosFormat_24_8 22u
|
|
#define kXenosFormat_24_8_FLOAT 23u
|
|
#define kXenosFormat_16 24u
|
|
#define kXenosFormat_16_16 25u
|
|
#define kXenosFormat_16_16_16_16 26u
|
|
#define kXenosFormat_16_EXPAND 27u
|
|
#define kXenosFormat_16_16_EXPAND 28u
|
|
#define kXenosFormat_16_16_16_16_EXPAND 29u
|
|
#define kXenosFormat_16_FLOAT 30u
|
|
#define kXenosFormat_16_16_FLOAT 31u
|
|
#define kXenosFormat_16_16_16_16_FLOAT 32u
|
|
#define kXenosFormat_32 33u
|
|
#define kXenosFormat_32_32 34u
|
|
#define kXenosFormat_32_32_32_32 35u
|
|
#define kXenosFormat_32_FLOAT 36u
|
|
#define kXenosFormat_32_32_FLOAT 37u
|
|
#define kXenosFormat_32_32_32_32_FLOAT 38u
|
|
#define kXenosFormat_32_AS_8 39u
|
|
#define kXenosFormat_32_AS_8_8 40u
|
|
#define kXenosFormat_16_MPEG 41u
|
|
#define kXenosFormat_16_16_MPEG 42u
|
|
#define kXenosFormat_8_INTERLACED 43u
|
|
#define kXenosFormat_32_AS_8_INTERLACED 44u
|
|
#define kXenosFormat_32_AS_8_8_INTERLACED 45u
|
|
#define kXenosFormat_16_INTERLACED 46u
|
|
#define kXenosFormat_16_MPEG_INTERLACED 47u
|
|
#define kXenosFormat_16_16_MPEG_INTERLACED 48u
|
|
#define kXenosFormat_DXN 49u
|
|
#define kXenosFormat_8_8_8_8_AS_16_16_16_16 50u
|
|
#define kXenosFormat_DXT1_AS_16_16_16_16 51u
|
|
#define kXenosFormat_DXT2_3_AS_16_16_16_16 52u
|
|
#define kXenosFormat_DXT4_5_AS_16_16_16_16 53u
|
|
#define kXenosFormat_2_10_10_10_AS_16_16_16_16 54u
|
|
#define kXenosFormat_10_11_11_AS_16_16_16_16 55u
|
|
#define kXenosFormat_11_11_10_AS_16_16_16_16 56u
|
|
#define kXenosFormat_32_32_32_FLOAT 57u
|
|
#define kXenosFormat_DXT3A 58u
|
|
#define kXenosFormat_DXT5A 59u
|
|
#define kXenosFormat_CTX1 60u
|
|
#define kXenosFormat_DXT3A_AS_1_1_1_1 61u
|
|
#define kXenosFormat_8_8_8_8_GAMMA_EDRAM 62u
|
|
#define kXenosFormat_2_10_10_10_FLOAT_EDRAM 63u
|
|
|
|
#define kXenosColorRenderTargetFormat_8_8_8_8 0u
|
|
#define kXenosColorRenderTargetFormat_8_8_8_8_GAMMA 1u
|
|
#define kXenosColorRenderTargetFormat_2_10_10_10 2u
|
|
#define kXenosColorRenderTargetFormat_2_10_10_10_FLOAT 3u
|
|
#define kXenosColorRenderTargetFormat_16_16 4u
|
|
#define kXenosColorRenderTargetFormat_16_16_16_16 5u
|
|
#define kXenosColorRenderTargetFormat_16_16_FLOAT 6u
|
|
#define kXenosColorRenderTargetFormat_16_16_16_16_FLOAT 7u
|
|
#define kXenosColorRenderTargetFormat_2_10_10_10_AS_10_10_10_10 10u
|
|
#define kXenosColorRenderTargetFormat_2_10_10_10_FLOAT_AS_16_16_16_16 12u
|
|
#define kXenosColorRenderTargetFormat_32_FLOAT 14u
|
|
#define kXenosColorRenderTargetFormat_32_32_FLOAT 15u
|
|
|
|
#define kXenosDepthRenderTargetFormat_D24S8 0u
|
|
#define kXenosDepthRenderTargetFormat_D24FS8 1u
|
|
|
|
// ColorFormat packing, according to the Direct3D 11.3 functional specification.
|
|
|
|
uint XePackR5G5B5A1UNorm(xesl_float4 f) {
|
|
xesl_uint4 n =
|
|
xesl_uint4(xesl_saturate(f) * xesl_float2(31.0, 1.0).xxxy + 0.5);
|
|
return n.r | (n.g << 5) | (n.b << 10) | (n.a << 15);
|
|
}
|
|
|
|
uint XePackR5G6B5UNorm(xesl_float3 f) {
|
|
xesl_uint3 n =
|
|
xesl_uint3(xesl_saturate(f) * xesl_float3(31.0, 63.0, 31.0) + 0.5);
|
|
return n.r | (n.g << 5) | (n.b << 11);
|
|
}
|
|
|
|
uint XePackR5G5B6UNorm(xesl_float3 f) {
|
|
xesl_uint3 n =
|
|
xesl_uint3(xesl_saturate(f) * xesl_float3(31.0, 31.0, 63.0) + 0.5);
|
|
return n.r | (n.g << 5) | (n.b << 10);
|
|
}
|
|
|
|
uint XePackR8G8B8A8UNorm(xesl_float4 f) {
|
|
xesl_uint4 n = xesl_uint4(xesl_saturate(f) * 255.0 + 0.5);
|
|
return n.r | (n.g << 8) | (n.b << 16) | (n.a << 24);
|
|
}
|
|
|
|
uint XePackR10G10B10A2UNorm(xesl_float4 f) {
|
|
xesl_uint4 n =
|
|
xesl_uint4(xesl_saturate(f) * xesl_float2(1023.0, 3.0).xxxy + 0.5);
|
|
return n.r | (n.g << 10) | (n.b << 20) | (n.a << 30);
|
|
}
|
|
|
|
uint XePackR4G4B4A4UNorm(xesl_float4 f) {
|
|
xesl_uint4 n = xesl_uint4(xesl_saturate(f) * 15.0 + 0.5);
|
|
return n.r | (n.g << 4) | (n.b << 8) | (n.a << 12);
|
|
}
|
|
|
|
uint XePackR11G11B10UNorm(xesl_float3 f) {
|
|
xesl_uint3 n =
|
|
xesl_uint3(xesl_saturate(f) * xesl_float3(2047.0, 2047.0, 1023.0) + 0.5);
|
|
return n.r | (n.g << 11) | (n.b << 22);
|
|
}
|
|
|
|
uint XePackR10G11B11UNorm(xesl_float3 f) {
|
|
xesl_uint3 n =
|
|
xesl_uint3(xesl_saturate(f) * xesl_float3(1023.0, 2047.0, 2047.0) + 0.5);
|
|
return n.r | (n.g << 10) | (n.b << 21);
|
|
}
|
|
|
|
uint XePackR16G16UNorm(xesl_float2 f) {
|
|
xesl_uint2 n = xesl_uint2(xesl_saturate(f) * 65535.0 + 0.5);
|
|
return n.r | (n.g << 16);
|
|
}
|
|
|
|
xesl_uint2 XePackR16G16B16A16UNorm(xesl_float4 f) {
|
|
xesl_uint4 n = xesl_uint4(xesl_saturate(f) * 65535.0 + 0.5);
|
|
return n.rb | (n.ga << 16);
|
|
}
|
|
|
|
xesl_uint2 XePack16bpp4PixelsInUInt2(xesl_float4 pixel_0, xesl_float4 pixel_1,
|
|
xesl_float4 pixel_2, xesl_float4 pixel_3,
|
|
uint format) {
|
|
xesl_uint2 packed;
|
|
switch (format) {
|
|
case kXenosFormat_1_5_5_5:
|
|
packed.x = XePackR5G5B5A1UNorm(pixel_0) |
|
|
(XePackR5G5B5A1UNorm(pixel_1) << 16u);
|
|
packed.y = XePackR5G5B5A1UNorm(pixel_2) |
|
|
(XePackR5G5B5A1UNorm(pixel_3) << 16u);
|
|
break;
|
|
case kXenosFormat_5_6_5:
|
|
packed.x = XePackR5G6B5UNorm(pixel_0.rgb) |
|
|
(XePackR5G6B5UNorm(pixel_1.rgb) << 16u);
|
|
packed.y = XePackR5G6B5UNorm(pixel_2.rgb) |
|
|
(XePackR5G6B5UNorm(pixel_3.rgb) << 16u);
|
|
break;
|
|
case kXenosFormat_6_5_5:
|
|
packed.x = XePackR5G5B6UNorm(pixel_0.rgb) |
|
|
(XePackR5G5B6UNorm(pixel_1.rgb) << 16u);
|
|
packed.y = XePackR5G5B6UNorm(pixel_2.rgb) |
|
|
(XePackR5G5B6UNorm(pixel_3.rgb) << 16u);
|
|
break;
|
|
case kXenosFormat_8_8:
|
|
packed.x = XePackR8G8B8A8UNorm(xesl_float4(pixel_0.rg, pixel_1.rg));
|
|
packed.y = XePackR8G8B8A8UNorm(xesl_float4(pixel_2.rg, pixel_3.rg));
|
|
break;
|
|
case kXenosFormat_4_4_4_4:
|
|
packed.x = XePackR4G4B4A4UNorm(pixel_0) |
|
|
(XePackR4G4B4A4UNorm(pixel_1) << 16u);
|
|
packed.y = XePackR4G4B4A4UNorm(pixel_2) |
|
|
(XePackR4G4B4A4UNorm(pixel_3) << 16u);
|
|
break;
|
|
case kXenosFormat_16:
|
|
packed = XePackR16G16B16A16UNorm(xesl_float4(pixel_0.r, pixel_1.r,
|
|
pixel_2.r, pixel_3.r));
|
|
break;
|
|
default:
|
|
// Treat as something (16_FLOAT).
|
|
packed.x = xesl_packHalf2x16(xesl_float2(pixel_0.r, pixel_1.r));
|
|
packed.y = xesl_packHalf2x16(xesl_float2(pixel_2.r, pixel_3.r));
|
|
break;
|
|
}
|
|
return packed;
|
|
}
|
|
|
|
xesl_uint4 XePack32bpp4Pixels(xesl_float4 pixel_0, xesl_float4 pixel_1,
|
|
xesl_float4 pixel_2, xesl_float4 pixel_3,
|
|
uint format) {
|
|
xesl_uint4 packed;
|
|
switch (format) {
|
|
case kXenosFormat_8_8_8_8:
|
|
// TODO(Triang3l): Investigate 8_8_8_8_A.
|
|
case kXenosFormat_8_8_8_8_A:
|
|
case kXenosFormat_8_8_8_8_AS_16_16_16_16:
|
|
packed.x = XePackR8G8B8A8UNorm(pixel_0);
|
|
packed.y = XePackR8G8B8A8UNorm(pixel_1);
|
|
packed.z = XePackR8G8B8A8UNorm(pixel_2);
|
|
packed.w = XePackR8G8B8A8UNorm(pixel_3);
|
|
break;
|
|
case kXenosFormat_2_10_10_10:
|
|
case kXenosFormat_2_10_10_10_AS_16_16_16_16:
|
|
packed.x = XePackR10G10B10A2UNorm(pixel_0);
|
|
packed.y = XePackR10G10B10A2UNorm(pixel_1);
|
|
packed.z = XePackR10G10B10A2UNorm(pixel_2);
|
|
packed.w = XePackR10G10B10A2UNorm(pixel_3);
|
|
break;
|
|
case kXenosFormat_10_11_11:
|
|
case kXenosFormat_10_11_11_AS_16_16_16_16:
|
|
packed.x = XePackR11G11B10UNorm(pixel_0.rgb);
|
|
packed.y = XePackR11G11B10UNorm(pixel_1.rgb);
|
|
packed.z = XePackR11G11B10UNorm(pixel_2.rgb);
|
|
packed.w = XePackR11G11B10UNorm(pixel_3.rgb);
|
|
break;
|
|
case kXenosFormat_11_11_10:
|
|
case kXenosFormat_11_11_10_AS_16_16_16_16:
|
|
packed.x = XePackR10G11B11UNorm(pixel_0.rgb);
|
|
packed.y = XePackR10G11B11UNorm(pixel_1.rgb);
|
|
packed.z = XePackR10G11B11UNorm(pixel_2.rgb);
|
|
packed.w = XePackR10G11B11UNorm(pixel_3.rgb);
|
|
break;
|
|
case kXenosFormat_16_16:
|
|
packed.x = XePackR16G16UNorm(pixel_0.rg);
|
|
packed.y = XePackR16G16UNorm(pixel_1.rg);
|
|
packed.z = XePackR16G16UNorm(pixel_2.rg);
|
|
packed.w = XePackR16G16UNorm(pixel_3.rg);
|
|
break;
|
|
case kXenosFormat_16_16_FLOAT:
|
|
packed.x = xesl_packHalf2x16(xesl_float2(pixel_0.r, pixel_0.g));
|
|
packed.y = xesl_packHalf2x16(xesl_float2(pixel_1.r, pixel_1.g));
|
|
packed.z = xesl_packHalf2x16(xesl_float2(pixel_2.r, pixel_2.g));
|
|
packed.w = xesl_packHalf2x16(xesl_float2(pixel_3.r, pixel_3.g));
|
|
break;
|
|
default:
|
|
// Treat as 32_FLOAT.
|
|
packed.x = xesl_floatBitsToUint(pixel_0.r);
|
|
packed.y = xesl_floatBitsToUint(pixel_1.r);
|
|
packed.z = xesl_floatBitsToUint(pixel_2.r);
|
|
packed.w = xesl_floatBitsToUint(pixel_3.r);
|
|
break;
|
|
}
|
|
return packed;
|
|
}
|
|
|
|
void XePack64bpp4Pixels(xesl_float4 pixel_0, xesl_float4 pixel_1,
|
|
xesl_float4 pixel_2, xesl_float4 pixel_3, uint format,
|
|
out xesl_uint4 packed_01, out xesl_uint4 packed_23) {
|
|
switch (format) {
|
|
case kXenosFormat_16_16_16_16:
|
|
packed_01.xy = XePackR16G16B16A16UNorm(pixel_0);
|
|
packed_01.zw = XePackR16G16B16A16UNorm(pixel_1);
|
|
packed_23.xy = XePackR16G16B16A16UNorm(pixel_2);
|
|
packed_23.zw = XePackR16G16B16A16UNorm(pixel_3);
|
|
break;
|
|
case kXenosFormat_16_16_16_16_FLOAT:
|
|
packed_01.x = xesl_packHalf2x16(xesl_float2(pixel_0.r, pixel_0.g));
|
|
packed_01.y = xesl_packHalf2x16(xesl_float2(pixel_0.b, pixel_0.a));
|
|
packed_01.z = xesl_packHalf2x16(xesl_float2(pixel_1.r, pixel_1.g));
|
|
packed_01.w = xesl_packHalf2x16(xesl_float2(pixel_1.b, pixel_1.a));
|
|
packed_23.x = xesl_packHalf2x16(xesl_float2(pixel_2.r, pixel_2.g));
|
|
packed_23.y = xesl_packHalf2x16(xesl_float2(pixel_2.b, pixel_2.a));
|
|
packed_23.z = xesl_packHalf2x16(xesl_float2(pixel_3.r, pixel_3.g));
|
|
packed_23.w = xesl_packHalf2x16(xesl_float2(pixel_3.b, pixel_3.a));
|
|
break;
|
|
default:
|
|
// Treat as 32_32_FLOAT.
|
|
packed_01 = xesl_floatBitsToUint(xesl_float4(pixel_0.rg, pixel_1.rg));
|
|
packed_23 = xesl_floatBitsToUint(xesl_float4(pixel_2.rg, pixel_3.rg));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// EDRAM color format unpacking.
|
|
|
|
xesl_float4 XeUnpackR8UNormX4(xesl_uint4 p) {
|
|
return xesl_float4(p & 255u) * (1.0 / 255.0);
|
|
}
|
|
|
|
xesl_float4 XeUnpackR8G8B8A8UNorm(uint p) {
|
|
return xesl_float4((p.xxxx >> xesl_uint4(0u, 8u, 16u, 24u)) & 255u) *
|
|
(1.0 / 255.0);
|
|
}
|
|
|
|
xesl_float4 XeUnpackR10UNormX4(xesl_uint4 p) {
|
|
return xesl_float4(p & 1023u) * (1.0 / 1023.0);
|
|
}
|
|
|
|
xesl_float4 XeUnpackR10G10B10A2UNorm(uint p) {
|
|
return xesl_float4((p.xxxx >> xesl_uint4(0u, 10u, 20u, 30u)) &
|
|
xesl_uint2(1023u, 3u).xxxy) *
|
|
xesl_float2(1.0 / 1023.0, 1.0 / 3.0).xxxy;
|
|
}
|
|
|
|
xesl_float4 XeUnpackR10FloatX4(xesl_uint4 p) {
|
|
// https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
|
|
xesl_uint4 f10u32 = p & 0x3FFu;
|
|
xesl_uint4 mantissa = f10u32 & 0x7Fu;
|
|
xesl_uint4 exponent = f10u32 >> 7u;
|
|
// Normalize the values for the denormalized components.
|
|
// Exponent = 1;
|
|
// do { Exponent--; Mantissa <<= 1; } while ((Mantissa & 0x80) == 0);
|
|
xesl_bool4 is_denormalized = xesl_equal(exponent, (0u).xxxx);
|
|
xesl_uint4 mantissa_lzcnt = (7u).xxxx - xesl_findMSB(mantissa);
|
|
exponent =
|
|
xesl_select(is_denormalized, ((1u).xxxx - mantissa_lzcnt), exponent);
|
|
mantissa = xesl_select(is_denormalized,
|
|
((mantissa << mantissa_lzcnt) & 0x7Fu), mantissa);
|
|
// Combine into 32-bit float bits and clear zeros.
|
|
return xesl_uintBitsToFloat(xesl_select(
|
|
xesl_equal(f10u32, (0u).xxxx), (0u).xxxx,
|
|
((exponent + 124u) << 23u) | (mantissa << 16u)));
|
|
}
|
|
|
|
xesl_float4 XeUnpackR10G10B10A2Float(uint p) {
|
|
// https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
|
|
xesl_uint3 rgb_f10u32 = (p.xxx >> xesl_uint3(0u, 10u, 20u)) & 0x3FFu;
|
|
xesl_uint3 mantissa = rgb_f10u32 & 0x7Fu;
|
|
xesl_uint3 exponent = rgb_f10u32 >> 7u;
|
|
// Normalize the values for the denormalized components.
|
|
// Exponent = 1;
|
|
// do { Exponent--; Mantissa <<= 1; } while ((Mantissa & 0x80) == 0);
|
|
xesl_bool3 is_denormalized = xesl_equal(exponent, (0u).xxx);
|
|
xesl_uint3 mantissa_lzcnt = (7u).xxx - xesl_findMSB(mantissa);
|
|
exponent =
|
|
xesl_select(is_denormalized, ((1u).xxx - mantissa_lzcnt), exponent);
|
|
mantissa = xesl_select(is_denormalized,
|
|
((mantissa << mantissa_lzcnt) & 0x7Fu), mantissa);
|
|
// Combine into 32-bit float bits and clear zeros.
|
|
xesl_uint3 rgb_f32u32 = xesl_select(
|
|
xesl_equal(rgb_f10u32, (0u).xxx), (0u).xxx,
|
|
((exponent + 124u) << 23u) | (mantissa << 16u));
|
|
return xesl_float4(xesl_uintBitsToFloat(rgb_f32u32),
|
|
float(p >> 30u) * (1.0 / 3.0));
|
|
}
|
|
|
|
// http://web.archive.org/web/20180826210254/https://www.students.science.uu.nl/~3220516/advancedgraphics/papers/inferred_lighting.pdf
|
|
// "The format of the DSF buffer (two 16 bit channels) in EDRAM is fixed point
|
|
// with a range of -32 to 32. The corresponding texture format is fixed point
|
|
// with a range of 0 to 1. This requires the shader to scale the shader output
|
|
// of 0 to 1 to -32 to 32. To maintain 16 bit precision, the texture used for
|
|
// the resolve needs to be created with a custom format that has a range of -1
|
|
// to 1. When sampling from this texture in a shader, the results must be
|
|
// scaled to a 0 to 1 range."
|
|
|
|
// Upper 16 bits are ignored by XeUnpackR16EdramX4.
|
|
|
|
xesl_float4 XeUnpackR16EdramX4(xesl_uint4 p) {
|
|
return max((-1.0).xxxx,
|
|
xesl_float4(xesl_int4(p) << 16 >> 16) * (32.0 / 32767.0));
|
|
}
|
|
|
|
xesl_float2 XeUnpackR16G16Edram(uint p) {
|
|
return max(
|
|
(-1.0).xx,
|
|
xesl_float2(int(p).xx << xesl_int2(16, 0) >> 16) * (32.0 / 32767.0));
|
|
}
|
|
|
|
xesl_float4 XeUnpackR16G16B16A16Edram(xesl_uint2 p) {
|
|
return max((-1.0).xxxx,
|
|
xesl_float4(xesl_int2(p).xxyy << xesl_int2(16, 0).xyxy >> 16) *
|
|
(32.0 / 32767.0));
|
|
}
|
|
|
|
// Xenos 16-bit packed textures are RGBA, but in Direct3D 12 they are BGRA.
|
|
|
|
xesl_uint4 XeR5G5B5A1ToB5G5R5A1(xesl_uint4 packed_texels) {
|
|
return (packed_texels & 0x83E083E0u) |
|
|
((packed_texels & 0x001F001Fu) << 10u) |
|
|
((packed_texels & 0x7C007C00u) >> 10u);
|
|
}
|
|
|
|
xesl_uint4 XeR5G6B5ToB5G6R5(xesl_uint4 packed_texels) {
|
|
return (packed_texels & 0x07E007E0u) |
|
|
((packed_texels & 0x001F001Fu) << 11u) |
|
|
((packed_texels & 0xF800F800u) >> 11u);
|
|
}
|
|
|
|
xesl_uint4 XeR4G4B4A4ToB4G4R4A4(xesl_uint4 packed_texels) {
|
|
return (packed_texels & 0xF0F0F0F0u) | ((packed_texels & 0x000F000Fu) << 8u) |
|
|
((packed_texels & 0x0F000F00u) >> 8u);
|
|
}
|
|
|
|
xesl_uint4 XeR4G4B4A4ToA4R4G4B4(xesl_uint4 packed_texels) {
|
|
return ((packed_texels & 0x0FFF0FFFu) << 4u) |
|
|
((packed_texels & 0xF000F000u) >> 12u);
|
|
}
|
|
|
|
// RRRRR GGGGG BBBBBB to GGGGG BBBBBB RRRRR (use RBGA swizzle when reading).
|
|
xesl_uint4 XeR5G5B6ToB5G6R5WithRBGASwizzle(xesl_uint4 packed_texels) {
|
|
return ((packed_texels & 0x001F001Fu) << 11u) |
|
|
((packed_texels & 0xFFE0FFE0u) >> 5u);
|
|
}
|
|
|
|
xesl_uint4 XeGBGR8ToGRGB8(xesl_uint4 packed_texels) {
|
|
return (packed_texels & 0x00FF00FFu) |
|
|
((packed_texels & 0x0000FF00u) << 16u) |
|
|
((packed_texels & 0xFF000000u) >> 16u);
|
|
}
|
|
|
|
// Also usable for BGRA8 <> RGBA8, but that's not needed for texture loading.
|
|
xesl_uint4 XeBGRG8ToRGBG8(xesl_uint4 packed_texels) {
|
|
return (packed_texels & 0xFF00FF00u) |
|
|
((packed_texels & 0x000000FFu) << 16u) |
|
|
((packed_texels & 0x00FF0000u) >> 16u);
|
|
}
|
|
|
|
xesl_uint4 XeR10G11B11UNormToRGBA16(xesl_uint2 packed_texels) {
|
|
// Red and blue.
|
|
xesl_uint4 result =
|
|
(((packed_texels.xxyy >> xesl_uint2(0u, 21u).xyxy) &
|
|
xesl_uint2(1023u, 2047u).xyxy) <<
|
|
xesl_uint2(6u, 5u).xyxy) |
|
|
((packed_texels.xxyy >> xesl_uint2(4u, 27u).xyxy) &
|
|
xesl_uint2(63u, 31u).xyxy);
|
|
// Green. The 5 bits to be duplicated to the bottom are already at 16.
|
|
result.xz |= ((packed_texels & (2047u << 10u)) << (21u - 10u)) |
|
|
(packed_texels & (31u << 16u));
|
|
// Alpha.
|
|
result.yw |= 0xFFFF0000u;
|
|
return result;
|
|
}
|
|
void XeR10G11B11UNormToRGBA16(xesl_uint4 packed_texels, out xesl_uint4 out_01,
|
|
out xesl_uint4 out_23) {
|
|
out_01 = XeR10G11B11UNormToRGBA16(packed_texels.xy);
|
|
out_23 = XeR10G11B11UNormToRGBA16(packed_texels.zw);
|
|
}
|
|
|
|
xesl_uint4 XeR11G11B10UNormToRGBA16(xesl_uint2 packed_texels) {
|
|
// Red and blue.
|
|
xesl_uint4 result =
|
|
(((packed_texels.xxyy >> xesl_uint2(0u, 22u).xyxy) &
|
|
xesl_uint2(2047u, 1023u).xyxy) <<
|
|
xesl_uint2(5u, 6u).xyxy) |
|
|
((packed_texels.xxyy >> xesl_uint2(6u, 26u).xyxy) &
|
|
xesl_uint2(31u, 63u).xyxy);
|
|
// Green.
|
|
result.xz |= ((packed_texels & (2047u << 11u)) << (21u - 11u)) |
|
|
((packed_texels & (31u << 17u)) >> (17u - 16u));
|
|
// Alpha.
|
|
result.yw |= 0xFFFF0000u;
|
|
return result;
|
|
}
|
|
void XeR11G11B10UNormToRGBA16(xesl_uint4 packed_texels, out xesl_uint4 out_01,
|
|
out xesl_uint4 out_23) {
|
|
out_01 = XeR11G11B10UNormToRGBA16(packed_texels.xy);
|
|
out_23 = XeR11G11B10UNormToRGBA16(packed_texels.zw);
|
|
}
|
|
|
|
// Assuming the original number has only 10 bits.
|
|
xesl_uint2 XeSNorm10To16(xesl_uint2 s10) {
|
|
xesl_uint2 signs = s10 >> 9u;
|
|
xesl_bool2 is_negative = xesl_notEqual(signs, (0u).xx);
|
|
// -512 and -511 are both -1.0, but with -512 the conversion will overflow.
|
|
s10 = xesl_select(xesl_equal(s10, (0x200u).xx), (0x201u).xx, s10);
|
|
// Take the absolute value.
|
|
s10 = (s10 ^ xesl_select(is_negative, (0x3FFu).xx, (0u).xx)) + signs;
|
|
// Expand the 9-bit absolute value to 15 bits like unorm.
|
|
s10 = (s10 << 6u) | (s10 >> 3u);
|
|
// Apply the sign.
|
|
return (s10 ^ xesl_select(is_negative, (0xFFFFu).xx, (0u).xx)) + signs;
|
|
}
|
|
|
|
// Assuming the original number has only 11 bits.
|
|
xesl_uint2 XeSNorm11To16(xesl_uint2 s11) {
|
|
xesl_uint2 signs = s11 >> 10u;
|
|
xesl_bool2 is_negative = xesl_notEqual(signs, (0u).xx);
|
|
// -1024 and -1023 are both -1.0, but with -1024 the conversion will overflow.
|
|
s11 = xesl_select(xesl_equal(s11, (0x400u).xx), (0x401u).xx, s11);
|
|
// Take the absolute value.
|
|
s11 = (s11 ^ xesl_select(is_negative, (0x7FFu).xx, (0u).xx)) + signs;
|
|
// Expand the 10-bit absolute value to 15 bits like unorm.
|
|
s11 = (s11 << 5u) | (s11 >> 5u);
|
|
// Apply the sign.
|
|
return (s11 ^ xesl_select(is_negative, (0xFFFFu).xx, (0u).xx)) + signs;
|
|
}
|
|
|
|
xesl_uint4 XeR10G11B11SNormToRGBA16(xesl_uint2 packed_texels) {
|
|
// uint4(RG0, RG1, BA0, BA1).xzyw == uint4(RG0, BA0, RG1, BA1).
|
|
return xesl_uint4(XeSNorm10To16(packed_texels & 1023u) |
|
|
(XeSNorm11To16((packed_texels >> 10u) & 2047u) << 16u),
|
|
XeSNorm11To16(packed_texels >> 21u) | 0x7FFF0000u).xzyw;
|
|
}
|
|
void XeR10G11B11SNormToRGBA16(xesl_uint4 packed_texels, out xesl_uint4 out_01,
|
|
out xesl_uint4 out_23) {
|
|
out_01 = XeR10G11B11SNormToRGBA16(packed_texels.xy);
|
|
out_23 = XeR10G11B11SNormToRGBA16(packed_texels.zw);
|
|
}
|
|
|
|
xesl_uint4 XeR11G11B10SNormToRGBA16(xesl_uint2 packed_texels) {
|
|
// uint4(RG0, RG1, BA0, BA1).xzyw == uint4(RG0, BA0, RG1, BA1).
|
|
return xesl_uint4(XeSNorm11To16(packed_texels & 2047u) |
|
|
(XeSNorm11To16((packed_texels >> 11u) & 2047u) << 16u),
|
|
XeSNorm10To16(packed_texels >> 22u) | 0x7FFF0000u).xzyw;
|
|
}
|
|
void XeR11G11B10SNormToRGBA16(xesl_uint4 packed_texels, out xesl_uint4 out_01,
|
|
out xesl_uint4 out_23) {
|
|
out_01 = XeR11G11B10SNormToRGBA16(packed_texels.xy);
|
|
out_23 = XeR11G11B10SNormToRGBA16(packed_texels.zw);
|
|
}
|
|
|
|
xesl_uint4 XeRG16UNormToRG16Float(xesl_uint4 packed_texels) {
|
|
xesl_float4 r = xesl_float4(packed_texels & 0xFFFFu) * (1.0 / 65535.0);
|
|
xesl_float4 g = xesl_float4(packed_texels >> 16u) * (1.0 / 65535.0);
|
|
return xesl_uint4(xesl_packHalf2x16(xesl_float2(r.x, g.x)),
|
|
xesl_packHalf2x16(xesl_float2(r.y, g.y)),
|
|
xesl_packHalf2x16(xesl_float2(r.z, g.z)),
|
|
xesl_packHalf2x16(xesl_float2(r.w, g.w)));
|
|
}
|
|
|
|
xesl_uint4 XeRG16SNormToRG16Float(xesl_uint4 packed_texels) {
|
|
xesl_float4 r =
|
|
max((-1.0).xxxx,
|
|
xesl_float4(xesl_int4(packed_texels) << 16 >> 16) * (1.0 / 32767.0));
|
|
xesl_float4 g =
|
|
max((-1.0).xxxx,
|
|
xesl_float4(xesl_int4(packed_texels) >> 16) * (1.0 / 32767.0));
|
|
return xesl_uint4(xesl_packHalf2x16(xesl_float2(r.x, g.x)),
|
|
xesl_packHalf2x16(xesl_float2(r.y, g.y)),
|
|
xesl_packHalf2x16(xesl_float2(r.z, g.z)),
|
|
xesl_packHalf2x16(xesl_float2(r.w, g.w)));
|
|
}
|
|
|
|
// Based on CFloat24 from d3dref9.dll and the 6e4 code from:
|
|
// https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
|
|
// 6e4 has a different exponent bias allowing [0,512) values, 20e4 allows [0,2).
|
|
// We also can't clamp the stored value to 1 as load->store->load must be exact.
|
|
|
|
uint XeFloat32To20e4(uint f32u32) {
|
|
// Keep only positive (high bit set means negative for both float and int) and
|
|
// saturate to the maximum representable value near 2 (also dropping NaNs).
|
|
f32u32 = min((f32u32 <= 0x7FFFFFFFu) ? f32u32 : 0u, 0x3FFFFFF8u);
|
|
uint denormalized =
|
|
((f32u32 & 0x7FFFFFu) | 0x800000u) >> min(113u - (f32u32 >> 23u), 24u);
|
|
uint f24u32 = (f32u32 < 0x38800000u) ? denormalized : (f32u32 + 0xC8000000u);
|
|
return ((f24u32 + 3u + ((f24u32 >> 3u) & 1u)) >> 3u) & 0xFFFFFFu;
|
|
}
|
|
|
|
uint XeFloat20e4To32(uint f24u32, bool remap_to_0_to_0_5) {
|
|
uint mantissa = f24u32 & 0xFFFFFu;
|
|
uint exponent = f24u32 >> 20u;
|
|
// Normalize the values for the denormalized components.
|
|
// Exponent = 1;
|
|
// do { Exponent--; Mantissa <<= 1; } while ((Mantissa & 0x100000) == 0);
|
|
bool is_denormalized = exponent == 0u;
|
|
uint mantissa_lzcnt = 20u - xesl_findMSB(mantissa);
|
|
exponent = is_denormalized ? (1u - mantissa_lzcnt) : exponent;
|
|
mantissa =
|
|
is_denormalized ? ((mantissa << mantissa_lzcnt) & 0xFFFFFu) : mantissa;
|
|
// Combine into 32-bit float bits and clear zeros and, if needed, bias the
|
|
// exponent.
|
|
return (f24u32 != 0u)
|
|
? ((exponent + (112u - uint(remap_to_0_to_0_5))) << 23u) |
|
|
(mantissa << 3u)
|
|
: 0u;
|
|
}
|
|
|
|
xesl_uint4 XeFloat20e4To32(xesl_uint4 f24u32) {
|
|
xesl_uint4 mantissa = f24u32 & 0xFFFFFu;
|
|
xesl_uint4 exponent = f24u32 >> 20u;
|
|
// Normalize the values for the denormalized components.
|
|
// Exponent = 1;
|
|
// do { Exponent--; Mantissa <<= 1; } while ((Mantissa & 0x100000) == 0);
|
|
xesl_bool4 is_denormalized = xesl_equal(exponent, (0u).xxxx);
|
|
xesl_uint4 mantissa_lzcnt = (20u).xxxx - xesl_findMSB(mantissa);
|
|
exponent = xesl_select(is_denormalized, (1u).xxxx - mantissa_lzcnt, exponent);
|
|
mantissa = xesl_select(
|
|
is_denormalized, (mantissa << mantissa_lzcnt) & 0xFFFFFu, mantissa);
|
|
// Combine into 32-bit float bits and clear zeros.
|
|
return xesl_select(xesl_equal(f24u32, (0u).xxxx), (0u).xxxx,
|
|
((exponent + 112u) << 23u) | (mantissa << 3u));
|
|
}
|
|
|
|
xesl_uint2 XeFloat20e4To32(xesl_uint2 f24u32) {
|
|
return XeFloat20e4To32(f24u32.xyxx).xy;
|
|
}
|
|
|
|
xesl_float4 XeUNorm24To32(xesl_uint4 n24) {
|
|
// Not 1.0 / 16777215.0 as that gives an incorrect result (like for a very
|
|
// common 0xC00000 which clears 2_10_10_10 to 0001). Division by 2^24 is just
|
|
// an exponent shift though, thus exact.
|
|
// Division by 16777215.0 behaves this way.
|
|
return xesl_float4(n24 + (n24 >> 23u)) * (1.0 / 16777216.0);
|
|
}
|
|
|
|
xesl_float2 XeUNorm24To32(xesl_uint2 n24) {
|
|
return XeUNorm24To32(n24.xyxx).xy;
|
|
}
|
|
|
|
// Converts endpoint BGR (first - X of the return value - in the low 16 bits,
|
|
// second - Y of the return value - in the high) of a DXT blocks to 8-bit, with
|
|
// 2 unused bits between each component to allow for overflow when multiplying
|
|
// by values up to 3 (so multiplication can be done for all components at once).
|
|
// Relative ordering between endpoints is preserved, so result.x > result.y
|
|
// (color0 > color1) and result.x <= result.y (color0 <= color1) can be used for
|
|
// choosing the DXT1 mode.
|
|
xesl_uint2 XeDXTColorEndpointsToBGR8In10(uint bgr_end_565) {
|
|
// Converting 5:6:5 to 8:8:8 similar to how Compressonator does that.
|
|
// https://github.com/GPUOpen-Tools/compressonator/blob/master/CMP_CompressonatorLib/DXTC/Codec_DXTC_RGBA.cpp#L340
|
|
xesl_uint2 bgr_end_8in10 =
|
|
// Blue in 0:4 and 16:20 - to 3:7.
|
|
(xesl_uint2(bgr_end_565 << 3u, bgr_end_565 >> (16u - 3u)) & (31u << 3u)) |
|
|
// Green in 5:10 and 21:26 - to 12:17.
|
|
(xesl_uint2(bgr_end_565 << (12u - 5u), bgr_end_565 >> (21u - 12u)) &
|
|
(63u << 12u)) |
|
|
// Red in 11:15 and 27:31 - to 23:27.
|
|
(xesl_uint2(bgr_end_565 << (23u - 11u), bgr_end_565 >> (27u - 23u)) &
|
|
(31u << 23u));
|
|
// Apply the lower bit replication to give full dynamic range.
|
|
// Blue and red.
|
|
bgr_end_8in10 |= (bgr_end_8in10 >> 5u) & (7u | (7u << 20u));
|
|
// Green.
|
|
bgr_end_8in10 |= (bgr_end_8in10 >> 6u) & (3u << 10u);
|
|
return bgr_end_8in10;
|
|
}
|
|
|
|
// Sorts the color indices of a DXT3/DXT5 or a DXT1 opaque block so they can be
|
|
// used as the weights for the second endpoint, from 0 to 3. To get the weights
|
|
// for the first endpoint, apply bitwise NOT to the result.
|
|
xesl_uint4 XeDXTHighColorWeights(xesl_uint4 codes) {
|
|
// Initially 00 = 3:0, 01 = 0:3, 10 = 2:1, 11 = 1:2.
|
|
// Swap bits. 00 = 3:0, 01 = 2:1, 10 = 0:3, 11 = 1:2.
|
|
codes = ((codes & 0x55555555u) << 1u) | ((codes & 0xAAAAAAAAu) >> 1u);
|
|
// Swap 10 and 11. 00 = 3:0, 01 = 2:1, 10 = 1:2, 11 = 0:3.
|
|
return codes ^ ((codes & 0xAAAAAAAAu) >> 1u);
|
|
}
|
|
xesl_uint2 XeDXTHighColorWeights(xesl_uint2 codes) {
|
|
return XeDXTHighColorWeights(codes.xyxx).xy;
|
|
}
|
|
uint XeDXTHighColorWeights(uint codes) {
|
|
return XeDXTHighColorWeights(codes.xx).x;
|
|
}
|
|
|
|
// Get the RGB colors of one row of a DXT opaque block. Endpoint colors can be
|
|
// obtained using XeDXTColorEndpointsToBGR8In10 (8 bits with 2 bits of free
|
|
// space between each), weights can be obtained using XeDXTHighColorWeights.
|
|
// Alpha is set to 0 in the result. Weights must be shifted right by 8 * row
|
|
// index before calling.
|
|
xesl_uint4 XeDXTOpaqueRowToRGB8(xesl_uint2 bgr_end_8in10, uint weights_high) {
|
|
const xesl_uint4 weights_shifts = xesl_uint4(0u, 2u, 4u, 6u);
|
|
xesl_uint4 bgr_row_8in10_3x =
|
|
(((~weights_high).xxxx >> weights_shifts) & 3u) * bgr_end_8in10.x +
|
|
((weights_high.xxxx >> weights_shifts) & 3u) * bgr_end_8in10.y;
|
|
return (((bgr_row_8in10_3x & 1023u) / 3u) << 16u) |
|
|
((((bgr_row_8in10_3x >> 10u) & 1023u) / 3u) << 8u) |
|
|
((bgr_row_8in10_3x >> 20u) / 3u);
|
|
}
|
|
|
|
// Sort the color indices of four transparent DXT1 blocks so bits of them can be
|
|
// used as endpoint weights (lower bit for the low endpoint, upper bit for the
|
|
// high endpoint, and both bits for 1/2 of each, AND of those bits can be used
|
|
// as the right shift amount for mixing the two colors in the punchthrough
|
|
// mode). Zero for the punchthrough alpha texels.
|
|
xesl_uint4 XeDXT1TransWeights(xesl_uint4 codes) {
|
|
// Initially 00 = 1:0, 01 = 0:1, 10 = 1:1, 11 = 0:0.
|
|
// 00 = 0:0, 01 = 1:1, 10 = 0:1, 11 = 1:0.
|
|
codes = ~codes;
|
|
// 00 = 0:0, 01 = 1:0, 10 = 0:1, 11 = 1:1.
|
|
return codes ^ ((codes & 0x55555555u) << 1u);
|
|
}
|
|
|
|
// Gets the RGBA colors of one row of a DXT1 punchthrough block. Endpoint colors
|
|
// can be obtained using XeDXTColorEndpointsToBGR8In10 (8 bits with 2 bits of
|
|
// free space between each), weights can be obtained using XeDXT1TransWeights
|
|
// and must be shifted right by 8 * row index before calling.
|
|
xesl_uint4 XeDXT1TransRowToRGBA8(xesl_uint2 bgr_end_8in10, uint weights) {
|
|
const xesl_uint4 weights_shifts_low = xesl_uint4(0u, 2u, 4u, 6u);
|
|
const xesl_uint4 weights_shifts_high = xesl_uint4(1u, 3u, 5u, 7u);
|
|
xesl_uint4 bgr_row_8in10_scaled =
|
|
((weights.xxxx >> weights_shifts_low) & 1u) * bgr_end_8in10.x +
|
|
((weights.xxxx >> weights_shifts_high) & 1u) * bgr_end_8in10.y;
|
|
// Whether the texel is (RGB0+RGB1)/2 - divide the weighted sum by 2 (shift
|
|
// right by 1) if it is.
|
|
uint weights_sums_log2 = weights & ((weights & 0xAAAAAAAAu) >> 1u);
|
|
xesl_uint4 bgr_shift = (weights_sums_log2.xxxx >> weights_shifts_low) & 1u;
|
|
// Whether the texel is opaque.
|
|
uint weights_alpha =
|
|
(weights & 0x55555555u) | ((weights & 0xAAAAAAAAu) >> 1u);
|
|
return (((bgr_row_8in10_scaled & 1023u) >> bgr_shift) << 16u) +
|
|
((((bgr_row_8in10_scaled >> 10u) & 1023u) >> bgr_shift) << 8u) +
|
|
((bgr_row_8in10_scaled >> 20u) >> bgr_shift) +
|
|
(((weights_alpha.xxxx >> weights_shifts_low) & 1u) * 0xFF000000u);
|
|
}
|
|
|
|
// Converts one row of four DXT3 alpha blocks to 16 packed R8 texels, useful for
|
|
// converting DXT3A. Only 16 bits of alpha half-blocks are used. Alpha is from
|
|
// word 0 for rows 0 and 1, from word 1 for rows 2 and 3, must be shifted right
|
|
// by 16 * (row index & 1) before calling.
|
|
xesl_uint4 XeDXT3FourBlocksRowToA8(xesl_uint4 alphas) {
|
|
// (alphas & 0xFu) | ((alphas & 0xFu) << 4u) |
|
|
// ((alphas & 0xF0u) << (8u - 4u)) | ((alphas & 0xF0u) << (12u - 4u)) |
|
|
// ((alphas & 0xF00u) << (16u - 8u)) | ((alphas & 0xF00u) << (20u - 8u)) |
|
|
// ((alphas & 0xF000u) << (24u - 12u)) | ((alphas & 0xF000u) << (28u - 12u))
|
|
return (alphas & 0xFu) | ((alphas & 0xFFu) << 4u) |
|
|
((alphas & 0xFF0u) << 8u) | ((alphas & 0xFF00u) << 12u) |
|
|
((alphas & 0xF000u) << 16u);
|
|
}
|
|
|
|
xesl_uint4 XeDXT3AAs1111TwoBlocksRowToBGRA4(xesl_uint2 halfblocks) {
|
|
// Only 16 bits of half-blocks are used. X contains pixels 0123, Y - 4567 (in
|
|
// the image, halfblocks.y is halfblocks.x + 8).
|
|
// In the row, X contains pixels 01, Y - 23, Z - 45, W - 67.
|
|
// Assuming alpha in LSB and red in MSB, because it's consistent with how
|
|
// DXT1/DXT3/DXT5 color components and CTX1 X/Y are ordered in:
|
|
// http://fileadmin.cs.lth.se/cs/Personal/Michael_Doggett/talks/unc-xenos-doggett.pdf
|
|
// (LSB on the right, MSB on the left.)
|
|
// TODO(Triang3l): Investigate this better, 4D53085B is the only known game
|
|
// that uses it (for lighting in certain places - one of easy to notice usages
|
|
// is the T-shaped (or somewhat H-shaped) metal beams in the beginning of the
|
|
// first mission), however the contents don't say anything about the channel
|
|
// order.
|
|
xesl_uint4 row =
|
|
(((halfblocks.xxyy >> xesl_uint2(3u, 11u).xyxy) & 1u) << 8u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(7u, 15u).xyxy) & 1u) << 24u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(2u, 10u).xyxy) & 1u) << 4u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(6u, 14u).xyxy) & 1u) << 20u) |
|
|
((halfblocks.xxyy >> xesl_uint2(1u, 9u).xyxy) & 1u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(5u, 13u).xyxy) & 1u) << 16u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(0u, 8u).xyxy) & 1u) << 12u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(4u, 12u).xyxy) & 1u) << 28u);
|
|
row |= row << 1u;
|
|
row |= row << 2u;
|
|
return row;
|
|
}
|
|
|
|
xesl_uint4 XeDXT3AAs1111TwoBlocksRowToARGB4(xesl_uint2 halfblocks) {
|
|
// Only 16 bits of half-blocks are used. X contains pixels 0123, Y - 4567 (in
|
|
// the image, halfblocks.y is halfblocks.x + 8).
|
|
// In the row, X contains pixels 01, Y - 23, Z - 45, W - 67.
|
|
// Assuming alpha in LSB and red in MSB, because it's consistent with how
|
|
// DXT1/DXT3/DXT5 color components and CTX1 X/Y are ordered in:
|
|
// http://fileadmin.cs.lth.se/cs/Personal/Michael_Doggett/talks/unc-xenos-doggett.pdf
|
|
// (LSB on the right, MSB on the left.)
|
|
// TODO(Triang3l): Investigate this better, 4D53085B is the only known game
|
|
// that uses it (for lighting in certain places - one of easy to notice usages
|
|
// is the T-shaped (or somewhat H-shaped) metal beams in the beginning of the
|
|
// first mission), however the contents don't say anything about the channel
|
|
// order.
|
|
xesl_uint4 row =
|
|
(((halfblocks.xxyy >> xesl_uint2(3u, 11u).xyxy) & 1u) << 4u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(7u, 15u).xyxy) & 1u) << 20u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(2u, 10u).xyxy) & 1u) << 8u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(6u, 14u).xyxy) & 1u) << 24u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(1u, 9u).xyxy) & 1u) << 12u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(5u, 13u).xyxy) & 1u) << 28u) |
|
|
((halfblocks.xxyy >> xesl_uint2(0u, 8u).xyxy) & 1u) |
|
|
(((halfblocks.xxyy >> xesl_uint2(4u, 12u).xyxy) & 1u) << 16u);
|
|
row |= row << 1u;
|
|
row |= row << 2u;
|
|
return row;
|
|
}
|
|
|
|
// & 0x249249 = bits 0 of 24 bits of DXT5 alpha codes.
|
|
// & 0x492492 = bits 1 of 24 bits of DXT5 alpha codes.
|
|
// & 0x924924 = bits 2 of 24 bits of DXT5 alpha codes.
|
|
|
|
// Sorts half (24 bits) of the codes of a DXT5 alpha block so they can be used
|
|
// as weights for the second endpoint, from 0 to 7, in alpha0 > alpha1 mode.
|
|
uint XeDXT5High8StepAlphaWeights(uint codes_24b) {
|
|
// Initially 000 - first endpoint, 001 - second endpoint, 010 and above -
|
|
// weights from 6:1 to 1:6. Need to make 001 111, and subtract 1 from 010 and
|
|
// above.
|
|
// Whether the bits are 000 (the first endpoint only).
|
|
uint is_first = ((codes_24b & 0x249249u) | ((codes_24b & 0x492492u) >> 1u) |
|
|
((codes_24b & 0x924924u) >> 2u)) ^ 0x249249u;
|
|
// Whether the bits are 001 (the second endpoint only).
|
|
uint is_second = (codes_24b & 0x249249u) & ~((codes_24b & 0x492492u) >> 1u) &
|
|
~((codes_24b & 0x924924u) >> 2u);
|
|
// Change 000 to 001 so subtracting 1 will result in 0 (and there will never
|
|
// be overflow), subtract 1, and if the code was originally 001 (the second
|
|
// endpoint only), make it 111.
|
|
return ((codes_24b | is_first) - 0x249249u) |
|
|
is_second | (is_second << 1u) | (is_second << 2u);
|
|
}
|
|
|
|
// Sorts half (24 bits) of the codes of a DXT5 alpha block so they can be used
|
|
// as weights for the second endpoint, from 0 to 5, in alpha0 <= alpha1 mode,
|
|
// except for 110 and 111 which represent 0 and 1 constants.
|
|
uint XeDXT5High6StepAlphaWeights(uint codes_24b) {
|
|
// Initially:
|
|
// 000 - first endpoint.
|
|
// 001 - second endpoint.
|
|
// 010 - 4:1.
|
|
// 011 - 3:2.
|
|
// 100 - 2:3.
|
|
// 101 - 1:4.
|
|
// 110 - constant 0.
|
|
// 111 - constant 1.
|
|
// Create 3-bit masks (111 or 000) of whether the codes represent 0 or 1
|
|
// constants to keep them 110 and 111 later.
|
|
uint is_constant = codes_24b & 0x492492u & ((codes_24b & 0x924924u) >> 1u);
|
|
is_constant |= (is_constant << 1u) | (is_constant >> 1u);
|
|
// Store the codes for the constants (110 or 111), or 0 if not a constant.
|
|
uint constant_values =
|
|
((codes_24b & 0x249249u) | (0x492492u | 0x924924u)) & is_constant;
|
|
// Need to make 001 101, and subtract 1 from 010 and above (constants will be
|
|
// handled separately later).
|
|
// Whether the bits are 000 (the first endpoint only).
|
|
uint is_first = ((codes_24b & 0x249249u) | ((codes_24b & 0x492492u) >> 1u) |
|
|
((codes_24b & 0x924924u) >> 2u)) ^ 0x249249u;
|
|
// Whether the bits are 001 (the second endpoint only).
|
|
uint is_second = (codes_24b & 0x249249u) & ~((codes_24b & 0x492492u) >> 1u) &
|
|
~((codes_24b & 0x924924u) >> 2u);
|
|
// Change 000 to 001 so subtracting 1 will result in 0 (and there will never
|
|
// be overflow), subtract 1, and if the code was originally 001 (the second
|
|
// endpoint only), make it 101.
|
|
codes_24b =
|
|
((codes_24b | is_first) - 0x249249u) | is_second | (is_second << 2u);
|
|
// Make constants 110 and 111 again (they are 101 and 110 now).
|
|
return (codes_24b & ~is_constant) | constant_values;
|
|
}
|
|
|
|
// Sorts half (24 bits) of the codes of a DXT5 alpha block so they can be used
|
|
// as weights for XeDXT5RowToA8.
|
|
uint XeDXT5HighAlphaWeights(xesl_uint2 end, uint codes_24b) {
|
|
return (end.x <= end.y) ? XeDXT5High6StepAlphaWeights(codes_24b)
|
|
: XeDXT5High8StepAlphaWeights(codes_24b);
|
|
}
|
|
|
|
// Get alphas of a DXT5 alpha row in alpha0 > alpha1 mode. Endpoint alphas are
|
|
// in bits 0:7 and 8:15 of the first dword, weights can be obtained using
|
|
// XeDXT5High8StepAlphaWeights and must be shifted right by 12 * (row index & 1)
|
|
// before calling.
|
|
uint XeDXT58StepRowToA8(xesl_uint2 end, uint weights_high) {
|
|
uint weights_low = ~weights_high;
|
|
return
|
|
((end.x * (weights_low & 7u) + end.y * (weights_high & 7u)) / 7u) |
|
|
(((end.x * ((weights_low >> 3u) & 7u) +
|
|
end.y * ((weights_high >> 3u) & 7u)) / 7u) << 8u) |
|
|
(((end.x * ((weights_low >> 6u) & 7u) +
|
|
end.y * ((weights_high >> 6u) & 7u)) / 7u) << 16u) |
|
|
(((end.x * ((weights_low >> 9u) & 7u) +
|
|
end.y * ((weights_high >> 9u) & 7u)) / 7u) << 24u);
|
|
}
|
|
|
|
// Version of XeDXT58StepRowToA8 that returns values packed in low 8 bits of
|
|
// 16-bit parts, for DXN decompression.
|
|
xesl_uint2 XeDXT58StepRowToA8In16(xesl_uint2 end, uint weights_high) {
|
|
uint weights_low = ~weights_high;
|
|
return xesl_uint2(
|
|
((end.x * (weights_low & 7u) + end.y * (weights_high & 7u)) / 7u) |
|
|
(((end.x * ((weights_low >> 3u) & 7u) +
|
|
end.y * ((weights_high >> 3u) & 7u)) / 7u) << 16u),
|
|
((end.x * ((weights_low >> 6u) & 7u) +
|
|
end.y * ((weights_high >> 6u) & 7u)) / 7u) |
|
|
(((end.x * ((weights_low >> 9u) & 7u) +
|
|
end.y * ((weights_high >> 9u) & 7u)) / 7u) << 16u));
|
|
}
|
|
|
|
// Get alphas of a DXT5 alpha row in alpha0 <= alpha1 mode. Endpoint alphas are
|
|
// in bits 0:7 and 8:15 of the first dword, weights can be obtained using
|
|
// XeDXT5High6StepAlphaWeights and must be shifted right by 12 * (row index & 1)
|
|
// before calling.
|
|
uint XeDXT56StepRowToA8(xesl_uint2 end, uint weights_6step) {
|
|
// Make a mask for whether the weights are constants.
|
|
uint is_constant = weights_6step & 0x492u & ((weights_6step & 0x924u) >> 1u);
|
|
is_constant |= (is_constant << 1u) | (is_constant >> 1u);
|
|
// Get the weights for the first endpoint and remove constant from the
|
|
// interpolation (set weights of the endpoints to 0 for them). First need to
|
|
// zero the weights of the second endpoint so 6 or 7 won't be subtracted from
|
|
// 5 while getting the weights of the first endpoint.
|
|
uint weights_high = weights_6step & ~is_constant;
|
|
uint weights_low = ((5u * 0x249u) - weights_high) & ~is_constant;
|
|
// Interpolate.
|
|
uint row =
|
|
((end.x * (weights_low & 7u) + end.y * (weights_high & 7u)) / 5u) |
|
|
(((end.x * ((weights_low >> 3u) & 7u) +
|
|
end.y * ((weights_high >> 3u) & 7u)) / 5u) << 8u) |
|
|
(((end.x * ((weights_low >> 6u) & 7u) +
|
|
end.y * ((weights_high >> 6u) & 7u)) / 5u) << 16u) |
|
|
(((end.x * ((weights_low >> 9u) & 7u) +
|
|
end.y * ((weights_high >> 9u) & 7u)) / 5u) << 24u);
|
|
// Get the constant values as 1 bit per pixel separated by 7 bits.
|
|
uint constant_values = weights_6step & is_constant;
|
|
constant_values = (constant_values & 1u) |
|
|
((constant_values & (1u << 3u)) << (8u - 3u)) |
|
|
((constant_values & (1u << 6u)) << (16u - 6u)) |
|
|
((constant_values & (1u << 9u)) << (24u - 9u));
|
|
// Add constant 1 where needed.
|
|
return row + constant_values * 0xFFu;
|
|
}
|
|
|
|
// Version of XeDXT56StepRowToA8 that returns values packed in low 8 bits of
|
|
// 16-bit parts, for DXN decompression.
|
|
xesl_uint2 XeDXT56StepRowToA8In16(xesl_uint2 end, uint weights_6step) {
|
|
// Make a mask for whether the weights are constants.
|
|
uint is_constant = weights_6step & 0x492u & ((weights_6step & 0x924u) >> 1u);
|
|
is_constant |= (is_constant << 1u) | (is_constant >> 1u);
|
|
// Get the weights for the first endpoint and remove constant from the
|
|
// interpolation (set weights of the endpoints to 0 for them). First need to
|
|
// zero the weights of the second endpoint so 6 or 7 won't be subtracted from
|
|
// 5 while getting the weights of the first endpoint.
|
|
uint weights_high = weights_6step & ~is_constant;
|
|
uint weights_low = ((5u * 0x249u) - weights_high) & ~is_constant;
|
|
// Interpolate.
|
|
xesl_uint2 row = xesl_uint2(
|
|
((end.x * (weights_low & 7u) + end.y * (weights_high & 7u)) / 5u) |
|
|
(((end.x * ((weights_low >> 3u) & 7u) +
|
|
end.y * ((weights_high >> 3u) & 7u)) / 5u) << 16u),
|
|
((end.x * ((weights_low >> 6u) & 7u) +
|
|
end.y * ((weights_high >> 6u) & 7u)) / 5u) |
|
|
(((end.x * ((weights_low >> 9u) & 7u) +
|
|
end.y * ((weights_high >> 9u) & 7u)) / 5u) << 16u));
|
|
// Get the constant values as 1 bit per pixel separated by 7 bits.
|
|
uint constant_weights = weights_6step & is_constant;
|
|
xesl_uint2 constant_values = xesl_uint2(
|
|
(constant_weights & 1u) | ((constant_weights & (1u << 3u)) << (16u - 3u)),
|
|
((constant_weights >> 6u) & 1u) |
|
|
((constant_weights & (1u << 9u)) << (16u - 9u)));
|
|
// Add constant 1 where needed.
|
|
return row + constant_values * 0xFFu;
|
|
}
|
|
|
|
// Get alphas of a DXT5 alpha row. Endpoint alphas are in bits 0:7 and 8:15 of
|
|
// the first dword, weights can be obtained using XeDXT5HighAlphaWeights and
|
|
// must be shifted right by 12 * (row index & 1) before calling.
|
|
uint XeDXT5RowToA8(xesl_uint2 end, uint weights) {
|
|
return (end.x <= end.y) ? XeDXT56StepRowToA8(end, weights)
|
|
: XeDXT58StepRowToA8(end, weights);
|
|
}
|
|
|
|
// Version of XeDXT5RowToA8 that returns values packed in low 8 bits of 16-bit
|
|
// parts, for DXN decompression.
|
|
xesl_uint2 XeDXT5RowToA8In16(xesl_uint2 end, uint weights) {
|
|
return (end.x <= end.y) ? XeDXT56StepRowToA8In16(end, weights)
|
|
: XeDXT58StepRowToA8In16(end, weights);
|
|
}
|
|
|
|
// Converts one row of two CTX1 blocks to R8G8. Endpoints of block 0 in XY and
|
|
// of block 1 in ZW must be unpacked from 0xRRGGrrgg to 0x00gg00rr 0x00GG00RR so
|
|
// they can be multiplied by weights with room for overflow. Weights can be
|
|
// obtained using XeDXTHighColorWeights and must be shifted right by 8 * row
|
|
// index before calling.
|
|
xesl_uint4 XeCTX1TwoBlocksRowToR8G8(xesl_uint4 end_8in16,
|
|
xesl_uint2 weights_high) {
|
|
xesl_uint2 weights_low = ~weights_high;
|
|
const xesl_uint4 weights_shifts = xesl_uint4(0u, 2u, 4u, 6u);
|
|
xesl_uint4 row_8in16 =
|
|
((weights_low.xxxx >> weights_shifts) & 3u) * end_8in16.x +
|
|
((weights_high.xxxx >> weights_shifts) & 3u) * end_8in16.y;
|
|
xesl_uint4 result;
|
|
result.xy = ((row_8in16.xz & 0xFFFFu) / 3u) |
|
|
(((row_8in16.xz >> 16u) / 3u) << 8u) |
|
|
(((row_8in16.yw & 0xFFFFu) / 3u) << 16u) |
|
|
(((row_8in16.yw >> 16u) / 3u) << 24u);
|
|
row_8in16 = ((weights_low.yyyy >> weights_shifts) & 3u) * end_8in16.z +
|
|
((weights_high.yyyy >> weights_shifts) & 3u) * end_8in16.w;
|
|
result.zw = ((row_8in16.xz & 0xFFFFu) / 3u) |
|
|
(((row_8in16.xz >> 16u) / 3u) << 8u) |
|
|
(((row_8in16.yw & 0xFFFFu) / 3u) << 16u) |
|
|
(((row_8in16.yw >> 16u) / 3u) << 24u);
|
|
return result;
|
|
}
|
|
|
|
#endif // XENIA_GPU_SHADERS_PIXEL_FORMATS_XESLI_
|