/**
 ******************************************************************************
 * 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(float4_xe f) {
  uint4_xe n = uint4_xe(saturate_xe(f) * float2_xe(31.0f, 1.0f).xxxy + 0.5f);
  return n.r | (n.g << 5) | (n.b << 10) | (n.a << 15);
}

uint XePackR5G6B5UNorm(float3_xe f) {
  uint3_xe n = uint3_xe(saturate_xe(f) * float3_xe(31.0f, 63.0f, 31.0f) + 0.5f);
  return n.r | (n.g << 5) | (n.b << 11);
}

uint XePackR5G5B6UNorm(float3_xe f) {
  uint3_xe n = uint3_xe(saturate_xe(f) * float3_xe(31.0f, 31.0f, 63.0f) + 0.5f);
  return n.r | (n.g << 5) | (n.b << 10);
}

uint XePackR8G8B8A8UNorm(float4_xe f) {
  uint4_xe n = uint4_xe(saturate_xe(f) * 255.0f + 0.5f);
  return n.r | (n.g << 8) | (n.b << 16) | (n.a << 24);
}

uint XePackR10G10B10A2UNorm(float4_xe f) {
  uint4_xe n = uint4_xe(saturate_xe(f) * float2_xe(1023.0f, 3.0f).xxxy + 0.5f);
  return n.r | (n.g << 10) | (n.b << 20) | (n.a << 30);
}

uint XePackR4G4B4A4UNorm(float4_xe f) {
  uint4_xe n = uint4_xe(saturate_xe(f) * 15.0f + 0.5f);
  return n.r | (n.g << 4) | (n.b << 8) | (n.a << 12);
}

uint XePackR11G11B10UNorm(float3_xe f) {
  uint3_xe n =
      uint3_xe(saturate_xe(f) * float3_xe(2047.0f, 2047.0f, 1023.0f) + 0.5);
  return n.r | (n.g << 11) | (n.b << 22);
}

uint XePackR10G11B11UNorm(float3_xe f) {
  uint3_xe n =
      uint3_xe(saturate_xe(f) * float3_xe(1023.0f, 2047.0f, 2047.0f) + 0.5f);
  return n.r | (n.g << 10) | (n.b << 21);
}

uint XePackR16G16UNorm(float2_xe f) {
  uint2_xe n = uint2_xe(saturate_xe(f) * 65535.0f + 0.5f);
  return n.r | (n.g << 16);
}

uint2_xe XePackR16G16B16A16UNorm(float4_xe f) {
  uint4_xe n = uint4_xe(saturate_xe(f) * 65535.0f + 0.5f);
  return n.rb | (n.ga << 16);
}

uint2_xe XePack16bpp4PixelsInUInt2(float4_xe pixel_0, float4_xe pixel_1,
                                   float4_xe pixel_2, float4_xe pixel_3,
                                   uint format) {
  uint2_xe 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(float4_xe(pixel_0.rg, pixel_1.rg));
      packed.y = XePackR8G8B8A8UNorm(float4_xe(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(float4_xe(pixel_0.r, pixel_1.r,
                                                 pixel_2.r, pixel_3.r));
      break;
    default:
      // Treat as something (16_FLOAT).
      packed.x = pack_half_2x16_xe(float2_xe(pixel_0.r, pixel_1.r));
      packed.y = pack_half_2x16_xe(float2_xe(pixel_2.r, pixel_3.r));
      break;
  }
  return packed;
}

uint4_xe XePack32bpp4Pixels(float4_xe pixel_0, float4_xe pixel_1,
                            float4_xe pixel_2, float4_xe pixel_3,
                            uint format) {
  uint4_xe 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 = pack_half_2x16_xe(float2_xe(pixel_0.r, pixel_0.g));
      packed.y = pack_half_2x16_xe(float2_xe(pixel_1.r, pixel_1.g));
      packed.z = pack_half_2x16_xe(float2_xe(pixel_2.r, pixel_2.g));
      packed.w = pack_half_2x16_xe(float2_xe(pixel_3.r, pixel_3.g));
      break;
    default:
      // Treat as 32_FLOAT.
      packed.x = float_bits_to_uint_xe(pixel_0.r);
      packed.y = float_bits_to_uint_xe(pixel_1.r);
      packed.z = float_bits_to_uint_xe(pixel_2.r);
      packed.w = float_bits_to_uint_xe(pixel_3.r);
      break;
  }
  return packed;
}

void XePack64bpp4Pixels(float4_xe pixel_0, float4_xe pixel_1,
                        float4_xe pixel_2, float4_xe pixel_3, uint format,
                        out_param_xe(uint4_xe, packed_01),
                        out_param_xe(uint4_xe, 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 = pack_half_2x16_xe(float2_xe(pixel_0.r, pixel_0.g));
      packed_01.y = pack_half_2x16_xe(float2_xe(pixel_0.b, pixel_0.a));
      packed_01.z = pack_half_2x16_xe(float2_xe(pixel_1.r, pixel_1.g));
      packed_01.w = pack_half_2x16_xe(float2_xe(pixel_1.b, pixel_1.a));
      packed_23.x = pack_half_2x16_xe(float2_xe(pixel_2.r, pixel_2.g));
      packed_23.y = pack_half_2x16_xe(float2_xe(pixel_2.b, pixel_2.a));
      packed_23.z = pack_half_2x16_xe(float2_xe(pixel_3.r, pixel_3.g));
      packed_23.w = pack_half_2x16_xe(float2_xe(pixel_3.b, pixel_3.a));
      break;
    default:
      // Treat as 32_32_FLOAT.
      packed_01 = float_bits_to_uint_xe(float4_xe(pixel_0.rg, pixel_1.rg));
      packed_23 = float_bits_to_uint_xe(float4_xe(pixel_2.rg, pixel_3.rg));
      break;
  }
}

// EDRAM color format unpacking.

float4_xe XeUnpackR8UNormX4(uint4_xe p) {
  return float4_xe(p & 255u) * (1.0f / 255.0f);
}

float4_xe XeUnpackR8G8B8A8UNorm(uint p) {
  return float4_xe((uint_x4_xe(p) >> uint4_xe(0u, 8u, 16u, 24u)) & 255u) *
         (1.0f / 255.0f);
}

float4_xe XeUnpackR10UNormX4(uint4_xe p) {
  return float4_xe(p & 1023u) * (1.0f / 1023.0f);
}

float4_xe XeUnpackR10G10B10A2UNorm(uint p) {
  return float4_xe((uint_x4_xe(p) >> uint4_xe(0u, 10u, 20u, 30u)) &
                      uint2_xe(1023u, 3u).xxxy) *
         float2_xe(1.0f / 1023.0f, 1.0f / 3.0f).xxxy;
}

float4_xe XeUnpackR10FloatX4(uint4_xe p) {
  // https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
  uint4_xe f10u32 = p & 0x3FFu;
  uint4_xe mantissa = f10u32 & 0x7Fu;
  uint4_xe exponent = f10u32 >> 7u;
  // Normalize the values for the denormalized components.
  // Exponent = 1;
  // do { Exponent--; Mantissa <<= 1; } while ((Mantissa & 0x80) == 0);
  bool4_xe is_denormalized = equal_xe(exponent, uint_x4_xe(0u));
  uint4_xe mantissa_lzcnt = uint_x4_xe(7u) - first_one_bit_high_xe(mantissa);
  exponent = select_xe(is_denormalized, (uint_x4_xe(1u) - mantissa_lzcnt),
                       exponent);
  mantissa = select_xe(is_denormalized, ((mantissa << mantissa_lzcnt) & 0x7Fu),
                       mantissa);
  // Combine into 32-bit float bits and clear zeros.
  return uint_bits_to_float_xe(select_xe(
      equal_xe(f10u32, uint_x4_xe(0u)), uint_x4_xe(0u),
      ((exponent + 124u) << 23u) | (mantissa << 16u)));
}

float4_xe XeUnpackR10G10B10A2Float(uint p) {
  // https://github.com/Microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexConvert.cpp
  uint3_xe rgb_f10u32 = (uint_x3_xe(p) >> uint3_xe(0u, 10u, 20u)) & 0x3FFu;
  uint3_xe mantissa = rgb_f10u32 & 0x7Fu;
  uint3_xe exponent = rgb_f10u32 >> 7u;
  // Normalize the values for the denormalized components.
  // Exponent = 1;
  // do { Exponent--; Mantissa <<= 1; } while ((Mantissa & 0x80) == 0);
  bool3_xe is_denormalized = equal_xe(exponent, uint_x3_xe(0u));
  uint3_xe mantissa_lzcnt = uint_x3_xe(7u) - first_one_bit_high_xe(mantissa);
  exponent = select_xe(is_denormalized, (uint_x3_xe(1u) - mantissa_lzcnt),
                       exponent);
  mantissa = select_xe(is_denormalized, ((mantissa << mantissa_lzcnt) & 0x7Fu),
                       mantissa);
  // Combine into 32-bit float bits and clear zeros.
  uint3_xe rgb_f32u32 = select_xe(
      equal_xe(rgb_f10u32, uint_x3_xe(0u)), uint_x3_xe(0u),
      ((exponent + 124u) << 23u) | (mantissa << 16u));
  return float4_xe(uint_bits_to_float_xe(rgb_f32u32),
                   float(p >> 30u) * (1.0f / 3.0f));
}

// 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.

float4_xe XeUnpackR16EdramX4(uint4_xe p) {
  return max(float_x4_xe(-1.0f),
             float4_xe(int4_xe(p) << 16 >> 16) * (32.0f / 32767.0f));
}

float2_xe XeUnpackR16G16Edram(uint p) {
  return max(float_x2_xe(-1.0f),
             float2_xe(int_x2_xe(int(p)) << int2_xe(16, 0) >> 16) *
                 (32.0f / 32767.0f));
}

float4_xe XeUnpackR16G16B16A16Edram(uint2_xe p) {
  return max(float_x4_xe(-1.0f),
             float4_xe(int2_xe(p).xxyy << int2_xe(16, 0).xyxy >> 16) *
                 (32.0f / 32767.0f));
}

// Xenos 16-bit packed textures are RGBA, but in Direct3D 12 they are BGRA.

uint4_xe XeR5G5B5A1ToB5G5R5A1(uint4_xe packed_texels) {
  return (packed_texels & 0x83E083E0u) |
         ((packed_texels & 0x001F001Fu) << 10u) |
         ((packed_texels & 0x7C007C00u) >> 10u);
}

uint4_xe XeR5G6B5ToB5G6R5(uint4_xe packed_texels) {
  return (packed_texels & 0x07E007E0u) |
         ((packed_texels & 0x001F001Fu) << 11u) |
         ((packed_texels & 0xF800F800u) >> 11u);
}

uint4_xe XeR4G4B4A4ToB4G4R4A4(uint4_xe packed_texels) {
  return (packed_texels & 0xF0F0F0F0u) | ((packed_texels & 0x000F000Fu) << 8u) |
         ((packed_texels & 0x0F000F00u) >> 8u);
}

uint4_xe XeR4G4B4A4ToA4R4G4B4(uint4_xe packed_texels) {
  return ((packed_texels & 0x0FFF0FFFu) << 4u) |
         ((packed_texels & 0xF000F000u) >> 12u);
}

// RRRRR GGGGG BBBBBB to GGGGG BBBBBB RRRRR (use RBGA swizzle when reading).
uint4_xe XeR5G5B6ToB5G6R5WithRBGASwizzle(uint4_xe packed_texels) {
  return ((packed_texels & 0x001F001Fu) << 11u) |
         ((packed_texels & 0xFFE0FFE0u) >> 5u);
}

uint4_xe XeGBGR8ToGRGB8(uint4_xe 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.
uint4_xe XeBGRG8ToRGBG8(uint4_xe packed_texels) {
  return (packed_texels & 0xFF00FF00u) |
         ((packed_texels & 0x000000FFu) << 16u) |
         ((packed_texels & 0x00FF0000u) >> 16u);
}

// On the Xenos, it appears that the last existing component of a texture is
// replicated into the missing components. Writing blue directly to the alpha
// instead of 1 also makes this conversion correct for both unsigned and signed
// data.

uint4_xe XeGBGR8ToRGB8WithRGBBSwizzle(uint2_xe packed_texels) {
  uint2_xe rba = (packed_texels >> 24u) |
                 (((packed_texels & 0x0000FF00u) << 8u) * 0x101u);
  uint4_xe g = uint4_xe((packed_texels & 0x000000FFu) << 8u,
                        (packed_texels & 0x00FF0000u) >> 8u).xzyw;
  return rba.xxyy | g;
}
void XeGBGR8ToRGB8WithRGBBSwizzle(uint4_xe packed_texels,
                                  out_param_xe(uint4_xe, out_01),
                                  out_param_xe(uint4_xe, out_23)) {
  out_01 = XeGBGR8ToRGB8WithRGBBSwizzle(packed_texels.xy);
  out_23 = XeGBGR8ToRGB8WithRGBBSwizzle(packed_texels.zw);
}

uint4_xe XeBGRG8ToRGB8WithRGBBSwizzle(uint2_xe packed_texels) {
  uint2_xe rba = ((packed_texels & 0x00FF0000u) >> 16u) |
                 (((packed_texels & 0x000000FFu) << 16u) * 0x101u);
  uint4_xe g = uint4_xe(packed_texels & 0x0000FF00u,
                        (packed_texels & 0xFF000000u) >> 16u).xzyw;
  return rba.xxyy | g;
}
void XeBGRG8ToRGB8WithRGBBSwizzle(uint4_xe packed_texels,
                                  out_param_xe(uint4_xe, out_01),
                                  out_param_xe(uint4_xe, out_23)) {
  out_01 = XeBGRG8ToRGB8WithRGBBSwizzle(packed_texels.xy);
  out_23 = XeBGRG8ToRGB8WithRGBBSwizzle(packed_texels.zw);
}

uint4_xe XeR10G11B11UNormToRGBA16(uint2_xe packed_texels) {
  // Red and blue.
  uint4_xe result =
      (((packed_texels.xxyy >> uint2_xe(0u, 21u).xyxy) &
        uint2_xe(1023u, 2047u).xyxy) <<
       uint2_xe(6u, 5u).xyxy) |
      ((packed_texels.xxyy >> uint2_xe(4u, 27u).xyxy) &
       uint2_xe(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(uint4_xe packed_texels,
                              out_param_xe(uint4_xe, out_01),
                              out_param_xe(uint4_xe, out_23)) {
  out_01 = XeR10G11B11UNormToRGBA16(packed_texels.xy);
  out_23 = XeR10G11B11UNormToRGBA16(packed_texels.zw);
}

uint4_xe XeR11G11B10UNormToRGBA16(uint2_xe packed_texels) {
  // Red and blue.
  uint4_xe result =
      (((packed_texels.xxyy >> uint2_xe(0u, 22u).xyxy) &
        uint2_xe(2047u, 1023u).xyxy) <<
       uint2_xe(5u, 6u).xyxy) |
      ((packed_texels.xxyy >> uint2_xe(6u, 26u).xyxy) &
       uint2_xe(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(uint4_xe packed_texels,
                              out_param_xe(uint4_xe, out_01),
                              out_param_xe(uint4_xe, out_23)) {
  out_01 = XeR11G11B10UNormToRGBA16(packed_texels.xy);
  out_23 = XeR11G11B10UNormToRGBA16(packed_texels.zw);
}

// Assuming the original number has only 10 bits.
uint2_xe XeSNorm10To16(uint2_xe s10) {
  uint2_xe signs = s10 >> 9u;
  bool2_xe is_negative = not_equal_xe(signs, uint_x2_xe(0u));
  // -512 and -511 are both -1.0, but with -512 the conversion will overflow.
  s10 = select_xe(equal_xe(s10, uint_x2_xe(0x200u)), uint_x2_xe(0x201u), s10);
  // Take the absolute value.
  s10 = (s10 ^ select_xe(is_negative, uint_x2_xe(0x3FFu), uint_x2_xe(0u))) +
        signs;
  // Expand the 9-bit absolute value to 15 bits like unorm.
  s10 = (s10 << 6u) | (s10 >> 3u);
  // Apply the sign.
  return (s10 ^ select_xe(is_negative, uint_x2_xe(0xFFFFu), uint_x2_xe(0u))) +
         signs;
}

// Assuming the original number has only 11 bits.
uint2_xe XeSNorm11To16(uint2_xe s11) {
  uint2_xe signs = s11 >> 10u;
  bool2_xe is_negative = not_equal_xe(signs, uint_x2_xe(0u));
  // -1024 and -1023 are both -1.0, but with -1024 the conversion will overflow.
  s11 = select_xe(equal_xe(s11, uint_x2_xe(0x400u)), uint_x2_xe(0x401u), s11);
  // Take the absolute value.
  s11 = (s11 ^ select_xe(is_negative, uint_x2_xe(0x7FFu), uint_x2_xe(0u))) +
        signs;
  // Expand the 10-bit absolute value to 15 bits like unorm.
  s11 = (s11 << 5u) | (s11 >> 5u);
  // Apply the sign.
  return (s11 ^ select_xe(is_negative, uint_x2_xe(0xFFFFu), uint_x2_xe(0u))) +
         signs;
}

uint4_xe XeR10G11B11SNormToRGBA16(uint2_xe packed_texels) {
  // uint4(RG0, RG1, BA0, BA1).xzyw == uint4(RG0, BA0, RG1, BA1).
  return uint4_xe(XeSNorm10To16(packed_texels & 1023u) |
                      (XeSNorm11To16((packed_texels >> 10u) & 2047u) << 16u),
                  XeSNorm11To16(packed_texels >> 21u) | 0x7FFF0000u).xzyw;
}
void XeR10G11B11SNormToRGBA16(uint4_xe packed_texels,
                              out_param_xe(uint4_xe, out_01),
                              out_param_xe(uint4_xe, out_23)) {
  out_01 = XeR10G11B11SNormToRGBA16(packed_texels.xy);
  out_23 = XeR10G11B11SNormToRGBA16(packed_texels.zw);
}

uint4_xe XeR11G11B10SNormToRGBA16(uint2_xe packed_texels) {
  // uint4(RG0, RG1, BA0, BA1).xzyw == uint4(RG0, BA0, RG1, BA1).
  return uint4_xe(XeSNorm11To16(packed_texels & 2047u) |
                        (XeSNorm11To16((packed_texels >> 11u) & 2047u) << 16u),
                    XeSNorm10To16(packed_texels >> 22u) | 0x7FFF0000u).xzyw;
}
void XeR11G11B10SNormToRGBA16(uint4_xe packed_texels,
                              out_param_xe(uint4_xe, out_01),
                              out_param_xe(uint4_xe, out_23)) {
  out_01 = XeR11G11B10SNormToRGBA16(packed_texels.xy);
  out_23 = XeR11G11B10SNormToRGBA16(packed_texels.zw);
}

uint4_xe XeRG16UNormToRG16Float(uint4_xe packed_texels) {
  float4_xe r = float4_xe(packed_texels & 0xFFFFu) * (1.0f / 65535.0f);
  float4_xe g = float4_xe(packed_texels >> 16u) * (1.0f / 65535.0f);
  return uint4_xe(pack_half_2x16_xe(float2_xe(r.x, g.x)),
                  pack_half_2x16_xe(float2_xe(r.y, g.y)),
                  pack_half_2x16_xe(float2_xe(r.z, g.z)),
                  pack_half_2x16_xe(float2_xe(r.w, g.w)));
}

uint4_xe XeRG16SNormToRG16Float(uint4_xe packed_texels) {
  float4_xe r =
      max(float_x4_xe(-1.0f),
          float4_xe(int4_xe(packed_texels) << 16 >> 16) * (1.0f / 32767.0f));
  float4_xe g =
      max(float_x4_xe(-1.0f),
          float4_xe(int4_xe(packed_texels) >> 16) * (1.0f / 32767.0f));
  return uint4_xe(pack_half_2x16_xe(float2_xe(r.x, g.x)),
                  pack_half_2x16_xe(float2_xe(r.y, g.y)),
                  pack_half_2x16_xe(float2_xe(r.z, g.z)),
                  pack_half_2x16_xe(float2_xe(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, bool round_to_nearest_even) {
  // 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);
  if (round_to_nearest_even) {
    f24u32 += 3u + ((f24u32 >> 3u) & 1u);
  }
  return (f24u32 >> 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 - first_one_bit_high_xe(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;
}

uint4_xe XeFloat20e4To32(uint4_xe f24u32) {
  uint4_xe mantissa = f24u32 & 0xFFFFFu;
  uint4_xe exponent = f24u32 >> 20u;
  // Normalize the values for the denormalized components.
  // Exponent = 1;
  // do { Exponent--; Mantissa <<= 1; } while ((Mantissa & 0x100000) == 0);
  bool4_xe is_denormalized = equal_xe(exponent, uint_x4_xe(0u));
  uint4_xe mantissa_lzcnt = uint_x4_xe(20u) - first_one_bit_high_xe(mantissa);
  exponent =
      select_xe(is_denormalized, uint_x4_xe(1u) - mantissa_lzcnt, exponent);
  mantissa = select_xe(
      is_denormalized, (mantissa << mantissa_lzcnt) & 0xFFFFFu, mantissa);
  // Combine into 32-bit float bits and clear zeros.
  return select_xe(equal_xe(f24u32, uint_x4_xe(0u)), uint_x4_xe(0u),
                   ((exponent + 112u) << 23u) | (mantissa << 3u));
}

uint2_xe XeFloat20e4To32(uint2_xe f24u32) {
  return XeFloat20e4To32(f24u32.xyxx).xy;
}

float4_xe XeUNorm24To32(uint4_xe n24) {
  // Not 1.0f / 16777215.0f 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.0f behaves this way.
  return float4_xe(n24 + (n24 >> 23u)) * (1.0f / 16777216.0f);
}

float2_xe XeUNorm24To32(uint2_xe 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.
uint2_xe 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
  uint2_xe bgr_end_8in10 =
      // Blue in 0:4 and 16:20 - to 3:7.
      (uint2_xe(bgr_end_565 << 3u, bgr_end_565 >> (16u - 3u)) & (31u << 3u)) |
      // Green in 5:10 and 21:26 - to 12:17.
      (uint2_xe(bgr_end_565 << (12u - 5u), bgr_end_565 >> (21u - 12u)) &
       (63u << 12u)) |
      // Red in 11:15 and 27:31 - to 23:27.
      (uint2_xe(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.
uint4_xe XeDXTHighColorWeights(uint4_xe 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);
}
uint2_xe XeDXTHighColorWeights(uint2_xe codes) {
  return XeDXTHighColorWeights(codes.xyxx).xy;
}
uint XeDXTHighColorWeights(uint codes) {
  return XeDXTHighColorWeights(uint_x2_xe(codes)).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.
uint4_xe XeDXTOpaqueRowToRGB8(uint2_xe bgr_end_8in10, uint weights_high) {
  const uint4_xe weights_shifts = uint4_xe(0u, 2u, 4u, 6u);
  uint4_xe bgr_row_8in10_3x =
      ((uint_x4_xe(~weights_high) >> weights_shifts) & 3u) * bgr_end_8in10.x +
      ((uint_x4_xe(weights_high) >> 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.
uint4_xe XeDXT1TransWeights(uint4_xe 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.
uint4_xe XeDXT1TransRowToRGBA8(uint2_xe bgr_end_8in10, uint weights) {
  const uint4_xe weights_shifts_low = uint4_xe(0u, 2u, 4u, 6u);
  const uint4_xe weights_shifts_high = uint4_xe(1u, 3u, 5u, 7u);
  uint4_xe bgr_row_8in10_scaled =
      ((uint_x4_xe(weights) >> weights_shifts_low) & 1u) * bgr_end_8in10.x +
      ((uint_x4_xe(weights) >> 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);
  uint4_xe bgr_shift =
      (uint_x4_xe(weights_sums_log2) >> 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) +
         (((uint_x4_xe(weights_alpha) >> 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.
uint4_xe XeDXT3FourBlocksRowToA8(uint4_xe 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);
}

uint4_xe XeDXT3AAs1111TwoBlocksRowToBGRA4(uint2_xe 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.
  uint4_xe row =
      (((halfblocks.xxyy >> uint2_xe(3u, 11u).xyxy) & 1u) << 8u) |
      (((halfblocks.xxyy >> uint2_xe(7u, 15u).xyxy) & 1u) << 24u) |
      (((halfblocks.xxyy >> uint2_xe(2u, 10u).xyxy) & 1u) << 4u) |
      (((halfblocks.xxyy >> uint2_xe(6u, 14u).xyxy) & 1u) << 20u) |
      ((halfblocks.xxyy >> uint2_xe(1u, 9u).xyxy) & 1u) |
      (((halfblocks.xxyy >> uint2_xe(5u, 13u).xyxy) & 1u) << 16u) |
      (((halfblocks.xxyy >> uint2_xe(0u, 8u).xyxy) & 1u) << 12u) |
      (((halfblocks.xxyy >> uint2_xe(4u, 12u).xyxy) & 1u) << 28u);
  row |= row << 1u;
  row |= row << 2u;
  return row;
}

uint4_xe XeDXT3AAs1111TwoBlocksRowToARGB4(uint2_xe 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.
  uint4_xe row =
      (((halfblocks.xxyy >> uint2_xe(3u, 11u).xyxy) & 1u) << 4u) |
      (((halfblocks.xxyy >> uint2_xe(7u, 15u).xyxy) & 1u) << 20u) |
      (((halfblocks.xxyy >> uint2_xe(2u, 10u).xyxy) & 1u) << 8u) |
      (((halfblocks.xxyy >> uint2_xe(6u, 14u).xyxy) & 1u) << 24u) |
      (((halfblocks.xxyy >> uint2_xe(1u, 9u).xyxy) & 1u) << 12u) |
      (((halfblocks.xxyy >> uint2_xe(5u, 13u).xyxy) & 1u) << 28u) |
      ((halfblocks.xxyy >> uint2_xe(0u, 8u).xyxy) & 1u) |
      (((halfblocks.xxyy >> uint2_xe(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(uint2_xe 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(uint2_xe 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.
uint2_xe XeDXT58StepRowToA8In16(uint2_xe end, uint weights_high) {
  uint weights_low = ~weights_high;
  return uint2_xe(
      ((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(uint2_xe 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.
uint2_xe XeDXT56StepRowToA8In16(uint2_xe 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.
  uint2_xe row = uint2_xe(
      ((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;
  uint2_xe constant_values = uint2_xe(
      (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(uint2_xe 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.
uint2_xe XeDXT5RowToA8In16(uint2_xe 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.
uint4_xe XeCTX1TwoBlocksRowToR8G8(uint4_xe end_8in16, uint2_xe weights_high) {
  uint2_xe weights_low = ~weights_high;
  const uint4_xe weights_shifts = uint4_xe(0u, 2u, 4u, 6u);
  uint4_xe row_8in16 =
      ((weights_low.xxxx >> weights_shifts) & 3u) * end_8in16.x +
      ((weights_high.xxxx >> weights_shifts) & 3u) * end_8in16.y;
  uint4_xe 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_
