xref: /aosp_15_r20/external/mesa3d/src/asahi/vulkan/hk_sampler.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
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_physical_device.h"
12 
13 #include "vk_enum_to_str.h"
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)89 is_border_color_custom(VkBorderColor color)
90 {
91    /* TODO: for now, opaque black is treated as custom due to rgba4 swizzling
92     * issues, could be optimized though.
93     */
94    switch (color) {
95    case VK_BORDER_COLOR_INT_OPAQUE_BLACK:
96    case VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK:
97    case VK_BORDER_COLOR_INT_CUSTOM_EXT:
98    case VK_BORDER_COLOR_FLOAT_CUSTOM_EXT:
99       return true;
100    default:
101       return false;
102    }
103 }
104 
105 /* Translate an American VkBorderColor into a Canadian agx_border_colour */
106 static enum agx_border_colour
translate_border_color(VkBorderColor color,bool custom_to_1)107 translate_border_color(VkBorderColor color, bool custom_to_1)
108 {
109    switch (color) {
110    case VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK:
111    case VK_BORDER_COLOR_INT_TRANSPARENT_BLACK:
112       return AGX_BORDER_COLOUR_TRANSPARENT_BLACK;
113 
114    case VK_BORDER_COLOR_INT_OPAQUE_WHITE:
115    case VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE:
116       return AGX_BORDER_COLOUR_OPAQUE_WHITE;
117 
118    default:
119       assert(is_border_color_custom(color));
120       return custom_to_1 ? AGX_BORDER_COLOUR_OPAQUE_WHITE
121                          : AGX_BORDER_COLOUR_TRANSPARENT_BLACK;
122    }
123 }
124 
125 static void
pack_sampler(const struct hk_physical_device * pdev,const struct VkSamplerCreateInfo * info,bool custom_to_1,struct agx_sampler_packed * out)126 pack_sampler(const struct hk_physical_device *pdev,
127              const struct VkSamplerCreateInfo *info, bool custom_to_1,
128              struct agx_sampler_packed *out)
129 {
130    agx_pack(out, SAMPLER, cfg) {
131       cfg.minimum_lod = info->minLod;
132       cfg.maximum_lod = info->maxLod;
133       cfg.magnify = translate_filter(info->magFilter);
134       cfg.minify = translate_filter(info->minFilter);
135       cfg.mip_filter = translate_mipfilter(info->mipmapMode);
136       cfg.wrap_s = translate_address_mode(info->addressModeU);
137       cfg.wrap_t = translate_address_mode(info->addressModeV);
138       cfg.wrap_r = translate_address_mode(info->addressModeW);
139       cfg.pixel_coordinates = info->unnormalizedCoordinates;
140 
141       cfg.seamful_cube_maps =
142          info->flags & VK_SAMPLER_CREATE_NON_SEAMLESS_CUBE_MAP_BIT_EXT;
143 
144       if (info->compareEnable) {
145          cfg.compare_func = translate_texsamp_compare_op(info->compareOp);
146          cfg.compare_enable = true;
147       }
148 
149       if (info->anisotropyEnable) {
150          cfg.maximum_anisotropy =
151             util_next_power_of_two(MAX2(info->maxAnisotropy, 1));
152       } else {
153          cfg.maximum_anisotropy = 1;
154       }
155 
156       if (uses_border(info)) {
157          cfg.border_colour =
158             translate_border_color(info->borderColor, custom_to_1);
159       }
160    }
161 }
162 
163 VKAPI_ATTR VkResult VKAPI_CALL
hk_CreateSampler(VkDevice device,const VkSamplerCreateInfo * info,const VkAllocationCallbacks * pAllocator,VkSampler * pSampler)164 hk_CreateSampler(VkDevice device,
165                  const VkSamplerCreateInfo *info /* pCreateInfo */,
166                  const VkAllocationCallbacks *pAllocator, VkSampler *pSampler)
167 {
168    VK_FROM_HANDLE(hk_device, dev, device);
169    struct hk_physical_device *pdev = hk_device_physical(dev);
170    struct hk_sampler *sampler;
171    VkResult result;
172 
173    sampler = vk_sampler_create(&dev->vk, info, pAllocator, sizeof(*sampler));
174    if (!sampler)
175       return vk_error(dev, VK_ERROR_OUT_OF_HOST_MEMORY);
176 
177    struct agx_sampler_packed samp;
178    pack_sampler(pdev, info, true, &samp);
179 
180    /* LOD bias passed in the descriptor set */
181    sampler->lod_bias_fp16 = _mesa_float_to_half(info->mipLodBias);
182 
183    result =
184       hk_sampler_heap_add(dev, samp, &sampler->planes[sampler->plane_count].hw);
185    if (result != VK_SUCCESS) {
186       hk_DestroySampler(device, hk_sampler_to_handle(sampler), pAllocator);
187       return result;
188    }
189 
190    sampler->plane_count++;
191 
192    /* In order to support CONVERSION_SEPARATE_RECONSTRUCTION_FILTER_BIT, we
193     * need multiple sampler planes: at minimum we will need one for luminance
194     * (the default), and one for chroma.  Each sampler plane needs its own
195     * sampler table entry.  However, sampler table entries are very rare on
196     * G13, and each plane would burn one of those. So we make sure to allocate
197     * only the minimum amount that we actually need (i.e., either 1 or 2), and
198     * then just copy the last sampler plane out as far as we need to fill the
199     * number of image planes.
200     */
201    if (sampler->vk.ycbcr_conversion) {
202       assert(!uses_border(info) &&
203              "consequence of VUID-VkSamplerCreateInfo-addressModeU-01646");
204 
205       const VkFilter chroma_filter =
206          sampler->vk.ycbcr_conversion->state.chroma_filter;
207       if (info->magFilter != chroma_filter ||
208           info->minFilter != chroma_filter) {
209          VkSamplerCreateInfo plane2_info = *info;
210          plane2_info.magFilter = chroma_filter;
211          plane2_info.minFilter = chroma_filter;
212 
213          pack_sampler(pdev, &plane2_info, false, &samp);
214          result = hk_sampler_heap_add(
215             dev, samp, &sampler->planes[sampler->plane_count].hw);
216 
217          if (result != VK_SUCCESS) {
218             hk_DestroySampler(device, hk_sampler_to_handle(sampler),
219                               pAllocator);
220             return result;
221          }
222 
223          sampler->plane_count++;
224       }
225    } else if (uses_border(info)) {
226       /* If the sampler uses custom border colours, we need both clamp-to-1
227        * and clamp-to-0 variants. We treat these as planes.
228        */
229       pack_sampler(pdev, info, false, &samp);
230       result = hk_sampler_heap_add(dev, samp,
231                                    &sampler->planes[sampler->plane_count].hw);
232 
233       if (result != VK_SUCCESS) {
234          hk_DestroySampler(device, hk_sampler_to_handle(sampler), pAllocator);
235          return result;
236       }
237 
238       sampler->plane_count++;
239 
240       /* We also need to record the border.
241        *
242        * If there is a border colour component mapping, we need to swizzle with
243        * it. Otherwise, we can assume there's nothing to do.
244        */
245       VkClearColorValue bc = sampler->vk.border_color_value;
246 
247       const VkSamplerBorderColorComponentMappingCreateInfoEXT *swiz_info =
248          vk_find_struct_const(
249             info->pNext,
250             SAMPLER_BORDER_COLOR_COMPONENT_MAPPING_CREATE_INFO_EXT);
251 
252       if (swiz_info) {
253          const bool is_int = vk_border_color_is_int(info->borderColor);
254          bc = vk_swizzle_color_value(bc, swiz_info->components, is_int);
255       }
256 
257       sampler->custom_border = bc;
258       sampler->has_border = true;
259    }
260 
261    *pSampler = hk_sampler_to_handle(sampler);
262 
263    return VK_SUCCESS;
264 }
265 
266 VKAPI_ATTR void VKAPI_CALL
hk_DestroySampler(VkDevice device,VkSampler _sampler,const VkAllocationCallbacks * pAllocator)267 hk_DestroySampler(VkDevice device, VkSampler _sampler,
268                   const VkAllocationCallbacks *pAllocator)
269 {
270    VK_FROM_HANDLE(hk_device, dev, device);
271    VK_FROM_HANDLE(hk_sampler, sampler, _sampler);
272 
273    if (!sampler)
274       return;
275 
276    for (uint8_t plane = 0; plane < sampler->plane_count; plane++) {
277       hk_sampler_heap_remove(dev, sampler->planes[plane].hw);
278    }
279 
280    vk_sampler_destroy(&dev->vk, pAllocator, &sampler->vk);
281 }
282