mirror of https://gitlab.freedesktop.org/mesa/mesa
178 lines
5.5 KiB
C
178 lines
5.5 KiB
C
/*
|
|
* Copyright 2022 Alyssa Rosenzweig
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "util/format/format_utils.h"
|
|
#include "util/format/u_format.h"
|
|
#include "util/half_float.h"
|
|
#include "agx_formats.h"
|
|
#include "agx_pack.h"
|
|
|
|
/*
|
|
* AGX allows the sampler descriptor to specify a custom border colour. The
|
|
* packing depends on the texture format (i.e. no
|
|
* customBorderColorWithoutFormat).
|
|
*
|
|
* Each channel is packed separately into 32-bit words. Pure integers are stored
|
|
* as-is. Pure floats are extended to 16-bit/32-bit as appropriate. Normalized
|
|
* formats are encoded as usual, except sRGB gets 4 extra bits.
|
|
*
|
|
* The texture descriptor swizzle is applied to the border colour. That swizzle
|
|
* includes the format swizzle. In effect, we want to encode the border colour
|
|
* like it would be encoded in memory, and then the swizzles work out
|
|
* for Vulkan.
|
|
*/
|
|
|
|
struct channel {
|
|
enum util_format_type type;
|
|
bool normalized;
|
|
unsigned size;
|
|
};
|
|
|
|
static struct channel
|
|
get_channel_info(enum pipe_format format, unsigned channel)
|
|
{
|
|
/* Compressed formats may have packing with no PIPE equivalent, handle
|
|
* specially.
|
|
*/
|
|
switch (format) {
|
|
case PIPE_FORMAT_ETC2_R11_UNORM:
|
|
case PIPE_FORMAT_ETC2_RG11_UNORM:
|
|
return (struct channel){UTIL_FORMAT_TYPE_UNSIGNED, true, 11};
|
|
|
|
case PIPE_FORMAT_ETC2_R11_SNORM:
|
|
case PIPE_FORMAT_ETC2_RG11_SNORM:
|
|
return (struct channel){UTIL_FORMAT_TYPE_SIGNED, true, 11};
|
|
|
|
case PIPE_FORMAT_RGTC1_UNORM:
|
|
case PIPE_FORMAT_RGTC2_UNORM:
|
|
return (struct channel){UTIL_FORMAT_TYPE_UNSIGNED, true, 14};
|
|
case PIPE_FORMAT_RGTC1_SNORM:
|
|
case PIPE_FORMAT_RGTC2_SNORM:
|
|
return (struct channel){UTIL_FORMAT_TYPE_SIGNED, true, 14};
|
|
|
|
case PIPE_FORMAT_ETC1_RGB8:
|
|
case PIPE_FORMAT_ETC2_RGB8:
|
|
case PIPE_FORMAT_ETC2_RGBA8:
|
|
case PIPE_FORMAT_ETC2_RGB8A1:
|
|
case PIPE_FORMAT_BPTC_RGBA_UNORM:
|
|
case PIPE_FORMAT_DXT1_RGB:
|
|
case PIPE_FORMAT_DXT1_RGBA:
|
|
case PIPE_FORMAT_DXT3_RGBA:
|
|
case PIPE_FORMAT_DXT5_RGBA:
|
|
return (struct channel){UTIL_FORMAT_TYPE_UNSIGNED, true, 8};
|
|
|
|
case PIPE_FORMAT_ETC2_SRGB8:
|
|
case PIPE_FORMAT_ETC2_SRGBA8:
|
|
case PIPE_FORMAT_ETC2_SRGB8A1:
|
|
case PIPE_FORMAT_BPTC_SRGBA:
|
|
case PIPE_FORMAT_DXT1_SRGB:
|
|
case PIPE_FORMAT_DXT1_SRGBA:
|
|
case PIPE_FORMAT_DXT3_SRGBA:
|
|
case PIPE_FORMAT_DXT5_SRGBA:
|
|
return (struct channel){
|
|
UTIL_FORMAT_TYPE_UNSIGNED,
|
|
true,
|
|
channel == 3 ? 8 : 12,
|
|
};
|
|
|
|
case PIPE_FORMAT_BPTC_RGB_FLOAT:
|
|
case PIPE_FORMAT_BPTC_RGB_UFLOAT:
|
|
return (struct channel){UTIL_FORMAT_TYPE_FLOAT, false, 16};
|
|
|
|
default:
|
|
assert(
|
|
!util_format_is_compressed(format) &&
|
|
"Other compressed formats must be special cased for border colours."
|
|
"Add more cases if we have a use case");
|
|
|
|
break;
|
|
}
|
|
|
|
const struct util_format_description *desc = util_format_description(format);
|
|
struct util_format_channel_description chan_desc = desc->channel[channel];
|
|
bool srgb = (desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) &&
|
|
(desc->swizzle[channel] <= PIPE_SWIZZLE_Z);
|
|
|
|
if (chan_desc.type == UTIL_FORMAT_TYPE_UNSIGNED ||
|
|
chan_desc.type == UTIL_FORMAT_TYPE_SIGNED) {
|
|
|
|
assert((chan_desc.normalized ^ chan_desc.pure_integer) &&
|
|
"no SCALED formats supported for texturing");
|
|
}
|
|
|
|
if (srgb && chan_desc.type != UTIL_FORMAT_TYPE_VOID) {
|
|
assert(chan_desc.normalized && chan_desc.size == 8 &&
|
|
chan_desc.type == UTIL_FORMAT_TYPE_UNSIGNED &&
|
|
"only 8-bit unorm supported with sRGB");
|
|
}
|
|
|
|
return (struct channel){
|
|
.type = chan_desc.type,
|
|
.normalized = chan_desc.normalized,
|
|
.size = srgb ? 12 : chan_desc.size,
|
|
};
|
|
}
|
|
|
|
static uint32_t
|
|
pack_channel(uint32_t value, enum pipe_format format, unsigned channel)
|
|
{
|
|
struct channel chan = get_channel_info(format, channel);
|
|
|
|
switch (chan.type) {
|
|
case UTIL_FORMAT_TYPE_VOID:
|
|
return 0;
|
|
|
|
case UTIL_FORMAT_TYPE_UNSIGNED:
|
|
if (chan.normalized)
|
|
return _mesa_float_to_unorm(uif(value), chan.size);
|
|
else
|
|
return _mesa_unsigned_to_unsigned(value, chan.size);
|
|
|
|
case UTIL_FORMAT_TYPE_SIGNED:
|
|
if (chan.normalized)
|
|
return _mesa_float_to_snorm(uif(value), chan.size);
|
|
else
|
|
return _mesa_signed_to_signed(value, chan.size);
|
|
|
|
case UTIL_FORMAT_TYPE_FLOAT:
|
|
assert(chan.size == 32 || chan.size <= 16);
|
|
return chan.size == 32 ? value : _mesa_float_to_half(uif(value));
|
|
|
|
case UTIL_FORMAT_TYPE_FIXED:
|
|
unreachable("no FIXED textures");
|
|
}
|
|
|
|
unreachable("invalid format type");
|
|
}
|
|
|
|
void
|
|
agx_pack_border(struct agx_border_packed *out, const uint32_t in[4],
|
|
enum pipe_format format)
|
|
{
|
|
assert(format != PIPE_FORMAT_NONE);
|
|
|
|
const struct util_format_description *desc = util_format_description(format);
|
|
uint8_t channel_map[4] = {0};
|
|
|
|
/* Determine the in-memory order of the format. That is the inverse of the
|
|
* format swizzle. If a component is replicated, we use the first component,
|
|
* by looping backwards and overwriting.
|
|
*/
|
|
for (int i = 3; i >= 0; --i) {
|
|
static_assert(PIPE_SWIZZLE_X == 0, "known ordering");
|
|
static_assert(PIPE_SWIZZLE_W == 3, "known ordering");
|
|
|
|
if (desc->swizzle[i] <= PIPE_SWIZZLE_W)
|
|
channel_map[i] = desc->swizzle[i];
|
|
}
|
|
|
|
agx_pack(out, BORDER, cfg) {
|
|
cfg.channel_0 = pack_channel(in[channel_map[0]], format, 0);
|
|
cfg.channel_1 = pack_channel(in[channel_map[1]], format, 1);
|
|
cfg.channel_2 = pack_channel(in[channel_map[2]], format, 2);
|
|
cfg.channel_3 = pack_channel(in[channel_map[3]], format, 3);
|
|
}
|
|
}
|