1 /*
2 * Copyright 2022 Alyssa Rosenzweig
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "util/format/format_utils.h"
7 #include "util/format/u_format.h"
8 #include "util/half_float.h"
9 #include "agx_helpers.h"
10 #include "agx_pack.h"
11
12 /*
13 * AGX allows the sampler descriptor to specify a custom border colour. The
14 * packing depends on the texture format (i.e. no
15 * customBorderColorWithoutFormat).
16 *
17 * Each channel is packed separately into 32-bit words. Pure integers are stored
18 * as-is. Pure floats are extended to 16-bit/32-bit as appropriate. Normalized
19 * formats are encoded as usual, except sRGB gets 4 extra bits.
20 *
21 * The texture descriptor swizzle is applied to the border colour. That swizzle
22 * includes the format swizzle. In effect, we want to encode the border colour
23 * like it would be encoded in memory, and then the swizzles work out
24 * for Vulkan.
25 */
26
27 struct channel {
28 enum util_format_type type;
29 bool normalized;
30 unsigned size;
31 };
32
33 static struct channel
get_channel_info(enum pipe_format format,unsigned channel)34 get_channel_info(enum pipe_format format, unsigned channel)
35 {
36 /* Compressed formats may have packing with no PIPE equivalent, handle
37 * specially.
38 */
39 switch (format) {
40 case PIPE_FORMAT_ETC2_R11_UNORM:
41 case PIPE_FORMAT_ETC2_RG11_UNORM:
42 return (struct channel){UTIL_FORMAT_TYPE_UNSIGNED, true, 11};
43
44 case PIPE_FORMAT_ETC2_R11_SNORM:
45 case PIPE_FORMAT_ETC2_RG11_SNORM:
46 return (struct channel){UTIL_FORMAT_TYPE_SIGNED, true, 11};
47
48 case PIPE_FORMAT_RGTC1_UNORM:
49 case PIPE_FORMAT_RGTC2_UNORM:
50 return (struct channel){UTIL_FORMAT_TYPE_UNSIGNED, true, 14};
51 case PIPE_FORMAT_RGTC1_SNORM:
52 case PIPE_FORMAT_RGTC2_SNORM:
53 return (struct channel){UTIL_FORMAT_TYPE_SIGNED, true, 14};
54
55 case PIPE_FORMAT_ETC1_RGB8:
56 case PIPE_FORMAT_ETC2_RGB8:
57 case PIPE_FORMAT_ETC2_RGBA8:
58 case PIPE_FORMAT_ETC2_RGB8A1:
59 case PIPE_FORMAT_BPTC_RGBA_UNORM:
60 case PIPE_FORMAT_DXT1_RGB:
61 case PIPE_FORMAT_DXT1_RGBA:
62 case PIPE_FORMAT_DXT3_RGBA:
63 case PIPE_FORMAT_DXT5_RGBA:
64 return (struct channel){UTIL_FORMAT_TYPE_UNSIGNED, true, 8};
65
66 case PIPE_FORMAT_ETC2_SRGB8:
67 case PIPE_FORMAT_ETC2_SRGBA8:
68 case PIPE_FORMAT_ETC2_SRGB8A1:
69 case PIPE_FORMAT_BPTC_SRGBA:
70 case PIPE_FORMAT_DXT1_SRGB:
71 case PIPE_FORMAT_DXT1_SRGBA:
72 case PIPE_FORMAT_DXT3_SRGBA:
73 case PIPE_FORMAT_DXT5_SRGBA:
74 return (struct channel){
75 UTIL_FORMAT_TYPE_UNSIGNED,
76 true,
77 channel == 3 ? 8 : 12,
78 };
79
80 case PIPE_FORMAT_BPTC_RGB_FLOAT:
81 case PIPE_FORMAT_BPTC_RGB_UFLOAT:
82 return (struct channel){UTIL_FORMAT_TYPE_FLOAT, false, 16};
83
84 default:
85 assert(
86 !util_format_is_compressed(format) &&
87 "Other compressed formats must be special cased for border colours."
88 "Add more cases if we have a use case");
89
90 break;
91 }
92
93 const struct util_format_description *desc = util_format_description(format);
94 struct util_format_channel_description chan_desc = desc->channel[channel];
95 bool srgb = (desc->colorspace == UTIL_FORMAT_COLORSPACE_SRGB) &&
96 (desc->swizzle[channel] <= PIPE_SWIZZLE_Z);
97
98 if (chan_desc.type == UTIL_FORMAT_TYPE_UNSIGNED ||
99 chan_desc.type == UTIL_FORMAT_TYPE_SIGNED) {
100
101 assert((chan_desc.normalized ^ chan_desc.pure_integer) &&
102 "no SCALED formats supported for texturing");
103 }
104
105 if (srgb && chan_desc.type != UTIL_FORMAT_TYPE_VOID) {
106 assert(chan_desc.normalized && chan_desc.size == 8 &&
107 chan_desc.type == UTIL_FORMAT_TYPE_UNSIGNED &&
108 "only 8-bit unorm supported with sRGB");
109 }
110
111 return (struct channel){
112 .type = chan_desc.type,
113 .normalized = chan_desc.normalized,
114 .size = srgb ? 12 : chan_desc.size,
115 };
116 }
117
118 static uint32_t
pack_channel(uint32_t value,enum pipe_format format,unsigned channel)119 pack_channel(uint32_t value, enum pipe_format format, unsigned channel)
120 {
121 struct channel chan = get_channel_info(format, channel);
122
123 switch (chan.type) {
124 case UTIL_FORMAT_TYPE_VOID:
125 return 0;
126
127 case UTIL_FORMAT_TYPE_UNSIGNED:
128 if (chan.normalized)
129 return _mesa_float_to_unorm(uif(value), chan.size);
130 else
131 return _mesa_unsigned_to_unsigned(value, chan.size);
132
133 case UTIL_FORMAT_TYPE_SIGNED:
134 if (chan.normalized)
135 return _mesa_float_to_snorm(uif(value), chan.size);
136 else
137 return _mesa_signed_to_signed(value, chan.size);
138
139 case UTIL_FORMAT_TYPE_FLOAT:
140 assert(chan.size == 32 || chan.size <= 16);
141 return chan.size == 32 ? value : _mesa_float_to_half(uif(value));
142
143 case UTIL_FORMAT_TYPE_FIXED:
144 unreachable("no FIXED textures");
145 }
146
147 unreachable("invalid format type");
148 }
149
150 void
agx_pack_border(struct agx_border_packed * out,const uint32_t in[4],enum pipe_format format)151 agx_pack_border(struct agx_border_packed *out, const uint32_t in[4],
152 enum pipe_format format)
153 {
154 assert(format != PIPE_FORMAT_NONE);
155
156 const struct util_format_description *desc = util_format_description(format);
157 uint8_t channel_map[4] = {0};
158
159 /* Determine the in-memory order of the format. That is the inverse of the
160 * format swizzle. If a component is replicated, we use the first component,
161 * by looping backwards and overwriting.
162 */
163 for (int i = 3; i >= 0; --i) {
164 static_assert(PIPE_SWIZZLE_X == 0, "known ordering");
165 static_assert(PIPE_SWIZZLE_W == 3, "known ordering");
166
167 if (desc->swizzle[i] <= PIPE_SWIZZLE_W)
168 channel_map[i] = desc->swizzle[i];
169 }
170
171 agx_pack(out, BORDER, cfg) {
172 cfg.channel_0 = pack_channel(in[channel_map[0]], format, 0);
173 cfg.channel_1 = pack_channel(in[channel_map[1]], format, 1);
174 cfg.channel_2 = pack_channel(in[channel_map[2]], format, 2);
175 cfg.channel_3 = pack_channel(in[channel_map[3]], format, 3);
176 }
177 }
178