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