1 /*
2 * Copyright © 2024 Valve Corporation
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7 #include "radv_pipeline_binary.h"
8 #include "util/disk_cache.h"
9 #include "util/macros.h"
10 #include "util/mesa-blake3.h"
11 #include "util/mesa-sha1.h"
12 #include "util/u_atomic.h"
13 #include "util/u_debug.h"
14 #include "nir_serialize.h"
15 #include "radv_debug.h"
16 #include "radv_device.h"
17 #include "radv_entrypoints.h"
18 #include "radv_pipeline_cache.h"
19 #include "radv_pipeline_graphics.h"
20 #include "radv_pipeline_rt.h"
21 #include "radv_shader.h"
22 #include "vk_log.h"
23 #include "vk_pipeline.h"
24 #include "vk_util.h"
25
26 static VkResult
radv_get_pipeline_key(struct radv_device * device,const VkPipelineCreateInfoKHR * pPipelineCreateInfo,unsigned char * key)27 radv_get_pipeline_key(struct radv_device *device, const VkPipelineCreateInfoKHR *pPipelineCreateInfo,
28 unsigned char *key)
29 {
30 VkResult result = VK_SUCCESS;
31
32 switch (((VkBaseInStructure *)pPipelineCreateInfo->pNext)->sType) {
33 case VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO: {
34 const VkGraphicsPipelineCreateInfo *graphics_create_info =
35 (VkGraphicsPipelineCreateInfo *)pPipelineCreateInfo->pNext;
36 struct radv_graphics_pipeline_state gfx_state;
37
38 result = radv_generate_graphics_pipeline_state(device, graphics_create_info, &gfx_state);
39 if (result != VK_SUCCESS)
40 return result;
41
42 radv_graphics_pipeline_hash(device, &gfx_state, key);
43 radv_graphics_pipeline_state_finish(device, &gfx_state);
44 break;
45 }
46 case VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO: {
47 const VkComputePipelineCreateInfo *compute_create_info =
48 (VkComputePipelineCreateInfo *)pPipelineCreateInfo->pNext;
49
50 radv_compute_pipeline_hash(device, compute_create_info, key);
51 break;
52 }
53 case VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR: {
54 const VkRayTracingPipelineCreateInfoKHR *rt_create_info =
55 (VkRayTracingPipelineCreateInfoKHR *)pPipelineCreateInfo->pNext;
56 struct radv_ray_tracing_state_key rt_state;
57
58 result = radv_generate_ray_tracing_state_key(device, rt_create_info, &rt_state);
59 if (result != VK_SUCCESS)
60 return result;
61
62 radv_ray_tracing_pipeline_hash(device, rt_create_info, &rt_state, key);
63 radv_ray_tracing_state_key_finish(&rt_state);
64 break;
65 }
66 default:
67 unreachable("unsupported pipeline create info struct");
68 }
69
70 return result;
71 }
72
73 VKAPI_ATTR VkResult VKAPI_CALL
radv_GetPipelineKeyKHR(VkDevice _device,const VkPipelineCreateInfoKHR * pPipelineCreateInfo,VkPipelineBinaryKeyKHR * pPipelineKey)74 radv_GetPipelineKeyKHR(VkDevice _device, const VkPipelineCreateInfoKHR *pPipelineCreateInfo,
75 VkPipelineBinaryKeyKHR *pPipelineKey)
76 {
77 VK_FROM_HANDLE(radv_device, device, _device);
78 const struct radv_physical_device *pdev = radv_device_physical(device);
79 VkResult result;
80
81 memset(pPipelineKey->key, 0, sizeof(pPipelineKey->key));
82
83 /* Return the global key that applies to all pipelines. */
84 if (!pPipelineCreateInfo) {
85 struct mesa_blake3 ctx;
86
87 static_assert(sizeof(blake3_hash) <= sizeof(pPipelineKey->key), "mismatch pipeline binary key size");
88
89 _mesa_blake3_init(&ctx);
90 _mesa_blake3_update(&ctx, pdev->cache_uuid, sizeof(pdev->cache_uuid));
91 _mesa_blake3_update(&ctx, device->cache_hash, sizeof(device->cache_hash));
92 _mesa_blake3_final(&ctx, pPipelineKey->key);
93
94 pPipelineKey->keySize = sizeof(blake3_hash);
95
96 return VK_SUCCESS;
97 }
98
99 result = radv_get_pipeline_key(device, pPipelineCreateInfo, pPipelineKey->key);
100 if (result != VK_SUCCESS)
101 return result;
102
103 pPipelineKey->keySize = SHA1_DIGEST_LENGTH;
104
105 return VK_SUCCESS;
106 }
107
108 static VkResult
radv_create_pipeline_binary(struct radv_device * device,const VkAllocationCallbacks * pAllocator,const blake3_hash key,const void * data,size_t data_size,struct radv_pipeline_binary ** pipeline_binary_out)109 radv_create_pipeline_binary(struct radv_device *device, const VkAllocationCallbacks *pAllocator, const blake3_hash key,
110 const void *data, size_t data_size, struct radv_pipeline_binary **pipeline_binary_out)
111 {
112 struct radv_pipeline_binary *pipeline_binary;
113
114 pipeline_binary =
115 vk_zalloc2(&device->vk.alloc, pAllocator, sizeof(*pipeline_binary), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
116 if (pipeline_binary == NULL)
117 return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);
118
119 vk_object_base_init(&device->vk, &pipeline_binary->base, VK_OBJECT_TYPE_PIPELINE_BINARY_KHR);
120
121 pipeline_binary->data = (void *)data;
122 pipeline_binary->size = data_size;
123
124 memcpy(pipeline_binary->key, key, BLAKE3_OUT_LEN);
125
126 *pipeline_binary_out = pipeline_binary;
127 return VK_SUCCESS;
128 }
129
130 static VkResult
radv_create_pipeline_binary_from_data(struct radv_device * device,const VkAllocationCallbacks * pAllocator,const VkPipelineBinaryDataKHR * pData,const VkPipelineBinaryKeyKHR * pKey,struct util_dynarray * pipeline_binaries,uint32_t * num_binaries)131 radv_create_pipeline_binary_from_data(struct radv_device *device, const VkAllocationCallbacks *pAllocator,
132 const VkPipelineBinaryDataKHR *pData, const VkPipelineBinaryKeyKHR *pKey,
133 struct util_dynarray *pipeline_binaries, uint32_t *num_binaries)
134 {
135 struct radv_pipeline_binary *pipeline_binary;
136 VkResult result;
137 void *data;
138
139 if (!pipeline_binaries) {
140 (*num_binaries)++;
141 return VK_SUCCESS;
142 }
143
144 data = malloc(pData->dataSize);
145 if (!data)
146 return VK_ERROR_OUT_OF_HOST_MEMORY;
147
148 memcpy(data, pData->pData, pData->dataSize);
149
150 result = radv_create_pipeline_binary(device, pAllocator, pKey->key, data, pData->dataSize, &pipeline_binary);
151 if (result != VK_SUCCESS) {
152 free(data);
153 return result;
154 }
155
156 util_dynarray_append(pipeline_binaries, struct radv_pipeline_binary *, pipeline_binary);
157 return result;
158 }
159
160 VkResult
radv_create_pipeline_binary_from_shader(struct radv_device * device,const VkAllocationCallbacks * pAllocator,struct radv_shader * shader,struct util_dynarray * pipeline_binaries,uint32_t * num_binaries)161 radv_create_pipeline_binary_from_shader(struct radv_device *device, const VkAllocationCallbacks *pAllocator,
162 struct radv_shader *shader, struct util_dynarray *pipeline_binaries,
163 uint32_t *num_binaries)
164 {
165 struct radv_pipeline_binary *pipeline_binary;
166 struct blob blob;
167 size_t data_size;
168 VkResult result;
169 void *data;
170
171 if (!pipeline_binaries) {
172 (*num_binaries)++;
173 return VK_SUCCESS;
174 }
175
176 blob_init(&blob);
177 radv_shader_serialize(shader, &blob);
178 blob_finish_get_buffer(&blob, &data, &data_size);
179
180 result = radv_create_pipeline_binary(device, pAllocator, shader->hash, data, data_size, &pipeline_binary);
181 if (result != VK_SUCCESS) {
182 free(data);
183 return result;
184 }
185
186 util_dynarray_append(pipeline_binaries, struct radv_pipeline_binary *, pipeline_binary);
187 return result;
188 }
189
190 VkResult
radv_create_pipeline_binary_from_rt_shader(struct radv_device * device,const VkAllocationCallbacks * pAllocator,struct radv_shader * shader,bool is_traversal_shader,const uint8_t stage_sha1[SHA1_DIGEST_LENGTH],const struct radv_ray_tracing_stage_info * rt_stage_info,uint32_t stack_size,struct vk_pipeline_cache_object * nir,struct util_dynarray * pipeline_binaries,uint32_t * num_binaries)191 radv_create_pipeline_binary_from_rt_shader(struct radv_device *device, const VkAllocationCallbacks *pAllocator,
192 struct radv_shader *shader, bool is_traversal_shader,
193 const uint8_t stage_sha1[SHA1_DIGEST_LENGTH],
194 const struct radv_ray_tracing_stage_info *rt_stage_info, uint32_t stack_size,
195 struct vk_pipeline_cache_object *nir,
196 struct util_dynarray *pipeline_binaries, uint32_t *num_binaries)
197 {
198 struct radv_pipeline_binary *pipeline_binary;
199 struct mesa_blake3 ctx;
200 struct blob blob;
201 size_t data_size;
202 blake3_hash key;
203 VkResult result;
204 void *data;
205
206 if (!pipeline_binaries) {
207 (*num_binaries)++;
208 return VK_SUCCESS;
209 }
210
211 _mesa_blake3_init(&ctx);
212 _mesa_blake3_update(&ctx, stage_sha1, sizeof(*stage_sha1));
213 _mesa_blake3_final(&ctx, key);
214
215 struct radv_ray_tracing_binary_header header = {
216 .is_traversal_shader = is_traversal_shader,
217 .has_shader = !!shader,
218 .has_nir = !!nir,
219 .stack_size = stack_size,
220 };
221
222 memcpy(header.stage_sha1, stage_sha1, sizeof(header.stage_sha1));
223 memcpy(&header.stage_info, rt_stage_info, sizeof(header.stage_info));
224
225 blob_init(&blob);
226 blob_write_bytes(&blob, &header, sizeof(header));
227 if (header.has_shader)
228 radv_shader_serialize(shader, &blob);
229 if (header.has_nir) {
230 struct vk_raw_data_cache_object *nir_object = container_of(nir, struct vk_raw_data_cache_object, base);
231 blob_write_bytes(&blob, nir_object->data, nir_object->data_size);
232 }
233 blob_finish_get_buffer(&blob, &data, &data_size);
234
235 result = radv_create_pipeline_binary(device, pAllocator, key, data, data_size, &pipeline_binary);
236 if (result != VK_SUCCESS) {
237 free(data);
238 return result;
239 }
240
241 util_dynarray_append(pipeline_binaries, struct radv_pipeline_binary *, pipeline_binary);
242 return result;
243 }
244
245 static VkResult
radv_create_pipeline_binary_from_pipeline(struct radv_device * device,const VkAllocationCallbacks * pAllocator,struct radv_pipeline * pipeline,struct util_dynarray * pipeline_binaries,uint32_t * num_binaries)246 radv_create_pipeline_binary_from_pipeline(struct radv_device *device, const VkAllocationCallbacks *pAllocator,
247 struct radv_pipeline *pipeline, struct util_dynarray *pipeline_binaries,
248 uint32_t *num_binaries)
249 {
250 VkResult result = VK_SUCCESS;
251
252 if (pipeline->type == RADV_PIPELINE_RAY_TRACING) {
253 struct radv_ray_tracing_pipeline *rt_pipeline = radv_pipeline_to_ray_tracing(pipeline);
254
255 for (uint32_t i = 0; i < rt_pipeline->non_imported_stage_count; i++) {
256 struct radv_ray_tracing_stage *rt_stage = &rt_pipeline->stages[i];
257
258 result = radv_create_pipeline_binary_from_rt_shader(device, pAllocator, rt_stage->shader, false,
259 rt_stage->sha1, &rt_stage->info, rt_stage->stack_size,
260 rt_stage->nir, pipeline_binaries, num_binaries);
261 if (result != VK_SUCCESS)
262 return result;
263 }
264
265 struct radv_shader *traversal_shader = rt_pipeline->base.base.shaders[MESA_SHADER_INTERSECTION];
266 if (traversal_shader) {
267 result = radv_create_pipeline_binary_from_rt_shader(device, pAllocator, traversal_shader, true,
268 traversal_shader->hash, NULL, 0, NULL, pipeline_binaries,
269 num_binaries);
270 if (result != VK_SUCCESS)
271 return result;
272 }
273 } else {
274 for (uint32_t i = 0; i < MESA_VULKAN_SHADER_STAGES; i++) {
275 if (!pipeline->shaders[i])
276 continue;
277
278 result = radv_create_pipeline_binary_from_shader(device, pAllocator, pipeline->shaders[i], pipeline_binaries,
279 num_binaries);
280 if (result != VK_SUCCESS)
281 return result;
282 }
283
284 if (pipeline->gs_copy_shader) {
285 result = radv_create_pipeline_binary_from_shader(device, pAllocator, pipeline->gs_copy_shader,
286 pipeline_binaries, num_binaries);
287 if (result != VK_SUCCESS)
288 return result;
289 }
290 }
291
292 return result;
293 }
294
295 static VkResult
radv_create_pipeline_binary_from_cache(struct radv_device * device,const VkAllocationCallbacks * pAllocator,const VkPipelineCreateInfoKHR * pPipelineCreateInfo,struct util_dynarray * pipeline_binaries,uint32_t * num_binaries)296 radv_create_pipeline_binary_from_cache(struct radv_device *device, const VkAllocationCallbacks *pAllocator,
297 const VkPipelineCreateInfoKHR *pPipelineCreateInfo,
298 struct util_dynarray *pipeline_binaries, uint32_t *num_binaries)
299 {
300 unsigned char key[SHA1_DIGEST_LENGTH];
301 bool found_in_internal_cache;
302 VkResult result;
303
304 assert(pPipelineCreateInfo);
305
306 result = radv_get_pipeline_key(device, pPipelineCreateInfo, key);
307 if (result != VK_SUCCESS)
308 return result;
309
310 result = radv_pipeline_cache_get_binaries(device, pAllocator, key, pipeline_binaries, num_binaries,
311 &found_in_internal_cache);
312 if (result != VK_SUCCESS)
313 return result;
314
315 return found_in_internal_cache ? VK_SUCCESS : VK_PIPELINE_BINARY_MISSING_KHR;
316 }
317
318 static VkResult
radv_create_pipeline_binaries(struct radv_device * device,const VkPipelineBinaryCreateInfoKHR * pCreateInfo,const VkAllocationCallbacks * pAllocator,struct util_dynarray * pipeline_binaries,uint32_t * num_binaries)319 radv_create_pipeline_binaries(struct radv_device *device, const VkPipelineBinaryCreateInfoKHR *pCreateInfo,
320 const VkAllocationCallbacks *pAllocator, struct util_dynarray *pipeline_binaries,
321 uint32_t *num_binaries)
322 {
323 VkResult result = VK_SUCCESS;
324
325 if (pCreateInfo->pKeysAndDataInfo) {
326 const VkPipelineBinaryKeysAndDataKHR *pKeysAndDataInfo = pCreateInfo->pKeysAndDataInfo;
327
328 for (uint32_t i = 0; i < pKeysAndDataInfo->binaryCount; i++) {
329 const VkPipelineBinaryDataKHR *pData = &pKeysAndDataInfo->pPipelineBinaryData[i];
330 const VkPipelineBinaryKeyKHR *pKey = &pKeysAndDataInfo->pPipelineBinaryKeys[i];
331
332 result =
333 radv_create_pipeline_binary_from_data(device, pAllocator, pData, pKey, pipeline_binaries, num_binaries);
334 if (result != VK_SUCCESS)
335 return result;
336 }
337 } else if (pCreateInfo->pipeline) {
338 VK_FROM_HANDLE(radv_pipeline, pipeline, pCreateInfo->pipeline);
339
340 result = radv_create_pipeline_binary_from_pipeline(device, pAllocator, pipeline, pipeline_binaries, num_binaries);
341 } else {
342 result = radv_create_pipeline_binary_from_cache(device, pAllocator, pCreateInfo->pPipelineCreateInfo,
343 pipeline_binaries, num_binaries);
344 }
345
346 return result;
347 }
348
349 static void
radv_destroy_pipeline_binary(struct radv_device * device,const VkAllocationCallbacks * pAllocator,struct radv_pipeline_binary * pipeline_binary)350 radv_destroy_pipeline_binary(struct radv_device *device, const VkAllocationCallbacks *pAllocator,
351 struct radv_pipeline_binary *pipeline_binary)
352 {
353 if (!pipeline_binary)
354 return;
355
356 free(pipeline_binary->data);
357
358 vk_object_base_finish(&pipeline_binary->base);
359 vk_free2(&device->vk.alloc, pAllocator, pipeline_binary);
360 }
361
362 VKAPI_ATTR VkResult VKAPI_CALL
radv_CreatePipelineBinariesKHR(VkDevice _device,const VkPipelineBinaryCreateInfoKHR * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkPipelineBinaryHandlesInfoKHR * pBinaries)363 radv_CreatePipelineBinariesKHR(VkDevice _device, const VkPipelineBinaryCreateInfoKHR *pCreateInfo,
364 const VkAllocationCallbacks *pAllocator, VkPipelineBinaryHandlesInfoKHR *pBinaries)
365 {
366 VK_FROM_HANDLE(radv_device, device, _device);
367 struct util_dynarray pipeline_binaries;
368 VkResult result;
369
370 if (!pBinaries->pPipelineBinaries) {
371 result = radv_create_pipeline_binaries(device, pCreateInfo, pAllocator, NULL, &pBinaries->pipelineBinaryCount);
372 return result;
373 }
374
375 for (uint32_t i = 0; i < pBinaries->pipelineBinaryCount; i++)
376 pBinaries->pPipelineBinaries[i] = VK_NULL_HANDLE;
377
378 util_dynarray_init(&pipeline_binaries, NULL);
379
380 /* Get all pipeline binaries from the pCreateInfo first to simplify the creation. */
381 result = radv_create_pipeline_binaries(device, pCreateInfo, pAllocator, &pipeline_binaries, NULL);
382 if (result != VK_SUCCESS) {
383 util_dynarray_foreach (&pipeline_binaries, struct radv_pipeline_binary *, pipeline_binary)
384 radv_destroy_pipeline_binary(device, pAllocator, *pipeline_binary);
385 util_dynarray_fini(&pipeline_binaries);
386 return result;
387 }
388
389 const uint32_t num_binaries = util_dynarray_num_elements(&pipeline_binaries, struct radv_pipeline_binary *);
390
391 for (uint32_t i = 0; i < num_binaries; i++) {
392 struct radv_pipeline_binary **pipeline_binary =
393 util_dynarray_element(&pipeline_binaries, struct radv_pipeline_binary *, i);
394
395 if (i < pBinaries->pipelineBinaryCount) {
396 pBinaries->pPipelineBinaries[i] = radv_pipeline_binary_to_handle(*pipeline_binary);
397 } else {
398 /* Free the pipeline binary that couldn't be returned. */
399 radv_destroy_pipeline_binary(device, pAllocator, *pipeline_binary);
400 }
401 }
402
403 result = pBinaries->pipelineBinaryCount < num_binaries ? VK_INCOMPLETE : result;
404 pBinaries->pipelineBinaryCount = MIN2(num_binaries, pBinaries->pipelineBinaryCount);
405
406 util_dynarray_fini(&pipeline_binaries);
407 return result;
408 }
409
410 VKAPI_ATTR void VKAPI_CALL
radv_DestroyPipelineBinaryKHR(VkDevice _device,VkPipelineBinaryKHR pipelineBinary,const VkAllocationCallbacks * pAllocator)411 radv_DestroyPipelineBinaryKHR(VkDevice _device, VkPipelineBinaryKHR pipelineBinary,
412 const VkAllocationCallbacks *pAllocator)
413 {
414 VK_FROM_HANDLE(radv_pipeline_binary, pipeline_binary, pipelineBinary);
415 VK_FROM_HANDLE(radv_device, device, _device);
416
417 radv_destroy_pipeline_binary(device, pAllocator, pipeline_binary);
418 }
419
420 VKAPI_ATTR VkResult VKAPI_CALL
radv_GetPipelineBinaryDataKHR(VkDevice _device,const VkPipelineBinaryDataInfoKHR * pInfo,VkPipelineBinaryKeyKHR * pPipelineBinaryKey,size_t * pPipelineBinaryDataSize,void * pPipelineBinaryData)421 radv_GetPipelineBinaryDataKHR(VkDevice _device, const VkPipelineBinaryDataInfoKHR *pInfo,
422 VkPipelineBinaryKeyKHR *pPipelineBinaryKey, size_t *pPipelineBinaryDataSize,
423 void *pPipelineBinaryData)
424 {
425 VK_FROM_HANDLE(radv_pipeline_binary, pipeline_binary, pInfo->pipelineBinary);
426 const size_t size = pipeline_binary->size;
427
428 memcpy(pPipelineBinaryKey->key, pipeline_binary->key, sizeof(pipeline_binary->key));
429 pPipelineBinaryKey->keySize = sizeof(pipeline_binary->key);
430
431 if (!pPipelineBinaryData) {
432 *pPipelineBinaryDataSize = size;
433 return VK_SUCCESS;
434 }
435
436 if (*pPipelineBinaryDataSize < size) {
437 *pPipelineBinaryDataSize = size;
438 return VK_ERROR_NOT_ENOUGH_SPACE_KHR;
439 }
440
441 memcpy(pPipelineBinaryData, pipeline_binary->data, size);
442 *pPipelineBinaryDataSize = size;
443
444 return VK_SUCCESS;
445 }
446
447 VKAPI_ATTR VkResult VKAPI_CALL
radv_ReleaseCapturedPipelineDataKHR(VkDevice _device,const VkReleaseCapturedPipelineDataInfoKHR * pInfo,const VkAllocationCallbacks * pAllocator)448 radv_ReleaseCapturedPipelineDataKHR(VkDevice _device, const VkReleaseCapturedPipelineDataInfoKHR *pInfo,
449 const VkAllocationCallbacks *pAllocator)
450 {
451 /* no-op */
452 return VK_SUCCESS;
453 }
454