• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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