Branch data Line data Source code
1 : : /*
2 : : * This file is part of libplacebo.
3 : : *
4 : : * libplacebo is free software; you can redistribute it and/or
5 : : * modify it under the terms of the GNU Lesser General Public
6 : : * License as published by the Free Software Foundation; either
7 : : * version 2.1 of the License, or (at your option) any later version.
8 : : *
9 : : * libplacebo is distributed in the hope that it will be useful,
10 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : : * GNU Lesser General Public License for more details.
13 : : *
14 : : * You should have received a copy of the GNU Lesser General Public
15 : : * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16 : : */
17 : :
18 : : #include "common.h"
19 : : #include "command.h"
20 : : #include "utils.h"
21 : : #include "gpu.h"
22 : :
23 : : #ifdef PL_HAVE_VK_PROC_ADDR
24 : : VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vkGetInstanceProcAddr(
25 : : VkInstance instance,
26 : : const char* pName);
27 : : #endif
28 : :
29 : : const struct pl_vk_inst_params pl_vk_inst_default_params = {0};
30 : :
31 : : struct vk_fun {
32 : : const char *name;
33 : : size_t offset;
34 : : bool device_level;
35 : : };
36 : :
37 : : struct vk_ext {
38 : : const char *name;
39 : : const struct vk_fun *funs;
40 : : };
41 : :
42 : : #define PL_VK_INST_FUN(N) \
43 : : { .name = "vk" #N, \
44 : : .offset = offsetof(struct vk_ctx, N), \
45 : : }
46 : :
47 : : #define PL_VK_DEV_FUN(N) \
48 : : { .name = "vk" #N, \
49 : : .offset = offsetof(struct vk_ctx, N), \
50 : : .device_level = true, \
51 : : }
52 : :
53 : : // Table of optional vulkan instance extensions
54 : : static const char *vk_instance_extensions[] = {
55 : : VK_KHR_SURFACE_EXTENSION_NAME,
56 : : VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME,
57 : : VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
58 : : };
59 : :
60 : : // List of mandatory instance-level function pointers, including functions
61 : : // associated with mandatory instance extensions
62 : : static const struct vk_fun vk_inst_funs[] = {
63 : : PL_VK_INST_FUN(CreateDevice),
64 : : PL_VK_INST_FUN(EnumerateDeviceExtensionProperties),
65 : : PL_VK_INST_FUN(GetDeviceProcAddr),
66 : : PL_VK_INST_FUN(GetPhysicalDeviceExternalBufferProperties),
67 : : PL_VK_INST_FUN(GetPhysicalDeviceExternalSemaphoreProperties),
68 : : PL_VK_INST_FUN(GetPhysicalDeviceFeatures2KHR),
69 : : PL_VK_INST_FUN(GetPhysicalDeviceFormatProperties),
70 : : PL_VK_INST_FUN(GetPhysicalDeviceFormatProperties2KHR),
71 : : PL_VK_INST_FUN(GetPhysicalDeviceImageFormatProperties2KHR),
72 : : PL_VK_INST_FUN(GetPhysicalDeviceMemoryProperties),
73 : : PL_VK_INST_FUN(GetPhysicalDeviceProperties),
74 : : PL_VK_INST_FUN(GetPhysicalDeviceProperties2),
75 : : PL_VK_INST_FUN(GetPhysicalDeviceQueueFamilyProperties),
76 : :
77 : : // These are not actually mandatory, but they're universal enough that we
78 : : // just load them unconditionally (in lieu of not having proper support for
79 : : // loading arbitrary instance extensions). Their use is generally guarded
80 : : // behind various VkSurfaceKHR values already being provided by the API
81 : : // user (implying this extension is loaded).
82 : : PL_VK_INST_FUN(GetPhysicalDeviceSurfaceCapabilitiesKHR),
83 : : PL_VK_INST_FUN(GetPhysicalDeviceSurfaceFormatsKHR),
84 : : PL_VK_INST_FUN(GetPhysicalDeviceSurfacePresentModesKHR),
85 : : PL_VK_INST_FUN(GetPhysicalDeviceSurfaceSupportKHR),
86 : : };
87 : :
88 : : // Table of vulkan device extensions and functions they load, including
89 : : // functions exported by dependent instance-level extensions
90 : : static const struct vk_ext vk_device_extensions[] = {
91 : : {
92 : : .name = VK_KHR_SWAPCHAIN_EXTENSION_NAME,
93 : : .funs = (const struct vk_fun[]) {
94 : : PL_VK_DEV_FUN(AcquireNextImageKHR),
95 : : PL_VK_DEV_FUN(CreateSwapchainKHR),
96 : : PL_VK_DEV_FUN(DestroySwapchainKHR),
97 : : PL_VK_DEV_FUN(GetSwapchainImagesKHR),
98 : : PL_VK_DEV_FUN(QueuePresentKHR),
99 : : {0}
100 : : },
101 : : }, {
102 : : .name = VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
103 : : .funs = (const struct vk_fun[]) {
104 : : PL_VK_DEV_FUN(CmdPushDescriptorSetKHR),
105 : : {0}
106 : : },
107 : : }, {
108 : : .name = VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
109 : : .funs = (const struct vk_fun[]) {
110 : : PL_VK_DEV_FUN(GetMemoryFdKHR),
111 : : {0}
112 : : },
113 : : }, {
114 : : .name = VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
115 : : .funs = (const struct vk_fun[]) {
116 : : PL_VK_DEV_FUN(GetMemoryFdPropertiesKHR),
117 : : {0}
118 : : },
119 : : #ifdef PL_HAVE_WIN32
120 : : }, {
121 : : .name = VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
122 : : .funs = (const struct vk_fun[]) {
123 : : PL_VK_DEV_FUN(GetMemoryWin32HandleKHR),
124 : : {0}
125 : : },
126 : : #endif
127 : : }, {
128 : : .name = VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
129 : : .funs = (const struct vk_fun[]) {
130 : : PL_VK_DEV_FUN(GetMemoryHostPointerPropertiesEXT),
131 : : {0}
132 : : },
133 : : }, {
134 : : .name = VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
135 : : .funs = (const struct vk_fun[]) {
136 : : PL_VK_DEV_FUN(GetSemaphoreFdKHR),
137 : : {0}
138 : : },
139 : : #ifdef PL_HAVE_WIN32
140 : : }, {
141 : : .name = VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME,
142 : : .funs = (const struct vk_fun[]) {
143 : : PL_VK_DEV_FUN(GetSemaphoreWin32HandleKHR),
144 : : {0}
145 : : },
146 : : #endif
147 : : }, {
148 : : .name = VK_EXT_PCI_BUS_INFO_EXTENSION_NAME,
149 : : }, {
150 : : .name = VK_EXT_HDR_METADATA_EXTENSION_NAME,
151 : : .funs = (const struct vk_fun[]) {
152 : : PL_VK_DEV_FUN(SetHdrMetadataEXT),
153 : : {0}
154 : : },
155 : : }, {
156 : : .name = VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
157 : : .funs = (const struct vk_fun[]) {
158 : : PL_VK_DEV_FUN(GetImageDrmFormatModifierPropertiesEXT),
159 : : {0}
160 : : },
161 : : #ifdef VK_KHR_portability_subset
162 : : }, {
163 : : .name = VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
164 : : #endif
165 : : #ifdef VK_EXT_metal_objects
166 : : }, {
167 : : .name = VK_EXT_METAL_OBJECTS_EXTENSION_NAME,
168 : : .funs = (const struct vk_fun[]) {
169 : : PL_VK_DEV_FUN(ExportMetalObjectsEXT),
170 : : {0}
171 : : },
172 : : #endif
173 : : #ifdef VK_EXT_full_screen_exclusive
174 : : }, {
175 : : .name = VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME,
176 : : .funs = (const struct vk_fun[]) {
177 : : PL_VK_DEV_FUN(AcquireFullScreenExclusiveModeEXT),
178 : : {0}
179 : : },
180 : : #endif
181 : : }, {
182 : : .name = VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
183 : : .funs = (const struct vk_fun[]) {
184 : : PL_VK_DEV_FUN(CmdPipelineBarrier2KHR),
185 : : PL_VK_DEV_FUN(QueueSubmit2KHR),
186 : : {0}
187 : : },
188 : : },
189 : : };
190 : :
191 : : // Make sure to keep this in sync with the above!
192 : : const char * const pl_vulkan_recommended_extensions[] = {
193 : : VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,
194 : : VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
195 : : VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,
196 : : VK_EXT_EXTERNAL_MEMORY_DMA_BUF_EXTENSION_NAME,
197 : : VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
198 : : #ifdef PL_HAVE_WIN32
199 : : VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,
200 : : VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME,
201 : : #endif
202 : : VK_EXT_PCI_BUS_INFO_EXTENSION_NAME,
203 : : VK_EXT_HDR_METADATA_EXTENSION_NAME,
204 : : VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
205 : : #ifdef VK_KHR_portability_subset
206 : : VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
207 : : #endif
208 : : #ifdef VK_EXT_metal_objects
209 : : VK_EXT_METAL_OBJECTS_EXTENSION_NAME,
210 : : #endif
211 : : #ifdef VK_EXT_full_screen_exclusive
212 : : VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME,
213 : : #endif
214 : : VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,
215 : : };
216 : :
217 : : const int pl_vulkan_num_recommended_extensions =
218 : : PL_ARRAY_SIZE(pl_vulkan_recommended_extensions);
219 : :
220 : : // +1 because VK_KHR_swapchain is not automatically pulled in
221 : : static_assert(PL_ARRAY_SIZE(pl_vulkan_recommended_extensions) + 1 ==
222 : : PL_ARRAY_SIZE(vk_device_extensions),
223 : : "pl_vulkan_recommended_extensions out of sync with "
224 : : "vk_device_extensions?");
225 : :
226 : : // Recommended features; keep in sync with libavutil vulkan hwcontext
227 : : static const VkPhysicalDeviceVulkan13Features recommended_vk13 = {
228 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
229 : : .computeFullSubgroups = true,
230 : : .maintenance4 = true,
231 : : .shaderZeroInitializeWorkgroupMemory = true,
232 : : .synchronization2 = true,
233 : : };
234 : :
235 : : static const VkPhysicalDeviceVulkan12Features recommended_vk12 = {
236 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
237 : : .pNext = (void *) &recommended_vk13,
238 : : .bufferDeviceAddress = true,
239 : : .storagePushConstant8 = true,
240 : : .shaderInt8 = true,
241 : : .shaderFloat16 = true,
242 : : .shaderSharedInt64Atomics = true,
243 : : .storageBuffer8BitAccess = true,
244 : : .uniformAndStorageBuffer8BitAccess = true,
245 : : .vulkanMemoryModel = true,
246 : : .vulkanMemoryModelDeviceScope = true,
247 : : };
248 : :
249 : : static const VkPhysicalDeviceVulkan11Features recommended_vk11 = {
250 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
251 : : .pNext = (void *) &recommended_vk12,
252 : : .samplerYcbcrConversion = true,
253 : : .storagePushConstant16 = true,
254 : : };
255 : :
256 : : const VkPhysicalDeviceFeatures2 pl_vulkan_recommended_features = {
257 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
258 : : .pNext = (void *) &recommended_vk11,
259 : : .features = {
260 : : .shaderImageGatherExtended = true,
261 : : .shaderStorageImageReadWithoutFormat = true,
262 : : .shaderStorageImageWriteWithoutFormat = true,
263 : :
264 : : // Needed for GPU-assisted validation, but not harmful to enable
265 : : .fragmentStoresAndAtomics = true,
266 : : .vertexPipelineStoresAndAtomics = true,
267 : : .shaderInt64 = true,
268 : : }
269 : : };
270 : :
271 : : // Required features
272 : : static const VkPhysicalDeviceVulkan12Features required_vk12 = {
273 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
274 : : .hostQueryReset = true,
275 : : .timelineSemaphore = true,
276 : : };
277 : :
278 : : static const VkPhysicalDeviceVulkan11Features required_vk11 = {
279 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
280 : : .pNext = (void *) &required_vk12,
281 : : };
282 : :
283 : : const VkPhysicalDeviceFeatures2 pl_vulkan_required_features = {
284 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
285 : : .pNext = (void *) &required_vk11,
286 : : };
287 : :
288 : 3 : static bool check_required_features(struct vk_ctx *vk)
289 : : {
290 : : #define CHECK_FEATURE(maj, min, feat) do { \
291 : : const VkPhysicalDeviceVulkan##maj##min##Features *f; \
292 : : f = vk_find_struct(&vk->features, \
293 : : VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_##maj##_##min##_FEATURES); \
294 : : if (!f || !f->feat) { \
295 : : PL_ERR(vk, "Missing device feature: " #feat); \
296 : : return false; \
297 : : } \
298 : : } while (0)
299 : :
300 [ + - - + ]: 3 : CHECK_FEATURE(1, 2, hostQueryReset);
301 [ + - - + ]: 3 : CHECK_FEATURE(1, 2, timelineSemaphore);
302 : :
303 : : #undef CHECK_FEATURE
304 : : return true;
305 : : }
306 : :
307 : :
308 : : // List of mandatory device-level functions
309 : : //
310 : : // Note: Also includes VK_EXT_debug_utils functions, even though they aren't
311 : : // mandatory, simply because we load that extension in a special way.
312 : : static const struct vk_fun vk_dev_funs[] = {
313 : : PL_VK_DEV_FUN(AllocateCommandBuffers),
314 : : PL_VK_DEV_FUN(AllocateDescriptorSets),
315 : : PL_VK_DEV_FUN(AllocateMemory),
316 : : PL_VK_DEV_FUN(BeginCommandBuffer),
317 : : PL_VK_DEV_FUN(BindBufferMemory),
318 : : PL_VK_DEV_FUN(BindImageMemory),
319 : : PL_VK_DEV_FUN(CmdBeginDebugUtilsLabelEXT),
320 : : PL_VK_DEV_FUN(CmdBeginRenderPass),
321 : : PL_VK_DEV_FUN(CmdBindDescriptorSets),
322 : : PL_VK_DEV_FUN(CmdBindIndexBuffer),
323 : : PL_VK_DEV_FUN(CmdBindPipeline),
324 : : PL_VK_DEV_FUN(CmdBindVertexBuffers),
325 : : PL_VK_DEV_FUN(CmdBlitImage),
326 : : PL_VK_DEV_FUN(CmdClearColorImage),
327 : : PL_VK_DEV_FUN(CmdCopyBuffer),
328 : : PL_VK_DEV_FUN(CmdCopyBufferToImage),
329 : : PL_VK_DEV_FUN(CmdCopyImage),
330 : : PL_VK_DEV_FUN(CmdCopyImageToBuffer),
331 : : PL_VK_DEV_FUN(CmdDispatch),
332 : : PL_VK_DEV_FUN(CmdDraw),
333 : : PL_VK_DEV_FUN(CmdDrawIndexed),
334 : : PL_VK_DEV_FUN(CmdEndDebugUtilsLabelEXT),
335 : : PL_VK_DEV_FUN(CmdEndRenderPass),
336 : : PL_VK_DEV_FUN(CmdPipelineBarrier),
337 : : PL_VK_DEV_FUN(CmdPushConstants),
338 : : PL_VK_DEV_FUN(CmdResetQueryPool),
339 : : PL_VK_DEV_FUN(CmdSetScissor),
340 : : PL_VK_DEV_FUN(CmdSetViewport),
341 : : PL_VK_DEV_FUN(CmdUpdateBuffer),
342 : : PL_VK_DEV_FUN(CmdWriteTimestamp),
343 : : PL_VK_DEV_FUN(CreateBuffer),
344 : : PL_VK_DEV_FUN(CreateBufferView),
345 : : PL_VK_DEV_FUN(CreateCommandPool),
346 : : PL_VK_DEV_FUN(CreateComputePipelines),
347 : : PL_VK_DEV_FUN(CreateDescriptorPool),
348 : : PL_VK_DEV_FUN(CreateDescriptorSetLayout),
349 : : PL_VK_DEV_FUN(CreateFence),
350 : : PL_VK_DEV_FUN(CreateFramebuffer),
351 : : PL_VK_DEV_FUN(CreateGraphicsPipelines),
352 : : PL_VK_DEV_FUN(CreateImage),
353 : : PL_VK_DEV_FUN(CreateImageView),
354 : : PL_VK_DEV_FUN(CreatePipelineCache),
355 : : PL_VK_DEV_FUN(CreatePipelineLayout),
356 : : PL_VK_DEV_FUN(CreateQueryPool),
357 : : PL_VK_DEV_FUN(CreateRenderPass),
358 : : PL_VK_DEV_FUN(CreateSampler),
359 : : PL_VK_DEV_FUN(CreateSemaphore),
360 : : PL_VK_DEV_FUN(CreateShaderModule),
361 : : PL_VK_DEV_FUN(DestroyBuffer),
362 : : PL_VK_DEV_FUN(DestroyBufferView),
363 : : PL_VK_DEV_FUN(DestroyCommandPool),
364 : : PL_VK_DEV_FUN(DestroyDescriptorPool),
365 : : PL_VK_DEV_FUN(DestroyDescriptorSetLayout),
366 : : PL_VK_DEV_FUN(DestroyDevice),
367 : : PL_VK_DEV_FUN(DestroyFence),
368 : : PL_VK_DEV_FUN(DestroyFramebuffer),
369 : : PL_VK_DEV_FUN(DestroyImage),
370 : : PL_VK_DEV_FUN(DestroyImageView),
371 : : PL_VK_DEV_FUN(DestroyPipeline),
372 : : PL_VK_DEV_FUN(DestroyPipelineCache),
373 : : PL_VK_DEV_FUN(DestroyPipelineLayout),
374 : : PL_VK_DEV_FUN(DestroyQueryPool),
375 : : PL_VK_DEV_FUN(DestroyRenderPass),
376 : : PL_VK_DEV_FUN(DestroySampler),
377 : : PL_VK_DEV_FUN(DestroySemaphore),
378 : : PL_VK_DEV_FUN(DestroyShaderModule),
379 : : PL_VK_DEV_FUN(DeviceWaitIdle),
380 : : PL_VK_DEV_FUN(EndCommandBuffer),
381 : : PL_VK_DEV_FUN(FlushMappedMemoryRanges),
382 : : PL_VK_DEV_FUN(FreeCommandBuffers),
383 : : PL_VK_DEV_FUN(FreeMemory),
384 : : PL_VK_DEV_FUN(GetBufferMemoryRequirements),
385 : : PL_VK_DEV_FUN(GetDeviceQueue),
386 : : PL_VK_DEV_FUN(GetImageMemoryRequirements2),
387 : : PL_VK_DEV_FUN(GetImageSubresourceLayout),
388 : : PL_VK_DEV_FUN(GetPipelineCacheData),
389 : : PL_VK_DEV_FUN(GetQueryPoolResults),
390 : : PL_VK_DEV_FUN(InvalidateMappedMemoryRanges),
391 : : PL_VK_DEV_FUN(MapMemory),
392 : : PL_VK_DEV_FUN(QueueSubmit),
393 : : PL_VK_DEV_FUN(QueueWaitIdle),
394 : : PL_VK_DEV_FUN(ResetFences),
395 : : PL_VK_DEV_FUN(ResetQueryPool),
396 : : PL_VK_DEV_FUN(SetDebugUtilsObjectNameEXT),
397 : : PL_VK_DEV_FUN(UpdateDescriptorSets),
398 : : PL_VK_DEV_FUN(WaitForFences),
399 : : PL_VK_DEV_FUN(WaitSemaphores),
400 : : };
401 : :
402 : 351 : static void load_vk_fun(struct vk_ctx *vk, const struct vk_fun *fun)
403 : : {
404 : 351 : PFN_vkVoidFunction *pfn = (void *) ((uintptr_t) vk + (ptrdiff_t) fun->offset);
405 : :
406 [ + + ]: 351 : if (fun->device_level) {
407 : 300 : *pfn = vk->GetDeviceProcAddr(vk->dev, fun->name);
408 : : } else {
409 : 51 : *pfn = vk->GetInstanceProcAddr(vk->inst, fun->name);
410 : : };
411 : :
412 [ + + ]: 351 : if (!*pfn) {
413 : : // Some functions get their extension suffix stripped when promoted
414 : : // to core. As a very simple work-around to this, try loading the
415 : : // function a second time with the reserved suffixes stripped.
416 : : static const char *ext_suffixes[] = { "KHR", "EXT" };
417 [ + - ]: 15 : pl_str fun_name = pl_str0(fun->name);
418 : : char buf[64];
419 : :
420 [ + - ]: 15 : for (int i = 0; i < PL_ARRAY_SIZE(ext_suffixes); i++) {
421 [ - + ]: 15 : if (!pl_str_eatend0(&fun_name, ext_suffixes[i]))
422 : : continue;
423 : :
424 [ - + ]: 15 : pl_assert(sizeof(buf) > fun_name.len);
425 [ + - + + ]: 30 : snprintf(buf, sizeof(buf), "%.*s", PL_STR_FMT(fun_name));
426 [ + + ]: 15 : if (fun->device_level) {
427 : 6 : *pfn = vk->GetDeviceProcAddr(vk->dev, buf);
428 : : } else {
429 : 9 : *pfn = vk->GetInstanceProcAddr(vk->inst, buf);
430 : : }
431 : 15 : return;
432 : : }
433 : : }
434 : : }
435 : :
436 : : // Private struct for pl_vk_inst
437 : : struct priv {
438 : : VkDebugUtilsMessengerEXT debug_utils_cb;
439 : : };
440 : :
441 : 4 : void pl_vk_inst_destroy(pl_vk_inst *inst_ptr)
442 : : {
443 : 4 : pl_vk_inst inst = *inst_ptr;
444 [ + + ]: 4 : if (!inst)
445 : : return;
446 : :
447 : 1 : struct priv *p = PL_PRIV(inst);
448 [ + - ]: 1 : if (p->debug_utils_cb) {
449 : 1 : PL_VK_LOAD_FUN(inst->instance, DestroyDebugUtilsMessengerEXT, inst->get_proc_addr);
450 : 1 : DestroyDebugUtilsMessengerEXT(inst->instance, p->debug_utils_cb, PL_VK_ALLOC);
451 : : }
452 : :
453 : 1 : PL_VK_LOAD_FUN(inst->instance, DestroyInstance, inst->get_proc_addr);
454 : 1 : DestroyInstance(inst->instance, PL_VK_ALLOC);
455 : 1 : pl_free_ptr((void **) inst_ptr);
456 : : }
457 : :
458 : 386 : static VkBool32 VKAPI_PTR vk_dbg_utils_cb(VkDebugUtilsMessageSeverityFlagBitsEXT sev,
459 : : VkDebugUtilsMessageTypeFlagsEXT msgType,
460 : : const VkDebugUtilsMessengerCallbackDataEXT *data,
461 : : void *priv)
462 : : {
463 : : pl_log log = priv;
464 : :
465 : : // Ignore errors for messages that we consider false positives
466 [ - + + ]: 386 : switch (data->messageIdNumber) {
467 : : case 0x7cd0911d: // VUID-VkSwapchainCreateInfoKHR-imageExtent-01274
468 : : case 0x8928392f: // UNASSIGNED-BestPractices-NonSuccess-Result
469 : : case 0xdc18ad6b: // UNASSIGNED-BestPractices-vkAllocateMemory-small-allocation
470 : : case 0xb3d4346b: // UNASSIGNED-BestPractices-vkBindMemory-small-dedicated-allocation
471 : : case 0x6cfe18a5: // UNASSIGNED-BestPractices-SemaphoreCount
472 : : case 0x48a09f6c: // UNASSIGNED-BestPractices-pipeline-stage-flags
473 : : // profile chain expectations
474 : : case 0x30f4ac70: // VUID-VkImageCreateInfo-pNext-06811
475 : : return false;
476 : :
477 : 0 : case 0x5f379b89: // UNASSIGNED-BestPractices-Error-Result
478 [ # # ]: 0 : if (strstr(data->pMessage, "VK_ERROR_FORMAT_NOT_SUPPORTED"))
479 : : return false;
480 : : break;
481 : :
482 : : case 0xf6a37cfa: // VUID-vkGetImageSubresourceLayout-format-04461
483 : : // Work around https://github.com/KhronosGroup/Vulkan-Docs/issues/2109
484 : : return false;
485 : :
486 : : case 0x54023d1d: // VUID-VkDescriptorSetLayoutCreateInfo-flags-00281
487 : : // Work around https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/9542
488 : : return false;
489 : : }
490 : :
491 : : enum pl_log_level lev;
492 [ + + + - : 376 : switch (sev) {
+ ]
493 : : case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: lev = PL_LOG_ERR; break;
494 : 58 : case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: lev = PL_LOG_WARN; break;
495 : 254 : case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: lev = PL_LOG_DEBUG; break;
496 : 58 : case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: lev = PL_LOG_TRACE; break;
497 : 0 : default: lev = PL_LOG_INFO; break;
498 : : }
499 : :
500 : 376 : pl_msg(log, lev, "vk %s", data->pMessage);
501 : :
502 [ - + ]: 376 : for (int i = 0; i < data->queueLabelCount; i++)
503 : 0 : pl_msg(log, lev, " during %s", data->pQueueLabels[i].pLabelName);
504 [ + + ]: 416 : for (int i = 0; i < data->cmdBufLabelCount; i++)
505 : 40 : pl_msg(log, lev, " inside %s", data->pCmdBufLabels[i].pLabelName);
506 [ + + ]: 712 : for (int i = 0; i < data->objectCount; i++) {
507 : 336 : const VkDebugUtilsObjectNameInfoEXT *obj = &data->pObjects[i];
508 : 336 : pl_msg(log, lev, " using %s: %s (0x%llx)",
509 : 336 : vk_obj_type(obj->objectType),
510 : 336 : obj->pObjectName ? obj->pObjectName : "anon",
511 [ + + ]: 336 : (unsigned long long) obj->objectHandle);
512 : : }
513 : :
514 : : // The return value of this function determines whether the call will
515 : : // be explicitly aborted (to prevent GPU errors) or not. In this case,
516 : : // we generally want this to be on for the validation errors, but nothing
517 : : // else (e.g. performance warnings)
518 [ + + ]: 376 : bool is_error = (sev & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) &&
519 [ - + ]: 6 : (msgType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT);
520 : :
521 : : if (is_error) {
522 : 0 : pl_log_stack_trace(log, lev);
523 : 0 : pl_debug_abort();
524 : : return true;
525 : : }
526 : :
527 : : return false;
528 : : }
529 : :
530 : : static PFN_vkGetInstanceProcAddr get_proc_addr_fallback(pl_log log,
531 : : PFN_vkGetInstanceProcAddr get_proc_addr)
532 : : {
533 : 6 : if (get_proc_addr)
534 : 3 : return get_proc_addr;
535 : :
536 : : #ifdef PL_HAVE_VK_PROC_ADDR
537 : : return vkGetInstanceProcAddr;
538 : : #else
539 : : pl_fatal(log, "No `vkGetInstanceProcAddr` function provided, and "
540 : : "libplacebo built without linking against this function!");
541 : : return NULL;
542 : : #endif
543 : : }
544 : :
545 : : #define PRINTF_VER(ver) \
546 : : (int) VK_API_VERSION_MAJOR(ver), \
547 : : (int) VK_API_VERSION_MINOR(ver), \
548 : : (int) VK_API_VERSION_PATCH(ver)
549 : :
550 : 1 : pl_vk_inst pl_vk_inst_create(pl_log log, const struct pl_vk_inst_params *params)
551 : : {
552 : 1 : void *tmp = pl_tmp(NULL);
553 [ - + ]: 1 : params = PL_DEF(params, &pl_vk_inst_default_params);
554 : 1 : VkInstance inst = NULL;
555 : : pl_clock_t start;
556 : :
557 : : PL_ARRAY(const char *) exts = {0};
558 : :
559 : : PFN_vkGetInstanceProcAddr get_addr;
560 [ + - ]: 1 : if (!(get_addr = get_proc_addr_fallback(log, params->get_proc_addr)))
561 : : goto error;
562 : :
563 : : // Query instance version support
564 : 1 : uint32_t api_ver = VK_API_VERSION_1_0;
565 : 1 : PL_VK_LOAD_FUN(NULL, EnumerateInstanceVersion, get_addr);
566 [ + - - + ]: 1 : if (EnumerateInstanceVersion && EnumerateInstanceVersion(&api_ver) != VK_SUCCESS)
567 : 0 : goto error;
568 : :
569 : 1 : pl_debug(log, "Available instance version: %d.%d.%d", PRINTF_VER(api_ver));
570 : :
571 [ - + ]: 1 : if (params->max_api_version) {
572 : 0 : api_ver = PL_MIN(api_ver, params->max_api_version);
573 : 0 : pl_info(log, "Restricting API version to %d.%d.%d... new version %d.%d.%d",
574 : : PRINTF_VER(params->max_api_version), PRINTF_VER(api_ver));
575 : : }
576 : :
577 [ - + ]: 1 : if (api_ver < PL_VK_MIN_VERSION) {
578 : 0 : pl_fatal(log, "Instance API version %d.%d.%d is lower than the minimum "
579 : : "required version of %d.%d.%d, cannot proceed!",
580 : : PRINTF_VER(api_ver), PRINTF_VER(PL_VK_MIN_VERSION));
581 : 0 : goto error;
582 : : }
583 : :
584 : 1 : VkInstanceCreateInfo info = {
585 : : .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
586 : 1 : .pApplicationInfo = &(VkApplicationInfo) {
587 : : .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
588 : : .apiVersion = api_ver,
589 : : },
590 : : };
591 : :
592 : : // Enumerate all supported layers
593 : : start = pl_clock_now();
594 : 1 : PL_VK_LOAD_FUN(NULL, EnumerateInstanceLayerProperties, get_addr);
595 : 1 : uint32_t num_layers_avail = 0;
596 : 1 : EnumerateInstanceLayerProperties(&num_layers_avail, NULL);
597 : 1 : VkLayerProperties *layers_avail = pl_calloc_ptr(tmp, num_layers_avail, layers_avail);
598 : 1 : EnumerateInstanceLayerProperties(&num_layers_avail, layers_avail);
599 : 1 : pl_log_cpu_time(log, start, pl_clock_now(), "enumerating instance layers");
600 : :
601 : 1 : pl_debug(log, "Available layers:");
602 [ + + ]: 8 : for (int i = 0; i < num_layers_avail; i++) {
603 : 7 : pl_debug(log, " %s (v%d.%d.%d)", layers_avail[i].layerName,
604 : : PRINTF_VER(layers_avail[i].specVersion));
605 : : }
606 : :
607 : : PL_ARRAY(const char *) layers = {0};
608 : :
609 : :
610 : : // This layer has to be initialized first, otherwise all sorts of weirdness
611 : : // happens (random segfaults, yum)
612 : 1 : bool debug = params->debug;
613 : : uint32_t debug_layer = 0; // layer idx of debug layer
614 : : uint32_t debug_layer_version = 0;
615 [ - + ]: 1 : if (debug) {
616 [ + - ]: 3 : for (int n = 0; n < num_layers_avail; n++) {
617 : : const char * const layer = "VK_LAYER_KHRONOS_validation";
618 [ + + ]: 3 : if (strcmp(layer, layers_avail[n].layerName) != 0)
619 : : continue;
620 : :
621 : : debug_layer = n;
622 : 1 : debug_layer_version = layers_avail[n].specVersion;
623 : 1 : pl_info(log, "Enabling debug layer: %s (v%d.%d.%d)",
624 : : layer, PRINTF_VER(debug_layer_version));
625 [ + - ]: 1 : PL_ARRAY_APPEND(tmp, layers, layer);
626 : 1 : goto debug_layers_done;
627 : : }
628 : :
629 : : // No layer found..
630 : 0 : pl_warn(log, "API debugging requested but no debug meta layers present... ignoring");
631 : : debug = false;
632 : : }
633 : :
634 : 0 : debug_layers_done: ;
635 : :
636 [ - + ]: 1 : for (int i = 0; i < params->num_layers; i++)
637 [ # # # # : 0 : PL_ARRAY_APPEND(tmp, layers, params->layers[i]);
# # ]
638 : :
639 [ - + ]: 1 : for (int i = 0; i < params->num_opt_layers; i++) {
640 : 0 : const char *layer = params->opt_layers[i];
641 [ # # ]: 0 : for (int n = 0; n < num_layers_avail; n++) {
642 [ # # ]: 0 : if (strcmp(layer, layers_avail[n].layerName) == 0) {
643 [ # # # # : 0 : PL_ARRAY_APPEND(tmp, layers, layer);
# # ]
644 : 0 : break;
645 : : }
646 : : }
647 : : }
648 : :
649 : : // Enumerate all supported extensions
650 : : start = pl_clock_now();
651 : 1 : PL_VK_LOAD_FUN(NULL, EnumerateInstanceExtensionProperties, get_addr);
652 : 1 : uint32_t num_exts_avail = 0;
653 : 1 : EnumerateInstanceExtensionProperties(NULL, &num_exts_avail, NULL);
654 : 1 : VkExtensionProperties *exts_avail = pl_calloc_ptr(tmp, num_exts_avail, exts_avail);
655 : 1 : EnumerateInstanceExtensionProperties(NULL, &num_exts_avail, exts_avail);
656 : :
657 : : struct {
658 : : VkExtensionProperties *exts;
659 : : uint32_t num_exts;
660 : 1 : } *layer_exts = pl_calloc_ptr(tmp, num_layers_avail, layer_exts);
661 : :
662 : : // Enumerate extensions from layers
663 [ + + ]: 8 : for (int i = 0; i < num_layers_avail; i++) {
664 : 7 : VkExtensionProperties **lexts = &layer_exts[i].exts;
665 : 7 : uint32_t *num = &layer_exts[i].num_exts;
666 : :
667 : 7 : EnumerateInstanceExtensionProperties(layers_avail[i].layerName, num, NULL);
668 : 7 : *lexts = pl_calloc_ptr(tmp, *num, *lexts);
669 : 7 : EnumerateInstanceExtensionProperties(layers_avail[i].layerName, num, *lexts);
670 : :
671 : : // Replace all extensions that are already available globally by {0}
672 [ + + ]: 14 : for (int j = 0; j < *num; j++) {
673 [ + + ]: 175 : for (int k = 0; k < num_exts_avail; k++) {
674 [ + + ]: 168 : if (strcmp((*lexts)[j].extensionName, exts_avail[k].extensionName) == 0)
675 : 2 : (*lexts)[j] = (VkExtensionProperties) {0};
676 : : }
677 : : }
678 : : }
679 : :
680 : 1 : pl_log_cpu_time(log, start, pl_clock_now(), "enumerating instance extensions");
681 : 1 : pl_debug(log, "Available instance extensions:");
682 [ + + ]: 25 : for (int i = 0; i < num_exts_avail; i++)
683 : 24 : pl_debug(log, " %s", exts_avail[i].extensionName);
684 [ + + ]: 8 : for (int i = 0; i < num_layers_avail; i++) {
685 [ + + ]: 14 : for (int j = 0; j < layer_exts[i].num_exts; j++) {
686 [ + + ]: 7 : if (!layer_exts[i].exts[j].extensionName[0])
687 : 2 : continue;
688 : :
689 : 5 : pl_debug(log, " %s (via %s)",
690 : : layer_exts[i].exts[j].extensionName,
691 : : layers_avail[i].layerName);
692 : : }
693 : : }
694 : :
695 : : // Add optional extensions
696 [ + + ]: 4 : for (int i = 0; i < PL_ARRAY_SIZE(vk_instance_extensions); i++) {
697 : 3 : const char *ext = vk_instance_extensions[i];
698 [ + - ]: 39 : for (int n = 0; n < num_exts_avail; n++) {
699 [ + + ]: 39 : if (strcmp(ext, exts_avail[n].extensionName) == 0) {
700 [ + + - + : 3 : PL_ARRAY_APPEND(tmp, exts, ext);
- + ]
701 : 3 : break;
702 : : }
703 : : }
704 : : }
705 : :
706 : : #ifdef VK_KHR_portability_enumeration
707 : : // Required for macOS ( MoltenVK ) compatibility
708 [ + - ]: 23 : for (int n = 0; n < num_exts_avail; n++) {
709 [ + + ]: 23 : if (strcmp(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, exts_avail[n].extensionName) == 0) {
710 [ - + - + : 1 : PL_ARRAY_APPEND(tmp, exts, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
- + ]
711 : 1 : info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
712 : 1 : break;
713 : : }
714 : : }
715 : : #endif
716 : :
717 : : // Add extra user extensions
718 [ - + ]: 1 : for (int i = 0; i < params->num_extensions; i++) {
719 : 0 : const char *ext = params->extensions[i];
720 [ # # # # : 0 : PL_ARRAY_APPEND(tmp, exts, ext);
# # ]
721 : :
722 : : // Enable any additional layers that are required for this extension
723 [ # # ]: 0 : for (int n = 0; n < num_layers_avail; n++) {
724 [ # # ]: 0 : for (int j = 0; j < layer_exts[n].num_exts; j++) {
725 [ # # ]: 0 : if (!layer_exts[n].exts[j].extensionName[0])
726 : 0 : continue;
727 [ # # ]: 0 : if (strcmp(ext, layer_exts[n].exts[j].extensionName) == 0) {
728 [ # # # # : 0 : PL_ARRAY_APPEND(tmp, layers, layers_avail[n].layerName);
# # ]
729 : 0 : goto next_user_ext;
730 : : }
731 : : }
732 : : }
733 : :
734 : 0 : next_user_ext: ;
735 : : }
736 : :
737 : : // Add extra optional user extensions
738 [ + + ]: 3 : for (int i = 0; i < params->num_opt_extensions; i++) {
739 : 2 : const char *ext = params->opt_extensions[i];
740 [ + - ]: 29 : for (int n = 0; n < num_exts_avail; n++) {
741 [ + + ]: 29 : if (strcmp(ext, exts_avail[n].extensionName) == 0) {
742 [ - + - + : 2 : PL_ARRAY_APPEND(tmp, exts, ext);
- + ]
743 : 2 : goto next_opt_user_ext;
744 : : }
745 : : }
746 : :
747 [ # # ]: 0 : for (int n = 0; n < num_layers_avail; n++) {
748 [ # # ]: 0 : for (int j = 0; j < layer_exts[n].num_exts; j++) {
749 [ # # ]: 0 : if (!layer_exts[n].exts[j].extensionName[0])
750 : 0 : continue;
751 [ # # ]: 0 : if (strcmp(ext, layer_exts[n].exts[j].extensionName) == 0) {
752 [ # # # # : 0 : PL_ARRAY_APPEND(tmp, exts, ext);
# # ]
753 [ # # # # : 0 : PL_ARRAY_APPEND(tmp, layers, layers_avail[n].layerName);
# # ]
754 : 0 : goto next_opt_user_ext;
755 : : }
756 : : }
757 : : }
758 : :
759 : 2 : next_opt_user_ext: ;
760 : : }
761 : :
762 : 1 : const VkDebugUtilsMessengerCreateInfoEXT dinfo = {
763 : : .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
764 : : .messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
765 : : VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
766 : : VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
767 : : VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
768 : : .messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
769 : : VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
770 : : VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
771 : : .pfnUserCallback = vk_dbg_utils_cb,
772 : : .pUserData = (void *) log,
773 : : };
774 : :
775 : : // If debugging is enabled, load the necessary debug utils extension
776 [ - + ]: 1 : if (debug) {
777 : : const char * const ext = VK_EXT_DEBUG_UTILS_EXTENSION_NAME;
778 [ + - ]: 17 : for (int n = 0; n < num_exts_avail; n++) {
779 [ + + ]: 17 : if (strcmp(ext, exts_avail[n].extensionName) == 0) {
780 [ - + - + : 1 : PL_ARRAY_APPEND(tmp, exts, ext);
- + ]
781 : 1 : vk_link_struct(&info, &dinfo);
782 : 1 : goto debug_ext_done;
783 : : }
784 : : }
785 : :
786 [ # # ]: 0 : for (int n = 0; n < layer_exts[debug_layer].num_exts; n++) {
787 [ # # ]: 0 : if (strcmp(ext, layer_exts[debug_layer].exts[n].extensionName) == 0) {
788 [ # # # # : 0 : PL_ARRAY_APPEND(tmp, exts, ext);
# # ]
789 : 0 : vk_link_struct(&info, &dinfo);
790 : 0 : goto debug_ext_done;
791 : : }
792 : : }
793 : :
794 : : // No extension found
795 : 0 : pl_warn(log, "API debug layers enabled but no debug report extension "
796 : : "found... ignoring. Debug messages may be spilling to "
797 : : "stdout/stderr!");
798 : : debug = false;
799 : : }
800 : :
801 : 0 : debug_ext_done: ;
802 : :
803 : : #define ENABLE_BOOL(name) \
804 : : {"VK_LAYER_KHRONOS_validation", name, VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &(VkBool32){VK_TRUE}}
805 : 1 : const VkLayerSettingEXT debug_settings[] = {
806 : 1 : ENABLE_BOOL("validate_best_practices"),
807 : :
808 : 1 : ENABLE_BOOL("gpuav_enable"),
809 : 1 : ENABLE_BOOL("gpuav_image_layout"),
810 : 1 : ENABLE_BOOL("gpuav_debug_validate_instrumented_shaders"),
811 : :
812 : 1 : ENABLE_BOOL("validate_sync"),
813 : 1 : ENABLE_BOOL("syncval_shader_accesses_heuristic"),
814 : 1 : ENABLE_BOOL("syncval_submit_time_validation"),
815 : : };
816 : :
817 : 1 : const VkLayerSettingsCreateInfoEXT debug_layer_settings = {
818 : : .sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT,
819 : : .settingCount = PL_ARRAY_SIZE(debug_settings),
820 : : .pSettings = debug_settings,
821 : : };
822 : :
823 : : // Limit this to 1.3.296+ because of bugs in older versions.
824 [ - + - + : 1 : if (debug && params->debug_extra &&
- + ]
825 : : debug_layer_version >= VK_MAKE_API_VERSION(0, 1, 3, 296))
826 : : {
827 : : const char * const ext = VK_EXT_LAYER_SETTINGS_EXTENSION_NAME;
828 [ + + ]: 25 : for (int n = 0; n < num_exts_avail; n++) {
829 [ - + ]: 24 : if (strcmp(ext, exts_avail[n].extensionName) == 0) {
830 [ # # # # : 0 : PL_ARRAY_APPEND(tmp, exts, ext);
# # ]
831 : 0 : vk_link_struct(&info, &debug_layer_settings);
832 : 0 : goto debug_extra_ext_done;
833 : : }
834 : : }
835 : :
836 [ + - ]: 3 : for (int n = 0; n < layer_exts[debug_layer].num_exts; n++) {
837 [ + + ]: 3 : if (strcmp(ext, layer_exts[debug_layer].exts[n].extensionName) == 0) {
838 [ - + - + : 1 : PL_ARRAY_APPEND(tmp, exts, ext);
- + ]
839 : 1 : vk_link_struct(&info, &debug_layer_settings);
840 : 1 : goto debug_extra_ext_done;
841 : : }
842 : : }
843 : :
844 : 0 : pl_warn(log, "%s not available, debug_extra options were not applied.",
845 : : VK_EXT_LAYER_SETTINGS_EXTENSION_NAME);
846 : : }
847 : :
848 : 0 : debug_extra_ext_done: ;
849 : :
850 : 1 : info.ppEnabledExtensionNames = exts.elem;
851 : 1 : info.enabledExtensionCount = exts.num;
852 : 1 : info.ppEnabledLayerNames = layers.elem;
853 : 1 : info.enabledLayerCount = layers.num;
854 : :
855 [ - + ]: 1 : pl_info(log, "Creating vulkan instance%s", exts.num ? " with extensions:" : "");
856 [ + + ]: 9 : for (int i = 0; i < exts.num; i++)
857 : 8 : pl_info(log, " %s", exts.elem[i]);
858 : :
859 [ + - ]: 1 : if (layers.num) {
860 : 1 : pl_info(log, " and layers:");
861 [ + + ]: 2 : for (int i = 0; i < layers.num; i++)
862 : 1 : pl_info(log, " %s", layers.elem[i]);
863 : : }
864 : :
865 : : start = pl_clock_now();
866 : 1 : PL_VK_LOAD_FUN(NULL, CreateInstance, get_addr);
867 : 1 : VkResult res = CreateInstance(&info, PL_VK_ALLOC, &inst);
868 : 1 : pl_log_cpu_time(log, start, pl_clock_now(), "creating vulkan instance");
869 [ - + ]: 1 : if (res != VK_SUCCESS) {
870 : 0 : pl_fatal(log, "Failed creating instance: %s", vk_res_str(res));
871 : 0 : goto error;
872 : : }
873 : :
874 : 1 : struct pl_vk_inst_t *pl_vk = pl_zalloc_obj(NULL, pl_vk, struct priv);
875 : 1 : struct priv *p = PL_PRIV(pl_vk);
876 : 2 : *pl_vk = (struct pl_vk_inst_t) {
877 : : .instance = inst,
878 : : .api_version = api_ver,
879 : : .get_proc_addr = get_addr,
880 : 1 : .extensions = pl_steal(pl_vk, exts.elem),
881 : : .num_extensions = exts.num,
882 : 1 : .layers = pl_steal(pl_vk, layers.elem),
883 : : .num_layers = layers.num,
884 : : };
885 : :
886 : : // Set up a debug callback to catch validation messages
887 [ + - ]: 1 : if (debug) {
888 : 1 : PL_VK_LOAD_FUN(inst, CreateDebugUtilsMessengerEXT, get_addr);
889 : 1 : CreateDebugUtilsMessengerEXT(inst, &dinfo, PL_VK_ALLOC, &p->debug_utils_cb);
890 : : }
891 : :
892 : 1 : pl_free(tmp);
893 : 1 : return pl_vk;
894 : :
895 : 0 : error:
896 : 0 : pl_fatal(log, "Failed initializing vulkan instance");
897 [ # # ]: 0 : if (inst) {
898 : 0 : PL_VK_LOAD_FUN(inst, DestroyInstance, get_addr);
899 : 0 : DestroyInstance(inst, PL_VK_ALLOC);
900 : : }
901 : 0 : pl_free(tmp);
902 : 0 : return NULL;
903 : : }
904 : :
905 : : const struct pl_vulkan_params pl_vulkan_default_params = { PL_VULKAN_DEFAULTS };
906 : :
907 : 3 : void pl_vulkan_destroy(pl_vulkan *pl_vk)
908 : : {
909 [ + - ]: 3 : if (!*pl_vk)
910 : : return;
911 : :
912 : 3 : struct vk_ctx *vk = PL_PRIV(*pl_vk);
913 [ + - ]: 3 : if (vk->dev) {
914 [ + - ]: 3 : if ((*pl_vk)->gpu) {
915 : 3 : PL_DEBUG(vk, "Waiting for remaining commands...");
916 : 3 : pl_gpu_finish((*pl_vk)->gpu);
917 [ - + ]: 3 : pl_assert(vk->cmds_pending.num == 0);
918 : :
919 : 3 : pl_gpu_destroy((*pl_vk)->gpu);
920 : : }
921 : 3 : vk_malloc_destroy(&vk->ma);
922 [ + + ]: 6 : for (int i = 0; i < vk->pools.num; i++)
923 : 3 : vk_cmdpool_destroy(vk->pools.elem[i]);
924 : :
925 [ + + ]: 3 : if (!vk->imported)
926 : 2 : vk->DestroyDevice(vk->dev, PL_VK_ALLOC);
927 : : }
928 : :
929 [ + + ]: 6 : for (int i = 0; i < vk->queue_locks.num; i++) {
930 [ + + ]: 6 : for (int n = 0; n < vk->queue_locks.elem[i].num; n++)
931 : 3 : pl_mutex_destroy(&vk->queue_locks.elem[i].elem[n]);
932 : : }
933 : :
934 : 3 : pl_vk_inst_destroy(&vk->internal_instance);
935 : 3 : pl_mutex_destroy(&vk->lock);
936 : 3 : pl_free_ptr((void **) pl_vk);
937 : : }
938 : :
939 : 2 : static bool supports_surf(pl_log log, VkInstance inst,
940 : : PFN_vkGetInstanceProcAddr get_addr,
941 : : VkPhysicalDevice physd, VkSurfaceKHR surf)
942 : : {
943 : : // Hack for the VK macro's logging to work
944 : : struct { pl_log log; } *vk = (void *) &log;
945 : :
946 : 2 : PL_VK_LOAD_FUN(inst, GetPhysicalDeviceQueueFamilyProperties, get_addr);
947 : 2 : PL_VK_LOAD_FUN(inst, GetPhysicalDeviceSurfaceSupportKHR, get_addr);
948 : 2 : uint32_t qfnum = 0;
949 : 2 : GetPhysicalDeviceQueueFamilyProperties(physd, &qfnum, NULL);
950 : :
951 [ + - ]: 2 : for (int i = 0; i < qfnum; i++) {
952 : 2 : VkBool32 sup = false;
953 [ - + ]: 2 : VK(GetPhysicalDeviceSurfaceSupportKHR(physd, i, surf, &sup));
954 [ + - ]: 2 : if (sup)
955 : 2 : return true;
956 : : }
957 : :
958 : 0 : error:
959 : : return false;
960 : : }
961 : :
962 : 2 : VkPhysicalDevice pl_vulkan_choose_device(pl_log log,
963 : : const struct pl_vulkan_device_params *params)
964 : : {
965 : : // Hack for the VK macro's logging to work
966 : : struct { pl_log log; } *vk = (void *) &log;
967 : 2 : PL_INFO(vk, "Probing for vulkan devices:");
968 : :
969 [ - + ]: 2 : pl_assert(params->instance);
970 : : VkInstance inst = params->instance;
971 : : VkPhysicalDevice dev = VK_NULL_HANDLE;
972 : :
973 : : PFN_vkGetInstanceProcAddr get_addr;
974 [ + - ]: 2 : if (!(get_addr = get_proc_addr_fallback(log, params->get_proc_addr)))
975 : : return NULL;
976 : :
977 : 2 : PL_VK_LOAD_FUN(inst, EnumeratePhysicalDevices, get_addr);
978 : 2 : PL_VK_LOAD_FUN(inst, GetPhysicalDeviceProperties2, get_addr);
979 [ - + ]: 2 : pl_assert(GetPhysicalDeviceProperties2);
980 : :
981 : : pl_clock_t start = pl_clock_now();
982 : : VkPhysicalDevice *devices = NULL;
983 : 2 : uint32_t num = 0;
984 [ - + ]: 2 : VK(EnumeratePhysicalDevices(inst, &num, NULL));
985 : 2 : devices = pl_calloc_ptr(NULL, num, devices);
986 [ - + ]: 2 : VK(EnumeratePhysicalDevices(inst, &num, devices));
987 : 2 : pl_log_cpu_time(log, start, pl_clock_now(), "enumerating physical devices");
988 : :
989 : : static const struct { const char *name; int priority; } types[] = {
990 : : [VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU] = {"discrete", 5},
991 : : [VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU] = {"integrated", 4},
992 : : [VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU] = {"virtual", 3},
993 : : [VK_PHYSICAL_DEVICE_TYPE_CPU] = {"software", 2},
994 : : [VK_PHYSICAL_DEVICE_TYPE_OTHER] = {"other", 1},
995 : : };
996 : :
997 : : static const uint8_t nil[VK_UUID_SIZE] = {0};
998 : 2 : bool uuid_set = memcmp(params->device_uuid, nil, VK_UUID_SIZE) != 0;
999 : :
1000 : : int best = -1;
1001 [ + + ]: 6 : for (int i = 0; i < num; i++) {
1002 : 4 : VkPhysicalDeviceIDPropertiesKHR id_props = {
1003 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR,
1004 : : };
1005 : :
1006 : 4 : VkPhysicalDeviceProperties2 prop = {
1007 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
1008 : : .pNext = &id_props,
1009 : : };
1010 : :
1011 : 4 : GetPhysicalDeviceProperties2(devices[i], &prop);
1012 : 4 : VkPhysicalDeviceType t = prop.properties.deviceType;
1013 [ + - ]: 4 : const char *dtype = t < PL_ARRAY_SIZE(types) ? types[t].name : "unknown?";
1014 : 4 : PL_INFO(vk, " GPU %d: %s v%d.%d.%d (%s)", i, prop.properties.deviceName,
1015 : : PRINTF_VER(prop.properties.apiVersion), dtype);
1016 : 4 : PL_INFO(vk, " uuid: %s", PRINT_UUID(id_props.deviceUUID));
1017 : :
1018 [ + + ]: 4 : if (params->surface) {
1019 [ - + ]: 2 : if (!supports_surf(log, inst, get_addr, devices[i], params->surface)) {
1020 : 0 : PL_DEBUG(vk, " -> excluding due to lack of surface support");
1021 : 2 : continue;
1022 : : }
1023 : : }
1024 : :
1025 [ - + ]: 4 : if (uuid_set) {
1026 [ # # ]: 0 : if (memcmp(id_props.deviceUUID, params->device_uuid, VK_UUID_SIZE) == 0) {
1027 : 0 : dev = devices[i];
1028 : 0 : continue;
1029 : : } else {
1030 : 0 : PL_DEBUG(vk, " -> excluding due to UUID mismatch");
1031 : 0 : continue;
1032 : : }
1033 [ + + + - ]: 4 : } else if (params->device_name && params->device_name[0] != '\0') {
1034 [ + + ]: 2 : if (strcmp(params->device_name, prop.properties.deviceName) == 0) {
1035 : 1 : dev = devices[i];
1036 : 1 : continue;
1037 : : } else {
1038 : 1 : PL_DEBUG(vk, " -> excluding due to name mismatch");
1039 : 1 : continue;
1040 : : }
1041 : : }
1042 : :
1043 [ - + - - ]: 2 : if (!params->allow_software && t == VK_PHYSICAL_DEVICE_TYPE_CPU) {
1044 : 0 : PL_DEBUG(vk, " -> excluding due to !params->allow_software");
1045 : 0 : continue;
1046 : : }
1047 : :
1048 [ - + ]: 2 : if (prop.properties.apiVersion < PL_VK_MIN_VERSION) {
1049 : 0 : PL_DEBUG(vk, " -> excluding due to too low API version");
1050 : 0 : continue;
1051 : : }
1052 : :
1053 [ + - ]: 2 : int priority = t < PL_ARRAY_SIZE(types) ? types[t].priority : 0;
1054 [ + + ]: 2 : if (priority > best) {
1055 : 1 : dev = devices[i];
1056 : : best = priority;
1057 : : }
1058 : : }
1059 : :
1060 : 2 : error:
1061 : 2 : pl_free(devices);
1062 : : return dev;
1063 : : }
1064 : :
1065 : 800 : static void lock_queue_internal(void *priv, uint32_t qf, uint32_t qidx)
1066 : : {
1067 : : struct vk_ctx *vk = priv;
1068 : 800 : pl_mutex_lock(&vk->queue_locks.elem[qf].elem[qidx]);
1069 : 800 : }
1070 : :
1071 : 800 : static void unlock_queue_internal(void *priv, uint32_t qf, uint32_t qidx)
1072 : : {
1073 : : struct vk_ctx *vk = priv;
1074 : 800 : pl_mutex_unlock(&vk->queue_locks.elem[qf].elem[qidx]);
1075 : 800 : }
1076 : :
1077 : 3 : static void init_queue_locks(struct vk_ctx *vk, uint32_t qfnum,
1078 : : const VkQueueFamilyProperties *qfs)
1079 : : {
1080 : 3 : vk->queue_locks.elem = pl_calloc_ptr(vk->alloc, qfnum, vk->queue_locks.elem);
1081 : 3 : vk->queue_locks.num = qfnum;
1082 [ + + ]: 6 : for (int i = 0; i < qfnum; i++) {
1083 : 3 : const uint32_t qnum = qfs[i].queueCount;
1084 : 3 : vk->queue_locks.elem[i].elem = pl_calloc(vk->alloc, qnum, sizeof(pl_mutex));
1085 : 3 : vk->queue_locks.elem[i].num = qnum;
1086 [ + + ]: 6 : for (int n = 0; n < qnum; n++)
1087 [ - + ]: 3 : pl_mutex_init(&vk->queue_locks.elem[i].elem[n]);
1088 : : }
1089 : :
1090 : 3 : vk->lock_queue = lock_queue_internal;
1091 : 3 : vk->unlock_queue = unlock_queue_internal;
1092 : 3 : vk->queue_ctx = vk;
1093 : 3 : }
1094 : :
1095 : : // Find the most specialized queue supported a combination of flags. In cases
1096 : : // where there are multiple queue families at the same specialization level,
1097 : : // this finds the one with the most queues. Returns -1 if no queue was found.
1098 : 6 : static int find_qf(VkQueueFamilyProperties *qfs, int qfnum, VkQueueFlags flags)
1099 : : {
1100 : : int idx = -1;
1101 [ + + ]: 12 : for (int i = 0; i < qfnum; i++) {
1102 [ - + ]: 6 : if ((qfs[i].queueFlags & flags) != flags)
1103 : 0 : continue;
1104 : :
1105 : : // QF is more specialized. Since we don't care about other bits like
1106 : : // SPARSE_BIT, mask the ones we're interestew in
1107 : : const VkQueueFlags mask = VK_QUEUE_GRAPHICS_BIT |
1108 : : VK_QUEUE_TRANSFER_BIT |
1109 : : VK_QUEUE_COMPUTE_BIT;
1110 : :
1111 [ - + - - ]: 6 : if (idx < 0 || (qfs[i].queueFlags & mask) < (qfs[idx].queueFlags & mask))
1112 : : idx = i;
1113 : :
1114 : : // QF has more queues (at the same specialization level)
1115 [ - + ]: 6 : if (qfs[i].queueFlags == qfs[idx].queueFlags &&
1116 [ + - ]: 6 : qfs[i].queueCount > qfs[idx].queueCount)
1117 : : idx = i;
1118 : : }
1119 : :
1120 : 6 : return idx;
1121 : : }
1122 : :
1123 : 2 : static bool device_init(struct vk_ctx *vk, const struct pl_vulkan_params *params)
1124 : : {
1125 [ - + ]: 2 : pl_assert(vk->physd);
1126 : 2 : void *tmp = pl_tmp(NULL);
1127 : :
1128 : : // Enumerate the queue families and find suitable families for each task
1129 : 2 : uint32_t qfnum = 0;
1130 : 2 : vk->GetPhysicalDeviceQueueFamilyProperties(vk->physd, &qfnum, NULL);
1131 : 2 : VkQueueFamilyProperties *qfs = pl_calloc_ptr(tmp, qfnum, qfs);
1132 : 2 : vk->GetPhysicalDeviceQueueFamilyProperties(vk->physd, &qfnum, qfs);
1133 : 2 : init_queue_locks(vk, qfnum, qfs);
1134 : :
1135 : 2 : PL_DEBUG(vk, "Queue families supported by device:");
1136 [ + + ]: 4 : for (int i = 0; i < qfnum; i++) {
1137 : 2 : PL_DEBUG(vk, " %d: flags 0x%"PRIx32" num %"PRIu32, i,
1138 : : qfs[i].queueFlags, qfs[i].queueCount);
1139 : : }
1140 : :
1141 : : VkQueueFlagBits gfx_flags = VK_QUEUE_GRAPHICS_BIT;
1142 [ + + ]: 2 : if (!params->async_compute)
1143 : : gfx_flags |= VK_QUEUE_COMPUTE_BIT;
1144 : :
1145 : 2 : int idx_gfx = find_qf(qfs, qfnum, gfx_flags);
1146 : 2 : int idx_comp = find_qf(qfs, qfnum, VK_QUEUE_COMPUTE_BIT);
1147 : 2 : int idx_tf = find_qf(qfs, qfnum, VK_QUEUE_TRANSFER_BIT);
1148 [ - + ]: 2 : if (idx_tf < 0)
1149 : : idx_tf = idx_comp;
1150 : :
1151 [ + + ]: 2 : if (!params->async_compute)
1152 : : idx_comp = idx_gfx;
1153 [ + + ]: 2 : if (!params->async_transfer)
1154 : : idx_tf = idx_gfx;
1155 : :
1156 : 2 : PL_DEBUG(vk, "Using graphics queue %d", idx_gfx);
1157 [ - + ]: 2 : if (idx_tf != idx_gfx)
1158 : 0 : PL_INFO(vk, "Using async transfer (queue %d)", idx_tf);
1159 [ - + ]: 2 : if (idx_comp != idx_gfx)
1160 : 0 : PL_INFO(vk, "Using async compute (queue %d)", idx_comp);
1161 : :
1162 : : // Vulkan requires at least one GRAPHICS+COMPUTE queue, so if this fails
1163 : : // something is horribly wrong.
1164 [ + - - + ]: 2 : pl_assert(idx_gfx >= 0 && idx_comp >= 0 && idx_tf >= 0);
1165 : :
1166 : : // If needed, ensure we can actually present to the surface using this queue
1167 [ + - ]: 2 : if (params->surface) {
1168 : 2 : VkBool32 sup = false;
1169 [ - + ]: 2 : VK(vk->GetPhysicalDeviceSurfaceSupportKHR(vk->physd, idx_gfx,
1170 : : params->surface, &sup));
1171 [ - + ]: 2 : if (!sup) {
1172 : 0 : PL_FATAL(vk, "Queue family does not support surface presentation!");
1173 : 0 : goto error;
1174 : : }
1175 : : }
1176 : :
1177 : : // Enumerate all supported extensions
1178 : : pl_clock_t start = pl_clock_now();
1179 : 2 : uint32_t num_exts_avail = 0;
1180 [ - + ]: 2 : VK(vk->EnumerateDeviceExtensionProperties(vk->physd, NULL, &num_exts_avail, NULL));
1181 : 2 : VkExtensionProperties *exts_avail = pl_calloc_ptr(tmp, num_exts_avail, exts_avail);
1182 [ - + ]: 2 : VK(vk->EnumerateDeviceExtensionProperties(vk->physd, NULL, &num_exts_avail, exts_avail));
1183 : 2 : pl_log_cpu_time(vk->log, start, pl_clock_now(), "enumerating device extensions");
1184 : :
1185 : 2 : PL_DEBUG(vk, "Available device extensions:");
1186 [ + + ]: 324 : for (int i = 0; i < num_exts_avail; i++)
1187 : 322 : PL_DEBUG(vk, " %s", exts_avail[i].extensionName);
1188 : :
1189 : : // Add all extensions we need
1190 [ + - ]: 2 : if (params->surface)
1191 [ + - - - : 2 : PL_ARRAY_APPEND(vk->alloc, vk->exts, VK_KHR_SWAPCHAIN_EXTENSION_NAME);
- - ]
1192 : :
1193 : : // Keep track of all optional function pointers associated with extensions
1194 : : PL_ARRAY(const struct vk_fun *) ext_funs = {0};
1195 : :
1196 : : // Add all optional device-level extensions extensions
1197 [ + + ]: 24 : for (int i = 0; i < PL_ARRAY_SIZE(vk_device_extensions); i++) {
1198 : : const struct vk_ext *ext = &vk_device_extensions[i];
1199 : 22 : uint32_t core_ver = vk_ext_promoted_ver(ext->name);
1200 [ + + + + ]: 22 : if (core_ver && vk->api_ver >= core_ver) {
1201 : : // Layer is already implicitly enabled by the API version
1202 [ + - + + ]: 6 : for (const struct vk_fun *f = ext->funs; f && f->name; f++)
1203 [ - + - + : 4 : PL_ARRAY_APPEND(tmp, ext_funs, f);
- + ]
1204 : 2 : continue;
1205 : : }
1206 : :
1207 [ + + ]: 1790 : for (int n = 0; n < num_exts_avail; n++) {
1208 [ + + ]: 1786 : if (strcmp(ext->name, exts_avail[n].extensionName) == 0) {
1209 [ - + - + : 16 : PL_ARRAY_APPEND(vk->alloc, vk->exts, ext->name);
- + ]
1210 [ + + + + ]: 38 : for (const struct vk_fun *f = ext->funs; f && f->name; f++)
1211 [ + + + + : 22 : PL_ARRAY_APPEND(tmp, ext_funs, f);
- + ]
1212 : : break;
1213 : : }
1214 : : }
1215 : : }
1216 : :
1217 : : // Add extra user extensions
1218 [ - + ]: 2 : for (int i = 0; i < params->num_extensions; i++)
1219 [ # # # # : 0 : PL_ARRAY_APPEND(vk->alloc, vk->exts, params->extensions[i]);
# # ]
1220 : :
1221 : : // Add optional extra user extensions
1222 [ - + ]: 2 : for (int i = 0; i < params->num_opt_extensions; i++) {
1223 : 0 : const char *ext = params->opt_extensions[i];
1224 [ # # ]: 0 : for (int n = 0; n < num_exts_avail; n++) {
1225 [ # # ]: 0 : if (strcmp(ext, exts_avail[n].extensionName) == 0) {
1226 [ # # # # : 0 : PL_ARRAY_APPEND(vk->alloc, vk->exts, ext);
# # ]
1227 : 0 : break;
1228 : : }
1229 : : }
1230 : : }
1231 : :
1232 : 2 : VkPhysicalDeviceFeatures2 features = {
1233 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR
1234 : : };
1235 : :
1236 : 2 : vk_features_normalize(tmp, &pl_vulkan_required_features, vk->api_ver, &features);
1237 : 2 : vk_features_normalize(tmp, &pl_vulkan_recommended_features, vk->api_ver, &features);
1238 : 2 : vk_features_normalize(tmp, params->features, vk->api_ver, &features);
1239 : :
1240 : : // Explicitly clear the features struct before querying feature support
1241 : : // from the driver. This way, we don't mistakenly mark as supported
1242 : : // features coming from structs the driver doesn't have support for.
1243 : 2 : VkPhysicalDeviceFeatures2 *features_sup = vk_chain_memdup(tmp, &features);;
1244 [ + + ]: 10 : for (VkBaseOutStructure *out = (void *) features_sup; out; out = out->pNext) {
1245 : 8 : const size_t size = vk_struct_size(out->sType);
1246 : 8 : memset(&out[1], 0, size - sizeof(out[0]));
1247 : : }
1248 : :
1249 : 2 : vk->GetPhysicalDeviceFeatures2KHR(vk->physd, features_sup);
1250 : :
1251 : : // Filter out unsupported features
1252 [ + + ]: 10 : for (VkBaseOutStructure *f = (VkBaseOutStructure *) &features; f; f = f->pNext) {
1253 : 8 : const VkBaseInStructure *sup = vk_find_struct(features_sup, f->sType);
1254 : : VkBool32 *flags = (VkBool32 *) &f[1];
1255 : : const VkBool32 *flags_sup = (const VkBool32 *) &sup[1];
1256 : 8 : const size_t size = vk_struct_size(f->sType) - sizeof(VkBaseOutStructure);
1257 [ + + ]: 272 : for (int i = 0; i < size / sizeof(VkBool32); i++)
1258 : 264 : flags[i] &= flags_sup[i];
1259 : : }
1260 : :
1261 : : // Construct normalized output chain
1262 : 2 : vk->features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1263 : 2 : vk_features_normalize(vk->alloc, &features, vk->api_ver, &vk->features);
1264 [ - + ]: 2 : if (!check_required_features(vk)) {
1265 : 0 : PL_FATAL(vk, "Vulkan device does not support all required features!");
1266 : 0 : goto error;
1267 : : }
1268 : :
1269 : : // Enable all queues at device creation time, to maximize compatibility
1270 : : // with other API users (e.g. FFmpeg)
1271 : : PL_ARRAY(VkDeviceQueueCreateInfo) qinfos = {0};
1272 [ + + ]: 4 : for (int i = 0; i < qfnum; i++) {
1273 [ - + - - ]: 2 : bool use_qf = i == idx_gfx || i == idx_comp || i == idx_tf;
1274 : 2 : use_qf |= qfs[i].queueFlags & params->extra_queues;
1275 [ - + ]: 2 : if (!use_qf)
1276 : 0 : continue;
1277 [ + - - - : 2 : PL_ARRAY_APPEND(tmp, qinfos, (VkDeviceQueueCreateInfo) {
- - ]
1278 : : .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
1279 : : .queueFamilyIndex = i,
1280 : : .queueCount = qfs[i].queueCount,
1281 : : .pQueuePriorities = pl_calloc(tmp, qfs[i].queueCount, sizeof(float)),
1282 : : });
1283 : : }
1284 : :
1285 : 2 : VkDeviceCreateInfo dinfo = {
1286 : : .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
1287 : : .pNext = &vk->features,
1288 : : .pQueueCreateInfos = qinfos.elem,
1289 : 2 : .queueCreateInfoCount = qinfos.num,
1290 : 2 : .ppEnabledExtensionNames = vk->exts.elem,
1291 : 2 : .enabledExtensionCount = vk->exts.num,
1292 : : };
1293 : :
1294 [ - + ]: 2 : PL_INFO(vk, "Creating vulkan device%s", vk->exts.num ? " with extensions:" : "");
1295 [ + + ]: 20 : for (int i = 0; i < vk->exts.num; i++)
1296 : 18 : PL_INFO(vk, " %s", vk->exts.elem[i]);
1297 : :
1298 : : start = pl_clock_now();
1299 [ - + ]: 2 : VK(vk->CreateDevice(vk->physd, &dinfo, PL_VK_ALLOC, &vk->dev));
1300 : 2 : pl_log_cpu_time(vk->log, start, pl_clock_now(), "creating vulkan device");
1301 : :
1302 : : // Load all mandatory device-level functions
1303 [ + + ]: 176 : for (int i = 0; i < PL_ARRAY_SIZE(vk_dev_funs); i++)
1304 : 174 : load_vk_fun(vk, &vk_dev_funs[i]);
1305 : :
1306 : : // Load all of the optional functions from the extensions we enabled
1307 [ + + ]: 28 : for (int i = 0; i < ext_funs.num; i++)
1308 : 26 : load_vk_fun(vk, ext_funs.elem[i]);
1309 : :
1310 : : // Create the command pools for the queues we care about
1311 [ + - ]: 2 : const uint32_t qmax = PL_DEF(params->queue_count, UINT32_MAX);
1312 [ + + ]: 4 : for (int i = 0; i < qfnum; i++) {
1313 [ - + - - ]: 2 : if (i != idx_gfx && i != idx_tf && i != idx_comp)
1314 : 0 : continue; // ignore QFs not used internally
1315 : :
1316 : 2 : int qnum = qfs[i].queueCount;
1317 [ - + ]: 2 : if (qmax < qnum) {
1318 : 0 : PL_DEBUG(vk, "Restricting QF %d from %d queues to %d", i, qnum, qmax);
1319 : 0 : qnum = qmax;
1320 : : }
1321 : :
1322 : 2 : struct vk_cmdpool *pool = vk_cmdpool_create(vk, i, qnum, qfs[i]);
1323 [ - + ]: 2 : if (!pool)
1324 : 0 : goto error;
1325 [ + - - - : 2 : PL_ARRAY_APPEND(vk->alloc, vk->pools, pool);
- - ]
1326 : :
1327 : : // Update the pool_* pointers based on the corresponding index
1328 : : const char *qf_name = NULL;
1329 [ + - ]: 2 : if (i == idx_tf) {
1330 : 2 : vk->pool_transfer = pool;
1331 : : qf_name = "transfer";
1332 : : }
1333 [ + - ]: 2 : if (i == idx_comp) {
1334 : 2 : vk->pool_compute = pool;
1335 : : qf_name = "compute";
1336 : : }
1337 [ + - ]: 2 : if (i == idx_gfx) {
1338 : 2 : vk->pool_graphics = pool;
1339 : : qf_name = "graphics";
1340 : : }
1341 : :
1342 [ + + ]: 4 : for (int n = 0; n < pool->num_queues; n++)
1343 [ + - ]: 2 : PL_VK_NAME_HANDLE(QUEUE, pool->queues[n], qf_name);
1344 : : }
1345 : :
1346 : 2 : pl_free(tmp);
1347 : 2 : return true;
1348 : :
1349 : 0 : error:
1350 : 0 : PL_FATAL(vk, "Failed creating logical device!");
1351 : 0 : pl_free(tmp);
1352 : 0 : vk->failed = true;
1353 : 0 : return false;
1354 : : }
1355 : :
1356 : 0 : static void lock_queue(pl_vulkan pl_vk, uint32_t qf, uint32_t qidx)
1357 : : {
1358 : 0 : struct vk_ctx *vk = PL_PRIV(pl_vk);
1359 : 0 : vk->lock_queue(vk->queue_ctx, qf, qidx);
1360 : 0 : }
1361 : :
1362 : 0 : static void unlock_queue(pl_vulkan pl_vk, uint32_t qf, uint32_t qidx)
1363 : : {
1364 : 0 : struct vk_ctx *vk = PL_PRIV(pl_vk);
1365 : 0 : vk->unlock_queue(vk->queue_ctx, qf, qidx);
1366 : 0 : }
1367 : :
1368 : 3 : static bool finalize_context(struct pl_vulkan_t *pl_vk, int max_glsl_version,
1369 : : bool no_compute)
1370 : : {
1371 : 3 : struct vk_ctx *vk = PL_PRIV(pl_vk);
1372 : :
1373 [ - + ]: 3 : pl_assert(vk->pool_graphics);
1374 [ - + ]: 3 : pl_assert(vk->pool_compute);
1375 [ - + ]: 3 : pl_assert(vk->pool_transfer);
1376 : :
1377 : 3 : vk->ma = vk_malloc_create(vk);
1378 [ + - ]: 3 : if (!vk->ma)
1379 : : return false;
1380 : :
1381 : 3 : pl_vk->gpu = pl_gpu_create_vk(vk);
1382 [ + - ]: 3 : if (!pl_vk->gpu)
1383 : : return false;
1384 : :
1385 : : // Blacklist / restrict features
1386 : : struct pl_glsl_version *glsl = (struct pl_glsl_version *) &pl_vk->gpu->glsl;
1387 [ - + ]: 3 : if (max_glsl_version) {
1388 : 0 : glsl->version = PL_MIN(glsl->version, max_glsl_version);
1389 : 0 : glsl->version = PL_MAX(glsl->version, 140); // required for GL_KHR_vulkan_glsl
1390 : 0 : PL_INFO(vk, "Restricting GLSL version to %d... new version is %d",
1391 : : max_glsl_version, glsl->version);
1392 : : }
1393 : :
1394 : 3 : glsl->compute &= !no_compute;
1395 : :
1396 : : // Expose the resulting vulkan objects
1397 : 3 : pl_vk->instance = vk->inst;
1398 : 3 : pl_vk->phys_device = vk->physd;
1399 : 3 : pl_vk->device = vk->dev;
1400 : 3 : pl_vk->get_proc_addr = vk->GetInstanceProcAddr;
1401 : 3 : pl_vk->api_version = vk->api_ver;
1402 : 3 : pl_vk->extensions = vk->exts.elem;
1403 : 3 : pl_vk->num_extensions = vk->exts.num;
1404 : 3 : pl_vk->features = &vk->features;
1405 : 3 : pl_vk->num_queues = vk->pools.num;
1406 : 3 : pl_vk->queues = pl_calloc_ptr(vk->alloc, vk->pools.num, pl_vk->queues);
1407 : 3 : pl_vk->lock_queue = lock_queue;
1408 : 3 : pl_vk->unlock_queue = unlock_queue;
1409 : :
1410 [ + + ]: 6 : for (int i = 0; i < vk->pools.num; i++) {
1411 : : struct pl_vulkan_queue *queues = (struct pl_vulkan_queue *) pl_vk->queues;
1412 : 3 : queues[i] = (struct pl_vulkan_queue) {
1413 : 3 : .index = vk->pools.elem[i]->qf,
1414 : 3 : .count = vk->pools.elem[i]->num_queues,
1415 : : };
1416 : :
1417 [ + - ]: 3 : if (vk->pools.elem[i] == vk->pool_graphics)
1418 : 3 : pl_vk->queue_graphics = queues[i];
1419 [ + - ]: 3 : if (vk->pools.elem[i] == vk->pool_compute)
1420 : 3 : pl_vk->queue_compute = queues[i];
1421 [ + - ]: 3 : if (vk->pools.elem[i] == vk->pool_transfer)
1422 : 3 : pl_vk->queue_transfer = queues[i];
1423 : : }
1424 : :
1425 [ - + ]: 3 : pl_assert(vk->lock_queue);
1426 [ - + ]: 3 : pl_assert(vk->unlock_queue);
1427 : : return true;
1428 : : }
1429 : :
1430 : 2 : pl_vulkan pl_vulkan_create(pl_log log, const struct pl_vulkan_params *params)
1431 : : {
1432 [ - + ]: 2 : params = PL_DEF(params, &pl_vulkan_default_params);
1433 : 2 : struct pl_vulkan_t *pl_vk = pl_zalloc_obj(NULL, pl_vk, struct vk_ctx);
1434 : 2 : struct vk_ctx *vk = PL_PRIV(pl_vk);
1435 : 2 : *vk = (struct vk_ctx) {
1436 : : .vulkan = pl_vk,
1437 : : .alloc = pl_vk,
1438 : : .log = log,
1439 : 2 : .inst = params->instance,
1440 [ + - ]: 2 : .GetInstanceProcAddr = get_proc_addr_fallback(log, params->get_proc_addr),
1441 : : };
1442 : :
1443 [ - + ]: 2 : pl_mutex_init_type(&vk->lock, PL_MUTEX_RECURSIVE);
1444 [ - + ]: 2 : if (!vk->GetInstanceProcAddr)
1445 : 0 : goto error;
1446 : :
1447 [ - + ]: 2 : if (!vk->inst) {
1448 [ # # ]: 0 : pl_assert(!params->surface);
1449 [ # # ]: 0 : pl_assert(!params->device);
1450 : 0 : PL_DEBUG(vk, "No VkInstance provided, creating one...");
1451 : :
1452 : : // Mirror the instance params here to set `get_proc_addr` correctly
1453 : : struct pl_vk_inst_params iparams;
1454 [ # # ]: 0 : iparams = *PL_DEF(params->instance_params, &pl_vk_inst_default_params);
1455 : 0 : iparams.get_proc_addr = params->get_proc_addr;
1456 : 0 : vk->internal_instance = pl_vk_inst_create(log, &iparams);
1457 [ # # ]: 0 : if (!vk->internal_instance)
1458 : 0 : goto error;
1459 : 0 : vk->inst = vk->internal_instance->instance;
1460 : : }
1461 : :
1462 : : // Directly load all mandatory instance-level function pointers, since
1463 : : // these will be required for all further device creation logic
1464 [ + + ]: 36 : for (int i = 0; i < PL_ARRAY_SIZE(vk_inst_funs); i++)
1465 : 34 : load_vk_fun(vk, &vk_inst_funs[i]);
1466 : :
1467 : : // Choose the physical device
1468 [ + - ]: 2 : if (params->device) {
1469 : 2 : PL_DEBUG(vk, "Using specified VkPhysicalDevice");
1470 : 2 : vk->physd = params->device;
1471 : : } else {
1472 : 0 : struct pl_vulkan_device_params dparams = {
1473 : 0 : .instance = vk->inst,
1474 : 0 : .get_proc_addr = params->get_proc_addr,
1475 : 0 : .surface = params->surface,
1476 : 0 : .device_name = params->device_name,
1477 : 0 : .allow_software = params->allow_software,
1478 : : };
1479 : : memcpy(dparams.device_uuid, params->device_uuid, VK_UUID_SIZE);
1480 : :
1481 : 0 : vk->physd = pl_vulkan_choose_device(log, &dparams);
1482 [ # # ]: 0 : if (!vk->physd) {
1483 : 0 : PL_FATAL(vk, "Found no suitable device, giving up.");
1484 : 0 : goto error;
1485 : : }
1486 : : }
1487 : :
1488 : 2 : VkPhysicalDeviceIDPropertiesKHR id_props = {
1489 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR,
1490 : : };
1491 : :
1492 : 2 : VkPhysicalDeviceProperties2KHR prop = {
1493 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
1494 : : .pNext = &id_props,
1495 : : };
1496 : :
1497 : 2 : vk->GetPhysicalDeviceProperties2(vk->physd, &prop);
1498 : 2 : vk->props = prop.properties;
1499 : :
1500 : 2 : PL_INFO(vk, "Vulkan device properties:");
1501 : 2 : PL_INFO(vk, " Device Name: %s", prop.properties.deviceName);
1502 : 2 : PL_INFO(vk, " Device ID: %"PRIx32":%"PRIx32, prop.properties.vendorID,
1503 : : prop.properties.deviceID);
1504 : 2 : PL_INFO(vk, " Device UUID: %s", PRINT_UUID(id_props.deviceUUID));
1505 : 2 : PL_INFO(vk, " Driver version: %"PRIx32, prop.properties.driverVersion);
1506 : 2 : PL_INFO(vk, " API version: %d.%d.%d", PRINTF_VER(prop.properties.apiVersion));
1507 : :
1508 : : // Needed by device_init
1509 : 2 : vk->api_ver = prop.properties.apiVersion;
1510 [ - + ]: 2 : if (params->max_api_version) {
1511 : 0 : vk->api_ver = PL_MIN(vk->api_ver, params->max_api_version);
1512 : 0 : PL_INFO(vk, "Restricting API version to %d.%d.%d... new version %d.%d.%d",
1513 : : PRINTF_VER(params->max_api_version), PRINTF_VER(vk->api_ver));
1514 : : }
1515 : :
1516 [ - + ]: 2 : if (vk->api_ver < PL_VK_MIN_VERSION) {
1517 : 0 : PL_FATAL(vk, "Device API version %d.%d.%d is lower than the minimum "
1518 : : "required version of %d.%d.%d, cannot proceed!",
1519 : : PRINTF_VER(vk->api_ver), PRINTF_VER(PL_VK_MIN_VERSION));
1520 : 0 : goto error;
1521 : : }
1522 : :
1523 : : // Finally, initialize the logical device and the rest of the vk_ctx
1524 [ - + ]: 2 : if (!device_init(vk, params))
1525 : 0 : goto error;
1526 : :
1527 [ - + ]: 2 : if (!finalize_context(pl_vk, params->max_glsl_version, params->no_compute))
1528 : 0 : goto error;
1529 : :
1530 : 2 : return pl_vk;
1531 : :
1532 : 0 : error:
1533 : 0 : PL_FATAL(vk, "Failed initializing vulkan device");
1534 : 0 : pl_vulkan_destroy((pl_vulkan *) &pl_vk);
1535 : 0 : return NULL;
1536 : : }
1537 : :
1538 : 1 : pl_vulkan pl_vulkan_import(pl_log log, const struct pl_vulkan_import_params *params)
1539 : : {
1540 : 1 : void *tmp = pl_tmp(NULL);
1541 : :
1542 : 1 : struct pl_vulkan_t *pl_vk = pl_zalloc_obj(NULL, pl_vk, struct vk_ctx);
1543 : 1 : struct vk_ctx *vk = PL_PRIV(pl_vk);
1544 : 1 : *vk = (struct vk_ctx) {
1545 : : .vulkan = pl_vk,
1546 : : .alloc = pl_vk,
1547 : : .log = log,
1548 : : .imported = true,
1549 : 1 : .inst = params->instance,
1550 : 1 : .physd = params->phys_device,
1551 : 1 : .dev = params->device,
1552 : 1 : .GetInstanceProcAddr = get_proc_addr_fallback(log, params->get_proc_addr),
1553 : 1 : .lock_queue = params->lock_queue,
1554 : 1 : .unlock_queue = params->unlock_queue,
1555 [ + - ]: 1 : .queue_ctx = params->queue_ctx,
1556 : : };
1557 : :
1558 [ - + ]: 1 : pl_mutex_init_type(&vk->lock, PL_MUTEX_RECURSIVE);
1559 [ - + ]: 1 : if (!vk->GetInstanceProcAddr)
1560 : 0 : goto error;
1561 : :
1562 [ + + ]: 18 : for (int i = 0; i < PL_ARRAY_SIZE(vk_inst_funs); i++)
1563 : 17 : load_vk_fun(vk, &vk_inst_funs[i]);
1564 : :
1565 : 1 : VkPhysicalDeviceIDPropertiesKHR id_props = {
1566 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR,
1567 : : };
1568 : :
1569 : 1 : VkPhysicalDeviceProperties2KHR prop = {
1570 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
1571 : : .pNext = &id_props,
1572 : : };
1573 : :
1574 [ - + ]: 1 : pl_assert(vk->GetPhysicalDeviceProperties2);
1575 : 1 : vk->GetPhysicalDeviceProperties2(vk->physd, &prop);
1576 : 1 : vk->props = prop.properties;
1577 : :
1578 : 1 : PL_INFO(vk, "Imported vulkan device properties:");
1579 : 1 : PL_INFO(vk, " Device Name: %s", prop.properties.deviceName);
1580 : 1 : PL_INFO(vk, " Device ID: %"PRIx32":%"PRIx32, prop.properties.vendorID,
1581 : : prop.properties.deviceID);
1582 : 1 : PL_INFO(vk, " Device UUID: %s", PRINT_UUID(id_props.deviceUUID));
1583 : 1 : PL_INFO(vk, " Driver version: %"PRIx32, prop.properties.driverVersion);
1584 : 1 : PL_INFO(vk, " API version: %d.%d.%d", PRINTF_VER(prop.properties.apiVersion));
1585 : :
1586 : 1 : vk->api_ver = prop.properties.apiVersion;
1587 [ - + ]: 1 : if (params->max_api_version) {
1588 : 0 : vk->api_ver = PL_MIN(vk->api_ver, params->max_api_version);
1589 : 0 : PL_INFO(vk, "Restricting API version to %d.%d.%d... new version %d.%d.%d",
1590 : : PRINTF_VER(params->max_api_version), PRINTF_VER(vk->api_ver));
1591 : : }
1592 : :
1593 [ - + ]: 1 : if (vk->api_ver < PL_VK_MIN_VERSION) {
1594 : 0 : PL_FATAL(vk, "Device API version %d.%d.%d is lower than the minimum "
1595 : : "required version of %d.%d.%d, cannot proceed!",
1596 : : PRINTF_VER(vk->api_ver), PRINTF_VER(PL_VK_MIN_VERSION));
1597 : 0 : goto error;
1598 : : }
1599 : :
1600 : 1 : vk->features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
1601 : 1 : vk_features_normalize(vk->alloc, params->features, 0, &vk->features);
1602 [ - + ]: 1 : if (!check_required_features(vk)) {
1603 : 0 : PL_FATAL(vk, "Imported Vulkan device was not created with all required "
1604 : : "features!");
1605 : 0 : goto error;
1606 : : }
1607 : :
1608 : : // Load all mandatory device-level functions
1609 [ + + ]: 88 : for (int i = 0; i < PL_ARRAY_SIZE(vk_dev_funs); i++)
1610 : 87 : load_vk_fun(vk, &vk_dev_funs[i]);
1611 : :
1612 : : // Load all of the optional functions from the extensions enabled
1613 [ + + ]: 12 : for (int i = 0; i < PL_ARRAY_SIZE(vk_device_extensions); i++) {
1614 : : const struct vk_ext *ext = &vk_device_extensions[i];
1615 : 11 : uint32_t core_ver = vk_ext_promoted_ver(ext->name);
1616 [ + + + + ]: 11 : if (core_ver && vk->api_ver >= core_ver) {
1617 [ + - + + ]: 3 : for (const struct vk_fun *f = ext->funs; f && f->name; f++)
1618 : 2 : load_vk_fun(vk, f);
1619 : 1 : continue;
1620 : : }
1621 [ + + ]: 63 : for (int n = 0; n < params->num_extensions; n++) {
1622 [ + + ]: 61 : if (strcmp(ext->name, params->extensions[n]) == 0) {
1623 [ + + + + ]: 19 : for (const struct vk_fun *f = ext->funs; f && f->name; f++)
1624 : 11 : load_vk_fun(vk, f);
1625 : : break;
1626 : : }
1627 : : }
1628 : : }
1629 : :
1630 : 1 : uint32_t qfnum = 0;
1631 : 1 : vk->GetPhysicalDeviceQueueFamilyProperties(vk->physd, &qfnum, NULL);
1632 : 1 : VkQueueFamilyProperties *qfs = pl_calloc_ptr(tmp, qfnum, qfs);
1633 : 1 : vk->GetPhysicalDeviceQueueFamilyProperties(vk->physd, &qfnum, qfs);
1634 [ + - ]: 1 : if (!params->lock_queue)
1635 : 1 : init_queue_locks(vk, qfnum, qfs);
1636 : :
1637 : : // Create the command pools for each unique qf that exists
1638 : : struct {
1639 : : const struct pl_vulkan_queue *info;
1640 : : struct vk_cmdpool **pool;
1641 : : VkQueueFlagBits flags; // *any* of these flags provide the cap
1642 : 1 : } qinfos[] = {
1643 : : {
1644 : 1 : .info = ¶ms->queue_graphics,
1645 : 1 : .pool = &vk->pool_graphics,
1646 : : .flags = VK_QUEUE_GRAPHICS_BIT,
1647 : : }, {
1648 : 1 : .info = ¶ms->queue_compute,
1649 : 1 : .pool = &vk->pool_compute,
1650 : : .flags = VK_QUEUE_COMPUTE_BIT,
1651 : : }, {
1652 : 1 : .info = ¶ms->queue_transfer,
1653 : 1 : .pool = &vk->pool_transfer,
1654 : : .flags = VK_QUEUE_TRANSFER_BIT |
1655 : : VK_QUEUE_GRAPHICS_BIT |
1656 : : VK_QUEUE_COMPUTE_BIT,
1657 : : }
1658 : : };
1659 : :
1660 [ + + ]: 4 : for (int i = 0; i < PL_ARRAY_SIZE(qinfos); i++) {
1661 : 3 : int qf = qinfos[i].info->index;
1662 : 3 : struct vk_cmdpool **pool = qinfos[i].pool;
1663 [ - + ]: 3 : if (!qinfos[i].info->count)
1664 : 0 : continue;
1665 : :
1666 : : // API sanity check
1667 [ - + ]: 3 : pl_assert(qfs[qf].queueFlags & qinfos[i].flags);
1668 : :
1669 : : // See if we already created a pool for this queue family
1670 [ + + ]: 3 : for (int j = 0; j < i; j++) {
1671 [ + - + - ]: 2 : if (qinfos[j].info->count && qinfos[j].info->index == qf) {
1672 : 2 : *pool = *qinfos[j].pool;
1673 : 2 : goto next_qf;
1674 : : }
1675 : : }
1676 : :
1677 : 1 : *pool = vk_cmdpool_create(vk, qf, qinfos[i].info->count, qfs[qf]);
1678 [ - + ]: 1 : if (!*pool)
1679 : 0 : goto error;
1680 [ + - - - : 1 : PL_ARRAY_APPEND(vk->alloc, vk->pools, *pool);
- - ]
1681 : :
1682 : : // Pre-emptively set "lower priority" pools as well
1683 [ + + ]: 3 : for (int j = i+1; j < PL_ARRAY_SIZE(qinfos); j++) {
1684 [ + - ]: 2 : if (qfs[qf].queueFlags & qinfos[j].flags)
1685 : 2 : *qinfos[j].pool = *pool;
1686 : : }
1687 : :
1688 : 3 : next_qf: ;
1689 : : }
1690 : :
1691 [ - + ]: 1 : if (!vk->pool_graphics) {
1692 : 0 : PL_ERR(vk, "No valid queues provided?");
1693 : 0 : goto error;
1694 : : }
1695 : :
1696 [ - + ]: 1 : if (!finalize_context(pl_vk, params->max_glsl_version, params->no_compute))
1697 : 0 : goto error;
1698 : :
1699 : 1 : pl_free(tmp);
1700 : 1 : return pl_vk;
1701 : :
1702 : 0 : error:
1703 : 0 : PL_FATAL(vk, "Failed importing vulkan device");
1704 : 0 : pl_vulkan_destroy((pl_vulkan *) &pl_vk);
1705 : 0 : pl_free(tmp);
1706 : 0 : return NULL;
1707 : : }
|