1 /*
2 * Copyright 2024 Valve Corporation
3 * Copyright 2024 Alyssa Rosenzweig
4 * Copyright 2022-2023 Collabora Ltd. and Red Hat Inc.
5 * SPDX-License-Identifier: MIT
6 */
7 #include "hk_sampler.h"
8
9 #include "hk_device.h"
10 #include "hk_entrypoints.h"
11 #include "hk_instance.h"
12 #include "hk_physical_device.h"
13
14 #include "vk_format.h"
15 #include "vk_sampler.h"
16
17 #include "asahi/genxml/agx_pack.h"
18
19 static inline uint32_t
translate_address_mode(VkSamplerAddressMode addr_mode)20 translate_address_mode(VkSamplerAddressMode addr_mode)
21 {
22 #define MODE(VK, AGX_) [VK_SAMPLER_ADDRESS_MODE_##VK] = AGX_WRAP_##AGX_
23 static const uint8_t translate[] = {
24 MODE(REPEAT, REPEAT),
25 MODE(MIRRORED_REPEAT, MIRRORED_REPEAT),
26 MODE(CLAMP_TO_EDGE, CLAMP_TO_EDGE),
27 MODE(CLAMP_TO_BORDER, CLAMP_TO_BORDER),
28 MODE(MIRROR_CLAMP_TO_EDGE, MIRRORED_CLAMP_TO_EDGE),
29 };
30 #undef MODE
31
32 assert(addr_mode < ARRAY_SIZE(translate));
33 return translate[addr_mode];
34 }
35
36 static uint32_t
translate_texsamp_compare_op(VkCompareOp op)37 translate_texsamp_compare_op(VkCompareOp op)
38 {
39 #define OP(VK, AGX_) [VK_COMPARE_OP_##VK] = AGX_COMPARE_FUNC_##AGX_
40 static const uint8_t translate[] = {
41 OP(NEVER, NEVER),
42 OP(LESS, LESS),
43 OP(EQUAL, EQUAL),
44 OP(LESS_OR_EQUAL, LEQUAL),
45 OP(GREATER, GREATER),
46 OP(NOT_EQUAL, NOT_EQUAL),
47 OP(GREATER_OR_EQUAL, GEQUAL),
48 OP(ALWAYS, ALWAYS),
49 };
50 #undef OP
51
52 assert(op < ARRAY_SIZE(translate));
53 return translate[op];
54 }
55
56 static enum agx_filter
translate_filter(VkFilter filter)57 translate_filter(VkFilter filter)
58 {
59 static_assert((enum agx_filter)VK_FILTER_NEAREST == AGX_FILTER_NEAREST);
60 static_assert((enum agx_filter)VK_FILTER_LINEAR == AGX_FILTER_LINEAR);
61
62 return (enum agx_filter)filter;
63 }
64
65 static enum agx_mip_filter
translate_mipfilter(VkSamplerMipmapMode mode)66 translate_mipfilter(VkSamplerMipmapMode mode)
67 {
68 switch (mode) {
69 case VK_SAMPLER_MIPMAP_MODE_NEAREST:
70 return AGX_MIP_FILTER_NEAREST;
71
72 case VK_SAMPLER_MIPMAP_MODE_LINEAR:
73 return AGX_MIP_FILTER_LINEAR;
74
75 default:
76 unreachable("Invalid filter");
77 }
78 }
79
80 static bool
uses_border(const VkSamplerCreateInfo * info)81 uses_border(const VkSamplerCreateInfo *info)
82 {
83 return info->addressModeU == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
84 info->addressModeV == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER ||
85 info->addressModeW == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
86 }
87
88 static enum agx_border_colour
is_border_color_custom(VkBorderColor color,bool workaround_rgba4)89 is_border_color_custom(VkBorderColor color, bool workaround_rgba4)
90 {
91 switch (color) {
92 case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
93 /* We may need to workaround RGBA4 UNORM issues with opaque black. This
94 * only affects float opaque black, there are no pure integer RGBA4
95 * formats to worry about.
96 */
97 return workaround_rgba4;
98
99 case VK_BORDER_COLOR_INT_CUSTOM_EXT:
100 case VK_BORDER_COLOR_FLOAT_CUSTOM_EXT:
101 return true;
102
103 default:
104 return false;
105 }
106 }
107
108 /* Translate an American VkBorderColor into a Canadian agx_border_colour */
109 static enum agx_border_colour
translate_border_color(VkBorderColor color,bool custom_to_1,bool workaround_rgba4)110 translate_border_color(VkBorderColor color, bool custom_to_1,
111 bool workaround_rgba4)
112 {
113 if (is_border_color_custom(color, workaround_rgba4)) {
114 return custom_to_1 ? AGX_BORDER_COLOUR_OPAQUE_WHITE
115 : AGX_BORDER_COLOUR_TRANSPARENT_BLACK;
116 }
117
118 switch (color) {
119 case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:
120 case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK:
121 return AGX_BORDER_COLOUR_TRANSPARENT_BLACK;
122
123 case VK_BORDER_COLOR_INT_OPAQUE_BLACK:
124 case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
125 return AGX_BORDER_COLOUR_OPAQUE_BLACK;
126
127 case VK_BORDER_COLOR_INT_OPAQUE_WHITE:
128 case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:
129 return AGX_BORDER_COLOUR_OPAQUE_WHITE;
130
131 default:
132 unreachable("invalid");
133 }
134 }
135
136 static void
pack_sampler(const struct hk_physical_device * pdev,const struct VkSamplerCreateInfo * info,bool custom_to_1,bool workaround_rgba4,struct agx_sampler_packed * out)137 pack_sampler(const struct hk_physical_device *pdev,
138 const struct VkSamplerCreateInfo *info, bool custom_to_1,
139 bool workaround_rgba4, struct agx_sampler_packed *out)
140 {
141 agx_pack(out, SAMPLER, cfg) {
142 cfg.minimum_lod = info->minLod;
143 cfg.maximum_lod = info->maxLod;
144 cfg.magnify = translate_filter(info->magFilter);
145 cfg.minify = translate_filter(info->minFilter);
146 cfg.mip_filter = translate_mipfilter(info->mipmapMode);
147 cfg.wrap_s = translate_address_mode(info->addressModeU);
148 cfg.wrap_t = translate_address_mode(info->addressModeV);
149 cfg.wrap_r = translate_address_mode(info->addressModeW);
150 cfg.pixel_coordinates = info->unnormalizedCoordinates;
151
152 cfg.seamful_cube_maps =
153 info->flags & VK_SAMPLER_CREATE_NON_SEAMLESS_CUBE_MAP_BIT_EXT;
154
155 if (info->compareEnable) {
156 cfg.compare_func = translate_texsamp_compare_op(info->compareOp);
157 cfg.compare_enable = true;
158 }
159
160 if (info->anisotropyEnable) {
161 cfg.maximum_anisotropy =
162 util_next_power_of_two(MAX2(info->maxAnisotropy, 1));
163 } else {
164 cfg.maximum_anisotropy = 1;
165 }
166
167 if (uses_border(info)) {
168 cfg.border_colour = translate_border_color(
169 info->borderColor, custom_to_1, workaround_rgba4);
170 }
171 }
172 }
173
174 VKAPI_ATTR VkResult VKAPI_CALL
hk_CreateSampler(VkDevice device,const VkSamplerCreateInfo * info,const VkAllocationCallbacks * pAllocator,VkSampler * pSampler)175 hk_CreateSampler(VkDevice device,
176 const VkSamplerCreateInfo *info /* pCreateInfo */,
177 const VkAllocationCallbacks *pAllocator, VkSampler *pSampler)
178 {
179 VK_FROM_HANDLE(hk_device, dev, device);
180 struct hk_physical_device *pdev = hk_device_physical(dev);
181 struct hk_instance *instance = (struct hk_instance *)pdev->vk.instance;
182 struct hk_sampler *sampler;
183 VkResult result;
184
185 sampler = vk_sampler_create(&dev->vk, info, pAllocator, sizeof(*sampler));
186 if (!sampler)
187 return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
188
189 bool workaround_rgba4 = instance->workaround_rgba4;
190 bool custom_border =
191 uses_border(info) &&
192 is_border_color_custom(info->borderColor, workaround_rgba4);
193
194 /* Sanity check the noborder setting. There's no way to recover from it being
195 * wrong but at least we can make noise to lint for errors in the driconf.
196 */
197 if (HK_PERF(dev, NOBORDER) && custom_border) {
198 fprintf(stderr, "custom border colour used, but emulation is disabled\n");
199 fprintf(stderr, "border %u\n", info->borderColor);
200 fprintf(stderr, "rgba4 workaround: %u\n", workaround_rgba4);
201 fprintf(stderr, "unnorm %X\n", info->unnormalizedCoordinates);
202 fprintf(stderr, "compare %X\n", info->compareEnable);
203 fprintf(stderr, "value: %X, %X, %X, %X\n",
204 sampler->vk.border_color_value.uint32[0],
205 sampler->vk.border_color_value.uint32[1],
206 sampler->vk.border_color_value.uint32[2],
207 sampler->vk.border_color_value.uint32[3]);
208 fprintf(stderr, "wraps: %X, %X, %X\n", info->addressModeU,
209 info->addressModeV, info->addressModeW);
210
211 /* Blow up debug builds so we can fix the driconf. Allow the rare
212 * misrendering on release builds.
213 */
214 assert(0);
215 }
216
217 struct agx_sampler_packed samp;
218 pack_sampler(pdev, info, true, workaround_rgba4, &samp);
219
220 /* LOD bias passed in the descriptor set */
221 sampler->lod_bias_fp16 = _mesa_float_to_half(info->mipLodBias);
222
223 result =
224 hk_sampler_heap_add(dev, samp, &sampler->planes[sampler->plane_count].hw);
225 if (result != VK_SUCCESS) {
226 hk_DestroySampler(device, hk_sampler_to_handle(sampler), pAllocator);
227 return result;
228 }
229
230 sampler->plane_count++;
231
232 /* In order to support CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT, we
233 * need multiple sampler planes: at minimum we will need one for luminance
234 * (the default), and one for chroma. Each sampler plane needs its own
235 * sampler table entry. However, sampler table entries are very rare on
236 * G13, and each plane would burn one of those. So we make sure to allocate
237 * only the minimum amount that we actually need (i.e., either 1 or 2), and
238 * then just copy the last sampler plane out as far as we need to fill the
239 * number of image planes.
240 */
241 if (sampler->vk.ycbcr_conversion) {
242 assert(!uses_border(info) &&
243 "consequence of VUID-VkSamplerCreateInfo-addressModeU-01646");
244
245 const VkFilter chroma_filter =
246 sampler->vk.ycbcr_conversion->state.chroma_filter;
247 if (info->magFilter != chroma_filter ||
248 info->minFilter != chroma_filter) {
249 VkSamplerCreateInfo plane2_info = *info;
250 plane2_info.magFilter = chroma_filter;
251 plane2_info.minFilter = chroma_filter;
252
253 pack_sampler(pdev, &plane2_info, false, workaround_rgba4, &samp);
254 result = hk_sampler_heap_add(
255 dev, samp, &sampler->planes[sampler->plane_count].hw);
256
257 if (result != VK_SUCCESS) {
258 hk_DestroySampler(device, hk_sampler_to_handle(sampler),
259 pAllocator);
260 return result;
261 }
262
263 sampler->plane_count++;
264 }
265 } else if (custom_border) {
266 /* If the sampler uses custom border colours, we need both clamp-to-1
267 * and clamp-to-0 variants. We treat these as planes.
268 */
269 pack_sampler(pdev, info, false, workaround_rgba4, &samp);
270 result = hk_sampler_heap_add(dev, samp,
271 &sampler->planes[sampler->plane_count].hw);
272
273 if (result != VK_SUCCESS) {
274 hk_DestroySampler(device, hk_sampler_to_handle(sampler), pAllocator);
275 return result;
276 }
277
278 sampler->plane_count++;
279
280 /* We also need to record the border.
281 *
282 * If there is a border colour component mapping, we need to swizzle with
283 * it. Otherwise, we can assume there's nothing to do.
284 */
285 VkClearColorValue bc = sampler->vk.border_color_value;
286
287 const VkSamplerBorderColorComponentMappingCreateInfoEXT *swiz_info =
288 vk_find_struct_const(
289 info->pNext,
290 SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT);
291
292 if (swiz_info) {
293 const bool is_int = vk_border_color_is_int(info->borderColor);
294 bc = vk_swizzle_color_value(bc, swiz_info->components, is_int);
295 }
296
297 sampler->custom_border = bc;
298 sampler->has_border = true;
299 }
300
301 *pSampler = hk_sampler_to_handle(sampler);
302
303 return VK_SUCCESS;
304 }
305
306 VKAPI_ATTR void VKAPI_CALL
hk_DestroySampler(VkDevice device,VkSampler _sampler,const VkAllocationCallbacks * pAllocator)307 hk_DestroySampler(VkDevice device, VkSampler _sampler,
308 const VkAllocationCallbacks *pAllocator)
309 {
310 VK_FROM_HANDLE(hk_device, dev, device);
311 VK_FROM_HANDLE(hk_sampler, sampler, _sampler);
312
313 if (!sampler)
314 return;
315
316 for (uint8_t plane = 0; plane < sampler->plane_count; plane++) {
317 hk_sampler_heap_remove(dev, sampler->planes[plane].hw);
318 }
319
320 vk_sampler_destroy(&dev->vk, pAllocator, &sampler->vk);
321 }
322