/*
 * Copyright 2015 Advanced Micro Devices, Inc.
 * Copyright 2024 Valve Corporation
 *
 * SPDX-License-Identifier: MIT
 */

#include "ac_gpu_info.h"
#include "ac_formats.h"
#include "ac_gpu_info.h"

#include "sid.h"

uint32_t
ac_translate_buffer_numformat(const struct util_format_description *desc,
                              int first_non_void)
{
   if (desc->format == PIPE_FORMAT_R11G11B10_FLOAT)
      return V_008F0C_BUF_NUM_FORMAT_FLOAT;

   assert(first_non_void >= 0);

   switch (desc->channel[first_non_void].type) {
   case UTIL_FORMAT_TYPE_SIGNED:
   case UTIL_FORMAT_TYPE_FIXED:
      if (desc->channel[first_non_void].size >= 32 || desc->channel[first_non_void].pure_integer)
         return V_008F0C_BUF_NUM_FORMAT_SINT;
      else if (desc->channel[first_non_void].normalized)
         return V_008F0C_BUF_NUM_FORMAT_SNORM;
      else
         return V_008F0C_BUF_NUM_FORMAT_SSCALED;
      break;
   case UTIL_FORMAT_TYPE_UNSIGNED:
      if (desc->channel[first_non_void].size >= 32 || desc->channel[first_non_void].pure_integer)
         return V_008F0C_BUF_NUM_FORMAT_UINT;
      else if (desc->channel[first_non_void].normalized)
         return V_008F0C_BUF_NUM_FORMAT_UNORM;
      else
         return V_008F0C_BUF_NUM_FORMAT_USCALED;
      break;
   case UTIL_FORMAT_TYPE_FLOAT:
   default:
      return V_008F0C_BUF_NUM_FORMAT_FLOAT;
   }
}

uint32_t
ac_translate_buffer_dataformat(const struct util_format_description *desc,
                               int first_non_void)
{
   int i;

   if (desc->format == PIPE_FORMAT_R11G11B10_FLOAT)
      return V_008F0C_BUF_DATA_FORMAT_10_11_11;

   assert(first_non_void >= 0);

   if (desc->nr_channels == 4 && desc->channel[0].size == 10 && desc->channel[1].size == 10 &&
       desc->channel[2].size == 10 && desc->channel[3].size == 2)
      return V_008F0C_BUF_DATA_FORMAT_2_10_10_10;

   /* See whether the components are of the same size. */
   for (i = 0; i < desc->nr_channels; i++) {
      if (desc->channel[first_non_void].size != desc->channel[i].size)
         return V_008F0C_BUF_DATA_FORMAT_INVALID;
   }

   switch (desc->channel[first_non_void].size) {
   case 8:
      switch (desc->nr_channels) {
      case 1:
      case 3: /* 3 loads */
         return V_008F0C_BUF_DATA_FORMAT_8;
      case 2:
         return V_008F0C_BUF_DATA_FORMAT_8_8;
      case 4:
         return V_008F0C_BUF_DATA_FORMAT_8_8_8_8;
      }
      break;
   case 16:
      switch (desc->nr_channels) {
      case 1:
      case 3: /* 3 loads */
         return V_008F0C_BUF_DATA_FORMAT_16;
      case 2:
         return V_008F0C_BUF_DATA_FORMAT_16_16;
      case 4:
         return V_008F0C_BUF_DATA_FORMAT_16_16_16_16;
      }
      break;
   case 32:
      switch (desc->nr_channels) {
      case 1:
         return V_008F0C_BUF_DATA_FORMAT_32;
      case 2:
         return V_008F0C_BUF_DATA_FORMAT_32_32;
      case 3:
         return V_008F0C_BUF_DATA_FORMAT_32_32_32;
      case 4:
         return V_008F0C_BUF_DATA_FORMAT_32_32_32_32;
      }
      break;
   case 64:
      /* Legacy double formats. */
      switch (desc->nr_channels) {
      case 1: /* 1 load */
         return V_008F0C_BUF_DATA_FORMAT_32_32;
      case 2: /* 1 load */
         return V_008F0C_BUF_DATA_FORMAT_32_32_32_32;
      case 3: /* 3 loads */
         return V_008F0C_BUF_DATA_FORMAT_32_32;
      case 4: /* 2 loads */
         return V_008F0C_BUF_DATA_FORMAT_32_32_32_32;
      }
      break;
   }

   return V_008F0C_BUF_DATA_FORMAT_INVALID;
}

uint32_t
ac_translate_tex_numformat(const struct util_format_description *desc,
                           int first_non_void)
{
   uint32_t num_format;

   switch (desc->format) {
   case PIPE_FORMAT_S8_UINT_Z24_UNORM:
      num_format = V_008F14_IMG_NUM_FORMAT_UNORM;
      break;
   default:
      if (first_non_void < 0) {
         if (util_format_is_compressed(desc->format)) {
            switch (desc->format) {
            case PIPE_FORMAT_DXT1_SRGB:
            case PIPE_FORMAT_DXT1_SRGBA:
            case PIPE_FORMAT_DXT3_SRGBA:
            case PIPE_FORMAT_DXT5_SRGBA:
            case PIPE_FORMAT_BPTC_SRGBA:
            case PIPE_FORMAT_ETC2_SRGB8:
            case PIPE_FORMAT_ETC2_SRGB8A1:
            case PIPE_FORMAT_ETC2_SRGBA8:
               num_format = V_008F14_IMG_NUM_FORMAT_SRGB;
               break;
            case PIPE_FORMAT_RGTC1_SNORM:
            case PIPE_FORMAT_LATC1_SNORM:
            case PIPE_FORMAT_RGTC2_SNORM:
            case PIPE_FORMAT_LATC2_SNORM:
            case PIPE_FORMAT_ETC2_R11_SNORM:
            case PIPE_FORMAT_ETC2_RG11_SNORM:
            /* implies float, so use SNORM/UNORM to determine
               whether data is signed or not */
            case PIPE_FORMAT_BPTC_RGB_FLOAT:
               num_format = V_008F14_IMG_NUM_FORMAT_SNORM;
               break;
            default:
               num_format = V_008F14_IMG_NUM_FORMAT_UNORM;
               break;
            }
         } else if (desc->layout == UTIL_FORMAT_LAYOUT_SUBSAMPLED) {
            num_format = V_008F14_IMG_NUM_FORMAT_UNORM;
         } else {
            num_format = V_008F14_IMG_NUM_FORMAT_FLOAT;
         }
      } else if (desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) {
         num_format = V_008F14_IMG_NUM_FORMAT_SRGB;
      } else {
         switch (desc->channel[first_non_void].type) {
         case UTIL_FORMAT_TYPE_FLOAT:
            num_format = V_008F14_IMG_NUM_FORMAT_FLOAT;
            break;
         case UTIL_FORMAT_TYPE_SIGNED:
            if (desc->channel[first_non_void].normalized)
               num_format = V_008F14_IMG_NUM_FORMAT_SNORM;
            else if (desc->channel[first_non_void].pure_integer)
               num_format = V_008F14_IMG_NUM_FORMAT_SINT;
            else
               num_format = V_008F14_IMG_NUM_FORMAT_SSCALED;
            break;
         case UTIL_FORMAT_TYPE_UNSIGNED:
            if (desc->channel[first_non_void].normalized)
               num_format = V_008F14_IMG_NUM_FORMAT_UNORM;
            else if (desc->channel[first_non_void].pure_integer)
               num_format = V_008F14_IMG_NUM_FORMAT_UINT;
            else
               num_format = V_008F14_IMG_NUM_FORMAT_USCALED;
            break;
         default:
            num_format = V_008F14_IMG_NUM_FORMAT_UNORM;
            break;
         }
      }
   }

   return num_format;
}

uint32_t
ac_translate_tex_dataformat(const struct radeon_info *info,
                            const struct util_format_description *desc,
                            int first_non_void)
{
   bool uniform = true;
   int i;

   /* Colorspace (return non-RGB formats directly). */
   switch (desc->colorspace) {
   /* Depth stencil formats */
   case UTIL_FORMAT_COLORSPACE_ZS:
      switch (desc->format) {
      case PIPE_FORMAT_Z16_UNORM:
         return V_008F14_IMG_DATA_FORMAT_16;
      case PIPE_FORMAT_X24S8_UINT:
      case PIPE_FORMAT_S8X24_UINT:
         /*
          * Implemented as an 8_8_8_8 data format to fix texture
          * gathers in stencil sampling. This affects at least
          * GL45-CTS.texture_cube_map_array.sampling on GFX8.
          */
         if (info->gfx_level <= GFX8)
            return V_008F14_IMG_DATA_FORMAT_8_8_8_8;

         if (desc->format == PIPE_FORMAT_X24S8_UINT)
            return V_008F14_IMG_DATA_FORMAT_8_24;
         else
            return V_008F14_IMG_DATA_FORMAT_24_8;
      case PIPE_FORMAT_Z24X8_UNORM:
      case PIPE_FORMAT_Z24_UNORM_S8_UINT:
         return V_008F14_IMG_DATA_FORMAT_8_24;
      case PIPE_FORMAT_X8Z24_UNORM:
      case PIPE_FORMAT_S8_UINT_Z24_UNORM:
         return V_008F14_IMG_DATA_FORMAT_24_8;
      case PIPE_FORMAT_S8_UINT:
         return V_008F14_IMG_DATA_FORMAT_8;
      case PIPE_FORMAT_Z32_FLOAT:
         return V_008F14_IMG_DATA_FORMAT_32;
      case PIPE_FORMAT_X32_S8X24_UINT:
      case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
         return V_008F14_IMG_DATA_FORMAT_X24_8_32;
      default:
         return ~0;
      }

   case UTIL_FORMAT_COLORSPACE_YUV:
      return ~0; /* TODO */

   default:
      break;
   }

   if (desc->layout == UTIL_FORMAT_LAYOUT_RGTC) {
      switch (desc->format) {
      case PIPE_FORMAT_RGTC1_SNORM:
      case PIPE_FORMAT_LATC1_SNORM:
      case PIPE_FORMAT_RGTC1_UNORM:
      case PIPE_FORMAT_LATC1_UNORM:
         return V_008F14_IMG_DATA_FORMAT_BC4;
      case PIPE_FORMAT_RGTC2_SNORM:
      case PIPE_FORMAT_LATC2_SNORM:
      case PIPE_FORMAT_RGTC2_UNORM:
      case PIPE_FORMAT_LATC2_UNORM:
         return V_008F14_IMG_DATA_FORMAT_BC5;
      default:
         return ~0;
      }
   }

   if (desc->layout == UTIL_FORMAT_LAYOUT_ETC) {
      switch (desc->format) {
      case PIPE_FORMAT_ETC1_RGB8:
      case PIPE_FORMAT_ETC2_RGB8:
      case PIPE_FORMAT_ETC2_SRGB8:
         return V_008F14_IMG_DATA_FORMAT_ETC2_RGB;
      case PIPE_FORMAT_ETC2_RGB8A1:
      case PIPE_FORMAT_ETC2_SRGB8A1:
         return V_008F14_IMG_DATA_FORMAT_ETC2_RGBA1;
      case PIPE_FORMAT_ETC2_RGBA8:
      case PIPE_FORMAT_ETC2_SRGBA8:
         return V_008F14_IMG_DATA_FORMAT_ETC2_RGBA;
      case PIPE_FORMAT_ETC2_R11_UNORM:
      case PIPE_FORMAT_ETC2_R11_SNORM:
         return V_008F14_IMG_DATA_FORMAT_ETC2_R;
      case PIPE_FORMAT_ETC2_RG11_UNORM:
      case PIPE_FORMAT_ETC2_RG11_SNORM:
         return V_008F14_IMG_DATA_FORMAT_ETC2_RG;
      default:
         break;
      }
   }

   if (desc->layout == UTIL_FORMAT_LAYOUT_BPTC) {
      switch (desc->format) {
      case PIPE_FORMAT_BPTC_RGBA_UNORM:
      case PIPE_FORMAT_BPTC_SRGBA:
         return V_008F14_IMG_DATA_FORMAT_BC7;
      case PIPE_FORMAT_BPTC_RGB_FLOAT:
      case PIPE_FORMAT_BPTC_RGB_UFLOAT:
         return V_008F14_IMG_DATA_FORMAT_BC6;
      default:
         return ~0;
      }
   }

   if (desc->layout == UTIL_FORMAT_LAYOUT_SUBSAMPLED) {
      switch (desc->format) {
      case PIPE_FORMAT_R8G8_B8G8_UNORM:
      case PIPE_FORMAT_G8R8_B8R8_UNORM:
      case PIPE_FORMAT_B8G8_R8G8_UNORM:
         return V_008F14_IMG_DATA_FORMAT_GB_GR;
      case PIPE_FORMAT_G8R8_G8B8_UNORM:
      case PIPE_FORMAT_R8G8_R8B8_UNORM:
      case PIPE_FORMAT_G8B8_G8R8_UNORM:
         return V_008F14_IMG_DATA_FORMAT_BG_RG;
      default:
         return ~0;
      }
   }

   if (desc->layout == UTIL_FORMAT_LAYOUT_S3TC) {
      switch (desc->format) {
      case PIPE_FORMAT_DXT1_RGB:
      case PIPE_FORMAT_DXT1_RGBA:
      case PIPE_FORMAT_DXT1_SRGB:
      case PIPE_FORMAT_DXT1_SRGBA:
         return V_008F14_IMG_DATA_FORMAT_BC1;
      case PIPE_FORMAT_DXT3_RGBA:
      case PIPE_FORMAT_DXT3_SRGBA:
         return V_008F14_IMG_DATA_FORMAT_BC2;
      case PIPE_FORMAT_DXT5_RGBA:
      case PIPE_FORMAT_DXT5_SRGBA:
         return V_008F14_IMG_DATA_FORMAT_BC3;
      default:
         return ~0;
      }
   }

   if (desc->format == PIPE_FORMAT_R9G9B9E5_FLOAT) {
      return V_008F14_IMG_DATA_FORMAT_5_9_9_9;
   } else if (desc->format == PIPE_FORMAT_R11G11B10_FLOAT) {
      return V_008F14_IMG_DATA_FORMAT_10_11_11;
   }

   /* hw cannot support mixed formats (except depth/stencil, since only
    * depth is read).*/
   if (desc->is_mixed && desc->colorspace != UTIL_FORMAT_COLORSPACE_ZS)
      return ~0;

   if (first_non_void < 0 || first_non_void > 3)
      return ~0;

   /* See whether the components are of the same size. */
   for (i = 1; i < desc->nr_channels; i++) {
      uniform = uniform && desc->channel[0].size == desc->channel[i].size;
   }

   /* Non-uniform formats. */
   if (!uniform) {
      switch (desc->nr_channels) {
      case 3:
         if (desc->channel[0].size == 5 && desc->channel[1].size == 6 &&
             desc->channel[2].size == 5) {
            return V_008F14_IMG_DATA_FORMAT_5_6_5;
         }
         return ~0;
      case 4:
         /* 5551 and 1555 UINT formats fail on Gfx8/Carrizo´. */
         if (info->family == CHIP_CARRIZO &&
             desc->channel[1].size == 5 &&
             desc->channel[2].size == 5 &&
             desc->channel[first_non_void].type == UTIL_FORMAT_TYPE_UNSIGNED &&
             desc->channel[first_non_void].pure_integer)
            return ~0;

         if (desc->channel[0].size == 5 && desc->channel[1].size == 5 &&
             desc->channel[2].size == 5 && desc->channel[3].size == 1) {
            return V_008F14_IMG_DATA_FORMAT_1_5_5_5;
         }
         if (desc->channel[0].size == 1 && desc->channel[1].size == 5 &&
             desc->channel[2].size == 5 && desc->channel[3].size == 5) {
            return V_008F14_IMG_DATA_FORMAT_5_5_5_1;
         }
         if (desc->channel[0].size == 10 && desc->channel[1].size == 10 &&
             desc->channel[2].size == 10 && desc->channel[3].size == 2) {
            return V_008F14_IMG_DATA_FORMAT_2_10_10_10;
         }
         return ~0;
      }
      return ~0;
   }

   /* uniform formats */
   switch (desc->channel[first_non_void].size) {
   case 4:
      switch (desc->nr_channels) {
      case 4:
         /* 4444 UINT formats fail on Gfx8/Carrizo´. */
         if (info->family == CHIP_CARRIZO &&
             desc->channel[first_non_void].type == UTIL_FORMAT_TYPE_UNSIGNED &&
             desc->channel[first_non_void].pure_integer)
            return ~0;

         return V_008F14_IMG_DATA_FORMAT_4_4_4_4;
      }
      break;
   case 8:
      switch (desc->nr_channels) {
      case 1:
         return V_008F14_IMG_DATA_FORMAT_8;
      case 2:
         return V_008F14_IMG_DATA_FORMAT_8_8;
      case 4:
         return V_008F14_IMG_DATA_FORMAT_8_8_8_8;
      }
      break;
   case 16:
      switch (desc->nr_channels) {
      case 1:
         return V_008F14_IMG_DATA_FORMAT_16;
      case 2:
         return V_008F14_IMG_DATA_FORMAT_16_16;
      case 4:
         return V_008F14_IMG_DATA_FORMAT_16_16_16_16;
      }
      break;
   case 32:
      switch (desc->nr_channels) {
      case 1:
         return V_008F14_IMG_DATA_FORMAT_32;
      case 2:
         return V_008F14_IMG_DATA_FORMAT_32_32;
      /* Not supported for render targets */
      case 3:
         return V_008F14_IMG_DATA_FORMAT_32_32_32;
      case 4:
         return V_008F14_IMG_DATA_FORMAT_32_32_32_32;
      }
      break;
   case 64:
      if (desc->channel[0].type != UTIL_FORMAT_TYPE_FLOAT && desc->nr_channels == 1)
         return V_008F14_IMG_DATA_FORMAT_32_32;
      break;
   }

   return ~0;
}

unsigned
ac_get_cb_format(enum amd_gfx_level gfx_level, enum pipe_format format)
{
   const struct util_format_description *desc = util_format_description(format);

#define HAS_SIZE(x, y, z, w)                                                                       \
   (desc->channel[0].size == (x) && desc->channel[1].size == (y) &&                                \
    desc->channel[2].size == (z) && desc->channel[3].size == (w))

   if (format == PIPE_FORMAT_R11G11B10_FLOAT) /* isn't plain */
      return V_028C70_COLOR_10_11_11;

   if (gfx_level >= GFX10_3 &&
       format == PIPE_FORMAT_R9G9B9E5_FLOAT) /* isn't plain */
      return V_028C70_COLOR_5_9_9_9;

   if (desc->layout != UTIL_FORMAT_LAYOUT_PLAIN)
      return V_028C70_COLOR_INVALID;

   /* hw cannot support mixed formats (except depth/stencil, since
    * stencil is not written to). */
   if (desc->is_mixed && desc->colorspace != UTIL_FORMAT_COLORSPACE_ZS)
      return V_028C70_COLOR_INVALID;

   int first_non_void = util_format_get_first_non_void_channel(format);

   /* Reject SCALED formats because we don't implement them for CB. */
   if (first_non_void >= 0 && first_non_void <= 3 &&
       (desc->channel[first_non_void].type == UTIL_FORMAT_TYPE_UNSIGNED ||
        desc->channel[first_non_void].type == UTIL_FORMAT_TYPE_SIGNED) &&
       !desc->channel[first_non_void].normalized &&
       !desc->channel[first_non_void].pure_integer)
      return V_028C70_COLOR_INVALID;

   switch (desc->nr_channels) {
   case 1:
      switch (desc->channel[0].size) {
      case 8:
         return V_028C70_COLOR_8;
      case 16:
         return V_028C70_COLOR_16;
      case 32:
         return V_028C70_COLOR_32;
      case 64:
         return V_028C70_COLOR_32_32;
      }
      break;
   case 2:
      if (desc->channel[0].size == desc->channel[1].size) {
         switch (desc->channel[0].size) {
         case 8:
            return V_028C70_COLOR_8_8;
         case 16:
            return V_028C70_COLOR_16_16;
         case 32:
            return V_028C70_COLOR_32_32;
         }
      } else if (HAS_SIZE(8, 24, 0, 0)) {
         return V_028C70_COLOR_24_8;
      } else if (HAS_SIZE(24, 8, 0, 0)) {
         return V_028C70_COLOR_8_24;
      }
      break;
   case 3:
      if (HAS_SIZE(5, 6, 5, 0)) {
         return V_028C70_COLOR_5_6_5;
      } else if (HAS_SIZE(32, 8, 24, 0)) {
         return V_028C70_COLOR_X24_8_32_FLOAT;
      }
      break;
   case 4:
      if (desc->channel[0].size == desc->channel[1].size &&
          desc->channel[0].size == desc->channel[2].size &&
          desc->channel[0].size == desc->channel[3].size) {
         switch (desc->channel[0].size) {
         case 4:
            return V_028C70_COLOR_4_4_4_4;
         case 8:
            return V_028C70_COLOR_8_8_8_8;
         case 16:
            return V_028C70_COLOR_16_16_16_16;
         case 32:
            return V_028C70_COLOR_32_32_32_32;
         }
      } else if (HAS_SIZE(5, 5, 5, 1)) {
         return V_028C70_COLOR_1_5_5_5;
      } else if (HAS_SIZE(1, 5, 5, 5)) {
         return V_028C70_COLOR_5_5_5_1;
      } else if (HAS_SIZE(10, 10, 10, 2)) {
         return V_028C70_COLOR_2_10_10_10;
      } else if (HAS_SIZE(2, 10, 10, 10)) {
         return V_028C70_COLOR_10_10_10_2;
      }
      break;
   }
   return V_028C70_COLOR_INVALID;
}

unsigned ac_get_cb_number_type(enum pipe_format format)
{
   const struct util_format_description *desc = util_format_description(format);
   int chan = util_format_get_first_non_void_channel(format);

   if (chan == -1 || desc->channel[chan].type == UTIL_FORMAT_TYPE_FLOAT) {
      return V_028C70_NUMBER_FLOAT;
   } else {
      if (desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) {
         return V_028C70_NUMBER_SRGB;
      } else if (desc->channel[chan].type == UTIL_FORMAT_TYPE_SIGNED) {
         return desc->channel[chan].pure_integer ? V_028C70_NUMBER_SINT : V_028C70_NUMBER_SNORM;
      } else if (desc->channel[chan].type == UTIL_FORMAT_TYPE_UNSIGNED) {
         return desc->channel[chan].pure_integer ? V_028C70_NUMBER_UINT : V_028C70_NUMBER_UNORM;
      } else {
         return V_028C70_NUMBER_UNORM;
      }
   }
}

unsigned
ac_translate_colorswap(enum amd_gfx_level gfx_level, enum pipe_format format, bool do_endian_swap)
{
   const struct util_format_description *desc = util_format_description(format);

#define HAS_SWIZZLE(chan, swz) (desc->swizzle[chan] == PIPE_SWIZZLE_##swz)

   if (format == PIPE_FORMAT_R11G11B10_FLOAT) /* isn't plain */
      return V_028C70_SWAP_STD;

   if (gfx_level >= GFX10_3 &&
       format == PIPE_FORMAT_R9G9B9E5_FLOAT) /* isn't plain */
      return V_028C70_SWAP_STD;

   if (desc->layout != UTIL_FORMAT_LAYOUT_PLAIN)
      return ~0U;

   switch (desc->nr_channels) {
   case 1:
      if (HAS_SWIZZLE(0, X))
         return V_028C70_SWAP_STD; /* X___ */
      else if (HAS_SWIZZLE(3, X))
         return V_028C70_SWAP_ALT_REV; /* ___X */
      break;
   case 2:
      if ((HAS_SWIZZLE(0, X) && HAS_SWIZZLE(1, Y)) || (HAS_SWIZZLE(0, X) && HAS_SWIZZLE(1, NONE)) ||
          (HAS_SWIZZLE(0, NONE) && HAS_SWIZZLE(1, Y)))
         return V_028C70_SWAP_STD; /* XY__ */
      else if ((HAS_SWIZZLE(0, Y) && HAS_SWIZZLE(1, X)) ||
               (HAS_SWIZZLE(0, Y) && HAS_SWIZZLE(1, NONE)) ||
               (HAS_SWIZZLE(0, NONE) && HAS_SWIZZLE(1, X)))
         /* YX__ */
         return (do_endian_swap ? V_028C70_SWAP_STD : V_028C70_SWAP_STD_REV);
      else if (HAS_SWIZZLE(0, X) && HAS_SWIZZLE(3, Y))
         return V_028C70_SWAP_ALT; /* X__Y */
      else if (HAS_SWIZZLE(0, Y) && HAS_SWIZZLE(3, X))
         return V_028C70_SWAP_ALT_REV; /* Y__X */
      break;
   case 3:
      if (HAS_SWIZZLE(0, X))
         return (do_endian_swap ? V_028C70_SWAP_STD_REV : V_028C70_SWAP_STD);
      else if (HAS_SWIZZLE(0, Z))
         return V_028C70_SWAP_STD_REV; /* ZYX */
      break;
   case 4:
      /* check the middle channels, the 1st and 4th channel can be NONE */
      if (HAS_SWIZZLE(1, Y) && HAS_SWIZZLE(2, Z)) {
         return V_028C70_SWAP_STD; /* XYZW */
      } else if (HAS_SWIZZLE(1, Z) && HAS_SWIZZLE(2, Y)) {
         return V_028C70_SWAP_STD_REV; /* WZYX */
      } else if (HAS_SWIZZLE(1, Y) && HAS_SWIZZLE(2, X)) {
         return V_028C70_SWAP_ALT; /* ZYXW */
      } else if (HAS_SWIZZLE(1, Z) && HAS_SWIZZLE(2, W)) {
         /* YZWX */
         if (desc->is_array)
            return V_028C70_SWAP_ALT_REV;
         else
            return (do_endian_swap ? V_028C70_SWAP_ALT : V_028C70_SWAP_ALT_REV);
      }
      break;
   }
   return ~0U;
}

bool
ac_is_colorbuffer_format_supported(enum amd_gfx_level gfx_level, enum pipe_format format)
{
   return ac_get_cb_format(gfx_level, format) != V_028C70_COLOR_INVALID &&
          ac_translate_colorswap(gfx_level, format, false) != ~0U;
}

uint32_t
ac_colorformat_endian_swap(uint32_t colorformat)
{
   if (UTIL_ARCH_BIG_ENDIAN) {
      switch (colorformat) {
      /* 8-bit buffers. */
      case V_028C70_COLOR_8:
         return V_028C70_ENDIAN_NONE;

      /* 16-bit buffers. */
      case V_028C70_COLOR_5_6_5:
      case V_028C70_COLOR_1_5_5_5:
      case V_028C70_COLOR_4_4_4_4:
      case V_028C70_COLOR_16:
      case V_028C70_COLOR_8_8:
         return V_028C70_ENDIAN_8IN16;

      /* 32-bit buffers. */
      case V_028C70_COLOR_8_8_8_8:
      case V_028C70_COLOR_2_10_10_10:
      case V_028C70_COLOR_10_10_10_2:
      case V_028C70_COLOR_8_24:
      case V_028C70_COLOR_24_8:
      case V_028C70_COLOR_16_16:
         return V_028C70_ENDIAN_8IN32;

      /* 64-bit buffers. */
      case V_028C70_COLOR_16_16_16_16:
         return V_028C70_ENDIAN_8IN16;

      case V_028C70_COLOR_32_32:
         return V_028C70_ENDIAN_8IN32;

      /* 128-bit buffers. */
      case V_028C70_COLOR_32_32_32_32:
         return V_028C70_ENDIAN_8IN32;
      default:
         return V_028C70_ENDIAN_NONE; /* Unsupported. */
      }
   } else {
      return V_028C70_ENDIAN_NONE;
   }
}

uint32_t
ac_translate_dbformat(enum pipe_format format)
{
   switch (format) {
   case PIPE_FORMAT_Z16_UNORM:
   case PIPE_FORMAT_Z16_UNORM_S8_UINT:
      return V_028040_Z_16;
   case PIPE_FORMAT_S8_UINT_Z24_UNORM:
   case PIPE_FORMAT_X8Z24_UNORM:
   case PIPE_FORMAT_Z24X8_UNORM:
   case PIPE_FORMAT_Z24_UNORM_S8_UINT:
      return V_028040_Z_24; /* not present on GFX12 */
   case PIPE_FORMAT_Z32_FLOAT:
   case PIPE_FORMAT_Z32_FLOAT_S8X24_UINT:
      return V_028040_Z_32_FLOAT;
   default:
      return V_028040_Z_INVALID;
   }
}

bool
ac_is_zs_format_supported(enum pipe_format format)
{
   return ac_translate_dbformat(format) != V_028040_Z_INVALID;
}

uint32_t
ac_border_color_swizzle(const struct util_format_description *desc)
{
   unsigned bc_swizzle = V_008F20_BC_SWIZZLE_XYZW;

   if (desc->format == PIPE_FORMAT_S8_UINT) {
      /* Swizzle of 8-bit stencil format is defined as _x__ but the hw expects XYZW. */
      assert(desc->swizzle[1] == PIPE_SWIZZLE_X);
      return bc_swizzle;
   }

   if (desc->swizzle[3] == PIPE_SWIZZLE_X) {
      /* For the pre-defined border color values (white, opaque
       * black, transparent black), the only thing that matters is
       * that the alpha channel winds up in the correct place
       * (because the RGB channels are all the same) so either of
       * these enumerations will work.
       */
      if (desc->swizzle[2] == PIPE_SWIZZLE_Y)
         bc_swizzle = V_008F20_BC_SWIZZLE_WZYX;
      else
         bc_swizzle = V_008F20_BC_SWIZZLE_WXYZ;
   } else if (desc->swizzle[0] == PIPE_SWIZZLE_X) {
      if (desc->swizzle[1] == PIPE_SWIZZLE_Y)
         bc_swizzle = V_008F20_BC_SWIZZLE_XYZW;
      else
         bc_swizzle = V_008F20_BC_SWIZZLE_XWYZ;
   } else if (desc->swizzle[1] == PIPE_SWIZZLE_X) {
      bc_swizzle = V_008F20_BC_SWIZZLE_YXWZ;
   } else if (desc->swizzle[2] == PIPE_SWIZZLE_X) {
      bc_swizzle = V_008F20_BC_SWIZZLE_ZYXW;
   }

   return bc_swizzle;
}

/** Linearize and convert luminance/intensity to red. */
enum pipe_format
ac_simplify_cb_format(enum pipe_format format)
{
   format = util_format_linear(format);
   format = util_format_luminance_to_red(format);
   return util_format_intensity_to_red(format);
}

bool
ac_alpha_is_on_msb(const struct radeon_info *info, enum pipe_format format)
{
   if (info->gfx_level >= GFX11)
      return false;

   format = ac_simplify_cb_format(format);
   const struct util_format_description *desc = util_format_description(format);
   unsigned comp_swap = ac_translate_colorswap(info->gfx_level, format, false);

   /* The following code matches the hw behavior. */
   if (desc->nr_channels == 1) {
      return (comp_swap == V_028C70_SWAP_ALT_REV) != (info->family == CHIP_RAVEN2 ||
                                                      info->family == CHIP_RENOIR);
   }

   return comp_swap != V_028C70_SWAP_STD_REV && comp_swap != V_028C70_SWAP_ALT_REV;
}

/* GFX6-8:
 * - no integer format support
 * - no depth format support (depth formats without shadow samplers are supported,
 *   but that's not enough)
 * - only single-channel formats are supported
 * - limitations of early chips (GFX6 only): no R9G9B9E5 support
 *
 * GFX9+:
 * - all formats are supported
 */
bool
ac_is_reduction_mode_supported(const struct radeon_info *info, enum pipe_format format,
                               bool shadow_samplers)
{
   const struct util_format_description *desc = util_format_description(format);

   if (info->gfx_level <= GFX8) {
      /* old HW limitations */
      if (info->gfx_level == GFX6 && format == PIPE_FORMAT_R9G9B9E5_FLOAT)
         return false;

      /* reject if more than one channel */
      if (desc->nr_channels > 1)
         return false;

      /* no integer or depth format support */
      if (util_format_is_pure_integer(format) ||
          (shadow_samplers && util_format_has_depth(desc)))
         return false;
   }

   return true;
}

void
ac_set_sx_downconvert_state_for_mrt(enum amd_gfx_level gfx_level, bool is_null, unsigned cb_color_info,
                                    unsigned cb_color_attrib, unsigned spi_shader_col_format,
                                    unsigned cb_target_mask, unsigned mrt_index,
                                    uint32_t *sx_ps_downconvert, uint32_t *sx_blend_opt_epsilon,
                                    uint32_t *sx_blend_opt_control, uint32_t *fix_cb_target_mask)
{
   if (is_null) {
      /* If the color buffer is not set, the driver sets 32_R as the SPI color format, because
       * the HW doesn't allow holes between color outputs, so also set this to enable RB+.
       */
      *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_32_R << (mrt_index * 4);
      return;
   }

   unsigned cb_format = gfx_level >= GFX11 ? G_028C70_FORMAT_GFX11(cb_color_info):
                                             G_028C70_FORMAT_GFX6(cb_color_info);
   unsigned cb_swap = G_028C70_COMP_SWAP(cb_color_info);
   unsigned spi_format = (spi_shader_col_format >> (mrt_index * 4)) & 0xf;
   unsigned colormask = (cb_target_mask >> (mrt_index * 4)) & 0xf;

   /* Set if RGB and A are present. */
   bool has_alpha = !(gfx_level >= GFX11 ? G_028C74_FORCE_DST_ALPHA_1_GFX11(cb_color_attrib):
                                           G_028C74_FORCE_DST_ALPHA_1_GFX6(cb_color_attrib));
   bool has_rgb;

   if (cb_format == V_028C70_COLOR_8 || cb_format == V_028C70_COLOR_16 || cb_format == V_028C70_COLOR_32)
      has_rgb = !has_alpha;
   else
      has_rgb = true;

   /* Check the colormask and export format. */
   if (!(colormask & PIPE_MASK_RGB))
      has_rgb = false;
   if (!(colormask & PIPE_MASK_A))
      has_alpha = false;

   if (spi_format == V_028714_SPI_SHADER_ZERO) {
      has_rgb = false;
      has_alpha = false;
   }

   /* Disable value checking for disabled channels during SX blend optimizations (determining
    * BLEND_BYPASS and DISCARD). These bits modify how BLEND_BYPASS and DISCARD are determined.
    *
    * When both DISABLE bits are set, the behavior is BLEND_BYPASS. When colormask is
    * also 0, the SX export format must still be set, and then it behaves like DISCARD.
    *
    * When any DISABLE bit is not set, those components must succeed the corresponding MRTi blend
    * opt test to get BLEND_BYPASS or DISCARD.
    */
   if (!has_rgb)
      *sx_blend_opt_control |= S_02875C_MRT0_COLOR_OPT_DISABLE(1) << (mrt_index * 4);
   if (!has_alpha)
      *sx_blend_opt_control |= S_02875C_MRT0_ALPHA_OPT_DISABLE(1) << (mrt_index * 4);

   /* Enable export down-conversion for 32bpp and smaller formats, also called RB+, required
    * for optimal pixel throughput. If this function doesn't set anything (the value stays at
    * SX_RT_EXPORT_NO_CONVERSION=0), the pixel throughput will be the same as 64bpp.
    */
   bool export_format_is_fp16 = spi_format == V_028714_SPI_SHADER_FP16_ABGR;
   bool export_format_is_int16 = spi_format == V_028714_SPI_SHADER_UINT16_ABGR ||
                                 spi_format == V_028714_SPI_SHADER_SINT16_ABGR;
   bool export_format_is_norm16 = spi_format == V_028714_SPI_SHADER_UNORM16_ABGR ||
                                  spi_format == V_028714_SPI_SHADER_SNORM16_ABGR;

   switch (cb_format) {
   case V_028C70_COLOR_8:
   case V_028C70_COLOR_8_8:
   case V_028C70_COLOR_8_8_8_8:
      if (export_format_is_fp16 || export_format_is_int16) {
         *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_8_8_8_8 << (mrt_index * 4);
         if (G_028C70_NUMBER_TYPE(cb_color_info) != V_028C70_NUMBER_SRGB)
            *sx_blend_opt_epsilon |= V_028758_8BIT_FORMAT_0_5 << (mrt_index * 4);
      }
      break;

   case V_028C70_COLOR_16:
   case V_028C70_COLOR_16_16:
      /* For 1-channel formats, use the superset thereof. */
      if (export_format_is_fp16 || export_format_is_int16 || export_format_is_norm16) {
         if (cb_swap == V_028C70_SWAP_STD || cb_swap == V_028C70_SWAP_STD_REV)
            *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_16_16_GR << (mrt_index * 4);
         else
            *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_16_16_AR << (mrt_index * 4);
      }
      break;

   case V_028C70_COLOR_32:
      if (cb_swap == V_028C70_SWAP_STD && spi_format == V_028714_SPI_SHADER_32_R)
         *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_32_R << (mrt_index * 4);
      else if (cb_swap == V_028C70_SWAP_ALT_REV && spi_format == V_028714_SPI_SHADER_32_AR)
         *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_32_A << (mrt_index * 4);
      break;

   case V_028C70_COLOR_5_6_5:
      if (export_format_is_fp16) {
         *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_5_6_5 << (mrt_index * 4);
         *sx_blend_opt_epsilon |= V_028758_6BIT_FORMAT_0_5 << (mrt_index * 4);
      }
      break;

   case V_028C70_COLOR_1_5_5_5:
   case V_028C70_COLOR_5_5_5_1:
      if (export_format_is_fp16) {
         *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_1_5_5_5 << (mrt_index * 4);
         *sx_blend_opt_epsilon |= V_028758_5BIT_FORMAT_0_5 << (mrt_index * 4);
      }
      break;

   case V_028C70_COLOR_4_4_4_4:
      if (export_format_is_fp16) {
         *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_4_4_4_4 << (mrt_index * 4);
         *sx_blend_opt_epsilon |= V_028758_4BIT_FORMAT_0_5 << (mrt_index * 4);
      }
      break;

   case V_028C70_COLOR_10_11_11:
   case V_028C70_COLOR_11_11_10:
      if (export_format_is_fp16)
         *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_10_11_11 << (mrt_index * 4);
      break;

   case V_028C70_COLOR_2_10_10_10:
   case V_028C70_COLOR_10_10_10_2:
      if (export_format_is_fp16 || export_format_is_int16) {
         *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_2_10_10_10 << (mrt_index * 4);
         *sx_blend_opt_epsilon |= V_028758_10BIT_FORMAT_0_5 << (mrt_index * 4);
      }
      break;

   case V_028C70_COLOR_5_9_9_9:
      /* This only executes on GFX10.3+. */
      assert(gfx_level >= GFX10_3);

      if (export_format_is_fp16) {
         if (gfx_level >= GFX12) {
            *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_9_9_9_E5 << (mrt_index * 4);
         } else {
            /* GFX10.3-11 have a bug where R9G9B9E5 is broken with RB+ when the color mask is neither
             * full nor empty.
             *
             * If A is missing in the color mask, add it. If it's the only bit set, remove it.
             */
            if (fix_cb_target_mask) {
               if (colormask == PIPE_MASK_RGB) {
                  colormask |= PIPE_MASK_A;
                  *fix_cb_target_mask |= PIPE_MASK_A << (mrt_index * 4);
               } else if (colormask == PIPE_MASK_A) {
                  colormask = 0;
                  *fix_cb_target_mask &= ~(PIPE_MASK_A << (mrt_index * 4));
               }
            }

            /* Don't enable RB+ if the color mask is not full or empty, which is done by not
             * setting SX_PS_DOWNCONVERT for that MRT. Even if the color mask is 0, we should
             * still set a non-zero export format to make sure the pixels are discarded quickly.
             */
            if (colormask == PIPE_MASK_RGBA || colormask == 0)
               *sx_ps_downconvert |= V_028754_SX_RT_EXPORT_9_9_9_E5 << (mrt_index * 4);
         }
      }
      break;
   }
}
