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 "gpu.h"
19 : : #include "formats.h"
20 : : #include "glsl/spirv.h"
21 : :
22 : : #ifdef PL_HAVE_UNIX
23 : : #include <unistd.h>
24 : : #endif
25 : :
26 : : // Gives us enough queries for 8 results
27 : : #define QUERY_POOL_SIZE 16
28 : :
29 : : struct pl_timer_t {
30 : : VkQueryPool qpool; // even=start, odd=stop
31 : : int index_write; // next index to write to
32 : : int index_read; // next index to read from
33 : : uint64_t pending; // bitmask of queries that are still running
34 : : };
35 : :
36 : : static inline uint64_t timer_bit(int index)
37 : : {
38 : 3962 : return 1llu << (index / 2);
39 : : }
40 : :
41 : 520 : static void timer_destroy_cb(pl_gpu gpu, pl_timer timer)
42 : : {
43 : 520 : struct pl_vk *p = PL_PRIV(gpu);
44 : 520 : struct vk_ctx *vk = p->vk;
45 : :
46 [ - + ]: 520 : pl_assert(!timer->pending);
47 : 520 : vk->DestroyQueryPool(vk->dev, timer->qpool, PL_VK_ALLOC);
48 : 520 : pl_free(timer);
49 : 520 : }
50 : :
51 : 520 : VK_CB_FUNC_DEF(timer_destroy_cb);
52 : :
53 : 520 : static pl_timer vk_timer_create(pl_gpu gpu)
54 : : {
55 : 520 : struct pl_vk *p = PL_PRIV(gpu);
56 : 520 : struct vk_ctx *vk = p->vk;
57 : :
58 : 520 : pl_timer timer = pl_alloc_ptr(NULL, timer);
59 : 520 : *timer = (struct pl_timer_t) {0};
60 : :
61 : 520 : struct VkQueryPoolCreateInfo qinfo = {
62 : : .sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
63 : : .queryType = VK_QUERY_TYPE_TIMESTAMP,
64 : : .queryCount = QUERY_POOL_SIZE,
65 : : };
66 : :
67 [ - + ]: 520 : VK(vk->CreateQueryPool(vk->dev, &qinfo, PL_VK_ALLOC, &timer->qpool));
68 : : return timer;
69 : :
70 : : error:
71 : 0 : timer_destroy_cb(gpu, timer);
72 : 0 : return NULL;
73 : : }
74 : :
75 : 520 : static void vk_timer_destroy(pl_gpu gpu, pl_timer timer)
76 : : {
77 : 520 : vk_gpu_idle_callback(gpu, VK_CB_FUNC(timer_destroy_cb), gpu, timer);
78 : 520 : }
79 : :
80 : 1268 : static uint64_t vk_timer_query(pl_gpu gpu, pl_timer timer)
81 : : {
82 : 1268 : struct pl_vk *p = PL_PRIV(gpu);
83 : 1268 : struct vk_ctx *vk = p->vk;
84 : :
85 [ + + ]: 1268 : if (timer->index_read == timer->index_write)
86 : : return 0; // no more unprocessed results
87 : :
88 : 1262 : vk_poll_commands(vk, 0);
89 [ + + ]: 1262 : if (timer->pending & timer_bit(timer->index_read))
90 : : return 0; // still waiting for results
91 : :
92 : : VkResult res;
93 : 735 : uint64_t ts[2] = {0};
94 : 735 : res = vk->GetQueryPoolResults(vk->dev, timer->qpool, timer->index_read, 2,
95 : : sizeof(ts), &ts[0], sizeof(uint64_t),
96 : : VK_QUERY_RESULT_64_BIT);
97 : :
98 [ - + - ]: 735 : switch (res) {
99 : 735 : case VK_SUCCESS:
100 : 735 : timer->index_read = (timer->index_read + 2) % QUERY_POOL_SIZE;
101 : 735 : return (ts[1] - ts[0]) * vk->props.limits.timestampPeriod;
102 : : case VK_NOT_READY:
103 : : return 0;
104 : : default:
105 : 0 : PL_VK_ASSERT(res, "Retrieving query pool results");
106 : : }
107 : :
108 : : error:
109 : 0 : return 0;
110 : : }
111 : :
112 : 1423 : static void timer_begin(pl_gpu gpu, struct vk_cmd *cmd, pl_timer timer)
113 : : {
114 : 1423 : struct pl_vk *p = PL_PRIV(gpu);
115 : 1423 : struct vk_ctx *vk = p->vk;
116 : :
117 [ + + ]: 1423 : if (!timer)
118 : : return;
119 : :
120 [ - + ]: 900 : if (!cmd->pool->props.timestampValidBits) {
121 : 0 : PL_TRACE(gpu, "QF %d does not support timestamp queries", cmd->pool->qf);
122 : 0 : return;
123 : : }
124 : :
125 : 900 : vk_poll_commands(vk, 0);
126 [ + - ]: 900 : if (timer->pending & timer_bit(timer->index_write))
127 : : return; // next query is still running, skip this timer
128 : :
129 : : VkQueueFlags reset_flags = VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT;
130 [ + - ]: 900 : if (cmd->pool->props.queueFlags & reset_flags) {
131 : : // Use direct command buffer resets
132 : 900 : vk->CmdResetQueryPool(cmd->buf, timer->qpool, timer->index_write, 2);
133 : : } else {
134 : : // Use host query reset
135 : 0 : vk->ResetQueryPool(vk->dev, timer->qpool, timer->index_write, 2);
136 : : }
137 : :
138 : 900 : vk->CmdWriteTimestamp(cmd->buf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
139 : 900 : timer->qpool, timer->index_write);
140 : :
141 : 900 : p->cmd_timer = timer;
142 : : }
143 : :
144 : : static inline bool supports_marks(struct vk_cmd *cmd) {
145 : : // Spec says debug markers are only available on graphics/compute queues
146 : 2846 : VkQueueFlags flags = cmd->pool->props.queueFlags;
147 : 2846 : return flags & (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT);
148 : : }
149 : :
150 : 1423 : struct vk_cmd *_begin_cmd(pl_gpu gpu, enum queue_type type, const char *label,
151 : : pl_timer timer)
152 : : {
153 : 1423 : struct pl_vk *p = PL_PRIV(gpu);
154 : 1423 : struct vk_ctx *vk = p->vk;
155 : 1423 : pl_mutex_lock(&p->recording);
156 : :
157 : : struct vk_cmdpool *pool;
158 [ - + + + : 1423 : switch (type) {
- ]
159 [ # # ]: 0 : case ANY: pool = p->cmd ? p->cmd->pool : vk->pool_graphics; break;
160 : 928 : case GRAPHICS: pool = vk->pool_graphics; break;
161 : 84 : case COMPUTE: pool = vk->pool_compute; break;
162 : 411 : case TRANSFER: pool = vk->pool_transfer; break;
163 : 0 : default: pl_unreachable();
164 : : }
165 : :
166 [ + + - + ]: 1423 : if (!p->cmd || p->cmd->pool != pool) {
167 : 780 : vk_cmd_submit(&p->cmd);
168 : 780 : p->cmd = vk_cmd_begin(pool, label);
169 [ - + ]: 780 : if (!p->cmd) {
170 : 0 : pl_mutex_unlock(&p->recording);
171 : 0 : return NULL;
172 : : }
173 : : }
174 : :
175 [ + - + - ]: 1423 : if (vk->CmdBeginDebugUtilsLabelEXT && supports_marks(p->cmd)) {
176 : 1423 : vk->CmdBeginDebugUtilsLabelEXT(p->cmd->buf, &(VkDebugUtilsLabelEXT) {
177 : : .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
178 : : .pLabelName = label,
179 : : });
180 : : }
181 : :
182 : 1423 : timer_begin(gpu, p->cmd, timer);
183 : 1423 : return p->cmd;
184 : : }
185 : :
186 : 900 : static void timer_end_cb(void *ptimer, void *pindex)
187 : : {
188 : : pl_timer timer = ptimer;
189 : 900 : int index = (uintptr_t) pindex;
190 : 900 : timer->pending &= ~timer_bit(index);
191 : 900 : }
192 : :
193 : 1757 : bool _end_cmd(pl_gpu gpu, struct vk_cmd **pcmd, bool submit)
194 : : {
195 : 1757 : struct pl_vk *p = PL_PRIV(gpu);
196 : 1757 : struct vk_ctx *vk = p->vk;
197 : : bool ret = true;
198 [ + + ]: 1757 : if (!pcmd) {
199 [ + - ]: 334 : if (submit) {
200 : 334 : pl_mutex_lock(&p->recording);
201 : 334 : ret = vk_cmd_submit(&p->cmd);
202 : 334 : pl_mutex_unlock(&p->recording);
203 : : }
204 : 334 : return ret;
205 : : }
206 : :
207 : 1423 : struct vk_cmd *cmd = *pcmd;
208 [ - + ]: 1423 : pl_assert(p->cmd == cmd);
209 : :
210 [ + + ]: 1423 : if (p->cmd_timer) {
211 : : pl_timer timer = p->cmd_timer;
212 : 900 : vk->CmdWriteTimestamp(cmd->buf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
213 : 900 : timer->qpool, timer->index_write + 1);
214 : :
215 : 900 : timer->pending |= timer_bit(timer->index_write);
216 : 900 : vk_cmd_callback(cmd, timer_end_cb, timer,
217 : 900 : (void *) (uintptr_t) timer->index_write);
218 : :
219 : 900 : timer->index_write = (timer->index_write + 2) % QUERY_POOL_SIZE;
220 [ - + ]: 900 : if (timer->index_write == timer->index_read) {
221 : : // forcibly drop the least recent result to make space
222 : 0 : timer->index_read = (timer->index_read + 2) % QUERY_POOL_SIZE;
223 : : }
224 : :
225 : 900 : p->cmd_timer = NULL;
226 : : }
227 : :
228 [ + - + - ]: 1423 : if (vk->CmdEndDebugUtilsLabelEXT && supports_marks(cmd))
229 : 1423 : vk->CmdEndDebugUtilsLabelEXT(cmd->buf);
230 : :
231 [ + + ]: 1423 : if (submit)
232 : 550 : ret = vk_cmd_submit(&p->cmd);
233 : :
234 : 1423 : pl_mutex_unlock(&p->recording);
235 : 1423 : return ret;
236 : : }
237 : :
238 : 680 : void vk_gpu_idle_callback(pl_gpu gpu, vk_cb cb, const void *priv, const void *arg)
239 : : {
240 : 680 : struct pl_vk *p = PL_PRIV(gpu);
241 : 680 : struct vk_ctx *vk = p->vk;
242 : :
243 : 680 : pl_mutex_lock(&p->recording);
244 [ - + ]: 680 : if (p->cmd) {
245 : 0 : vk_cmd_callback(p->cmd, cb, priv, arg);
246 : : } else {
247 : 680 : vk_dev_callback(vk, cb, priv, arg);
248 : : }
249 : 680 : pl_mutex_unlock(&p->recording);
250 : 680 : }
251 : :
252 : 3 : static void vk_gpu_destroy(pl_gpu gpu)
253 : : {
254 : 3 : struct pl_vk *p = PL_PRIV(gpu);
255 : 3 : struct vk_ctx *vk = p->vk;
256 : :
257 : 3 : vk_cmd_submit(&p->cmd);
258 : 3 : vk_wait_idle(vk);
259 : :
260 [ + + ]: 9 : for (enum pl_tex_sample_mode s = 0; s < PL_TEX_SAMPLE_MODE_COUNT; s++) {
261 [ + + ]: 24 : for (enum pl_tex_address_mode a = 0; a < PL_TEX_ADDRESS_MODE_COUNT; a++)
262 : 18 : vk->DestroySampler(vk->dev, p->samplers[s][a], PL_VK_ALLOC);
263 : : }
264 : :
265 : 3 : pl_spirv_destroy(&p->spirv);
266 : 3 : pl_mutex_destroy(&p->recording);
267 : 3 : pl_free((void *) gpu);
268 : 3 : }
269 : :
270 : 0 : pl_vulkan pl_vulkan_get(pl_gpu gpu)
271 : : {
272 : 0 : const struct pl_gpu_fns *impl = PL_PRIV(gpu);
273 [ # # ]: 0 : if (impl->destroy == vk_gpu_destroy) {
274 : : struct pl_vk *p = (struct pl_vk *) impl;
275 : 0 : return p->vk->vulkan;
276 : : }
277 : :
278 : : return NULL;
279 : : }
280 : :
281 : 3 : static pl_handle_caps vk_sync_handle_caps(struct vk_ctx *vk)
282 : : {
283 : : pl_handle_caps caps = 0;
284 : :
285 [ + + ]: 6 : for (int i = 0; vk_sync_handle_list[i]; i++) {
286 : : enum pl_handle_type type = vk_sync_handle_list[i];
287 : :
288 : 6 : VkPhysicalDeviceExternalSemaphoreInfo info = {
289 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR,
290 : 3 : .handleType = vk_sync_handle_type(type),
291 : : };
292 : :
293 : 3 : VkExternalSemaphoreProperties props = {
294 : : .sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR,
295 : : };
296 : :
297 : 3 : vk->GetPhysicalDeviceExternalSemaphoreProperties(vk->physd, &info, &props);
298 : 3 : VkExternalSemaphoreFeatureFlags flags = props.externalSemaphoreFeatures;
299 [ + - ]: 3 : if ((props.compatibleHandleTypes & info.handleType) &&
300 [ + - ]: 3 : (flags & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR))
301 : : {
302 : 3 : caps |= type;
303 : : }
304 : : }
305 : :
306 : 3 : return caps;
307 : : }
308 : :
309 : 6 : static pl_handle_caps vk_tex_handle_caps(struct vk_ctx *vk, bool import)
310 : : {
311 : : pl_handle_caps caps = 0;
312 : :
313 [ + + ]: 24 : for (int i = 0; vk_mem_handle_list[i]; i++) {
314 : : enum pl_handle_type handle_type = vk_mem_handle_list[i];
315 [ + + - + ]: 18 : if (handle_type == PL_HANDLE_DMA_BUF && !vk->GetImageDrmFormatModifierPropertiesEXT) {
316 : 0 : PL_DEBUG(vk, "Tex caps for %s (0x%x) unsupported: no DRM modifiers",
317 : : vk_handle_name(vk_mem_handle_type(PL_HANDLE_DMA_BUF)),
318 : : (unsigned int) PL_HANDLE_DMA_BUF);
319 : 6 : continue;
320 : : }
321 : :
322 : : // Query whether creation of a "basic" dummy texture would work
323 : 18 : VkPhysicalDeviceImageDrmFormatModifierInfoEXT drm_pinfo = {
324 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_DRM_FORMAT_MODIFIER_INFO_EXT,
325 : : .drmFormatModifier = DRM_FORMAT_MOD_LINEAR,
326 : : .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
327 : : };
328 : :
329 : 36 : VkPhysicalDeviceExternalImageFormatInfoKHR ext_pinfo = {
330 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO_KHR,
331 : 18 : .handleType = vk_mem_handle_type(handle_type),
332 : : };
333 : :
334 : 18 : VkPhysicalDeviceImageFormatInfo2KHR pinfo = {
335 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR,
336 : : .pNext = &ext_pinfo,
337 : : .format = VK_FORMAT_R8_UNORM,
338 : : .type = VK_IMAGE_TYPE_2D,
339 : : .tiling = VK_IMAGE_TILING_OPTIMAL,
340 : : .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT,
341 : : };
342 : :
343 [ + + ]: 18 : if (handle_type == PL_HANDLE_DMA_BUF) {
344 : 6 : vk_link_struct(&pinfo, &drm_pinfo);
345 : 6 : pinfo.tiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
346 : : }
347 : :
348 : 18 : VkExternalImageFormatPropertiesKHR ext_props = {
349 : : .sType = VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES_KHR,
350 : : };
351 : :
352 : 18 : VkImageFormatProperties2KHR props = {
353 : : .sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR,
354 : : .pNext = &ext_props,
355 : : };
356 : :
357 : : VkResult res;
358 : 18 : res = vk->GetPhysicalDeviceImageFormatProperties2KHR(vk->physd, &pinfo, &props);
359 [ + + ]: 18 : if (res != VK_SUCCESS) {
360 : 6 : PL_DEBUG(vk, "Tex caps for %s (0x%x) unsupported: %s",
361 : : vk_handle_name(ext_pinfo.handleType),
362 : : (unsigned int) handle_type,
363 : : vk_res_str(res));
364 : 6 : continue;
365 : : }
366 : :
367 [ + - ]: 12 : if (vk_external_mem_check(vk, &ext_props.externalMemoryProperties,
368 : : handle_type, import))
369 : : {
370 : 12 : caps |= handle_type;
371 : : }
372 : : }
373 : :
374 : : #ifdef VK_EXT_metal_objects
375 : : if (vk->ExportMetalObjectsEXT && import)
376 : : caps |= PL_HANDLE_MTL_TEX | PL_HANDLE_IOSURFACE;
377 : : #endif
378 : :
379 : 6 : return caps;
380 : : }
381 : :
382 : : static const VkFilter filters[PL_TEX_SAMPLE_MODE_COUNT] = {
383 : : [PL_TEX_SAMPLE_NEAREST] = VK_FILTER_NEAREST,
384 : : [PL_TEX_SAMPLE_LINEAR] = VK_FILTER_LINEAR,
385 : : };
386 : :
387 : 3 : static inline struct pl_spirv_version get_spirv_version(const struct vk_ctx *vk)
388 : : {
389 [ + - ]: 3 : if (vk->api_ver >= VK_API_VERSION_1_3) {
390 : : const VkPhysicalDeviceMaintenance4Features *device_maintenance4;
391 : 3 : device_maintenance4 = vk_find_struct(&vk->features,
392 : : VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_4_FEATURES);
393 : :
394 [ + + + - ]: 3 : if (device_maintenance4 && device_maintenance4->maintenance4) {
395 : 1 : return (struct pl_spirv_version) {
396 : : .env_version = VK_API_VERSION_1_3,
397 : : .spv_version = PL_SPV_VERSION(1, 6),
398 : : };
399 : : }
400 : : }
401 : :
402 [ - + ]: 2 : pl_assert(vk->api_ver >= VK_API_VERSION_1_2);
403 : 2 : return (struct pl_spirv_version) {
404 : : .env_version = VK_API_VERSION_1_2,
405 : : .spv_version = PL_SPV_VERSION(1, 5),
406 : : };
407 : : }
408 : :
409 : : static const struct pl_gpu_fns pl_fns_vk;
410 : :
411 : 3 : pl_gpu pl_gpu_create_vk(struct vk_ctx *vk)
412 : : {
413 [ - + ]: 3 : pl_assert(vk->dev);
414 : :
415 : 3 : struct pl_gpu_t *gpu = pl_zalloc_obj(NULL, gpu, struct pl_vk);
416 : 3 : gpu->log = vk->log;
417 : :
418 : 3 : struct pl_vk *p = PL_PRIV(gpu);
419 [ - + ]: 3 : pl_mutex_init(&p->recording);
420 : 3 : p->vk = vk;
421 : 3 : p->impl = pl_fns_vk;
422 : 3 : p->spirv = pl_spirv_create(vk->log, get_spirv_version(vk));
423 [ - + ]: 3 : if (!p->spirv)
424 : 0 : goto error;
425 : :
426 : : // Query all device properties
427 : 3 : VkPhysicalDevicePCIBusInfoPropertiesEXT pci_props = {
428 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PCI_BUS_INFO_PROPERTIES_EXT,
429 : : };
430 : :
431 : 3 : VkPhysicalDeviceIDPropertiesKHR id_props = {
432 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES_KHR,
433 : : .pNext = &pci_props,
434 : : };
435 : :
436 : 3 : VkPhysicalDevicePushDescriptorPropertiesKHR pushd_props = {
437 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR,
438 : : .pNext = &id_props,
439 : : };
440 : :
441 : 3 : VkPhysicalDeviceSubgroupProperties group_props = {
442 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES,
443 : : .pNext = &pushd_props,
444 : : };
445 : :
446 : 3 : VkPhysicalDeviceExternalMemoryHostPropertiesEXT host_props = {
447 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_MEMORY_HOST_PROPERTIES_EXT,
448 : : .pNext = &group_props,
449 : : };
450 : :
451 : 3 : VkPhysicalDeviceProperties2KHR props = {
452 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
453 : : .pNext = &host_props,
454 : : };
455 : :
456 : : bool is_portability = false;
457 : :
458 : : #ifdef VK_KHR_portability_subset
459 : 3 : VkPhysicalDevicePortabilitySubsetPropertiesKHR port_props = {
460 : : .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_KHR,
461 : : .minVertexInputBindingStrideAlignment = 1,
462 : : };
463 : :
464 [ + + ]: 21 : for (int i = 0; i < vk->exts.num; i++) {
465 [ - + ]: 18 : if (!strcmp(vk->exts.elem[i], VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
466 : 0 : vk_link_struct(&props, &port_props);
467 : : is_portability = true;
468 : 0 : break;
469 : : }
470 : : }
471 : : #endif
472 : :
473 : 3 : vk->GetPhysicalDeviceProperties2(vk->physd, &props);
474 : 3 : VkPhysicalDeviceLimits limits = props.properties.limits;
475 : :
476 : : // Determine GLSL features and limits
477 : 3 : gpu->glsl = (struct pl_glsl_version) {
478 : : .version = 450,
479 : : .vulkan = true,
480 : : .compute = true,
481 : 3 : .max_shmem_size = limits.maxComputeSharedMemorySize,
482 : : .max_group_threads = limits.maxComputeWorkGroupInvocations,
483 : : .max_group_size = {
484 : : limits.maxComputeWorkGroupSize[0],
485 : : limits.maxComputeWorkGroupSize[1],
486 : : limits.maxComputeWorkGroupSize[2],
487 : : },
488 : : };
489 : :
490 : : VkShaderStageFlags req_stages = VK_SHADER_STAGE_FRAGMENT_BIT |
491 : : VK_SHADER_STAGE_COMPUTE_BIT;
492 : : VkSubgroupFeatureFlags req_flags = VK_SUBGROUP_FEATURE_BASIC_BIT |
493 : : VK_SUBGROUP_FEATURE_VOTE_BIT |
494 : : VK_SUBGROUP_FEATURE_ARITHMETIC_BIT |
495 : : VK_SUBGROUP_FEATURE_BALLOT_BIT |
496 : : VK_SUBGROUP_FEATURE_SHUFFLE_BIT;
497 : :
498 [ + - ]: 3 : if ((group_props.supportedStages & req_stages) == req_stages &&
499 [ + - ]: 3 : (group_props.supportedOperations & req_flags) == req_flags)
500 : : {
501 : 3 : gpu->glsl.subgroup_size = group_props.subgroupSize;
502 : : }
503 : :
504 [ + - ]: 3 : if (vk->features.features.shaderImageGatherExtended) {
505 : 3 : gpu->glsl.min_gather_offset = limits.minTexelGatherOffset;
506 : 3 : gpu->glsl.max_gather_offset = limits.maxTexelGatherOffset;
507 : : }
508 : :
509 : 3 : const size_t max_size = vk_malloc_avail(vk->ma, 0);
510 : 3 : gpu->limits = (struct pl_gpu_limits) {
511 : : // pl_gpu
512 : : .thread_safe = true,
513 : : .callbacks = true,
514 : : // pl_buf
515 : : .max_buf_size = max_size,
516 : 3 : .max_ubo_size = PL_MIN(limits.maxUniformBufferRange, max_size),
517 : 3 : .max_ssbo_size = PL_MIN(limits.maxStorageBufferRange, max_size),
518 : 3 : .max_vbo_size = vk_malloc_avail(vk->ma, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
519 : 3 : .max_mapped_size = vk_malloc_avail(vk->ma, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT),
520 : 3 : .max_mapped_vram = vk_malloc_avail(vk->ma, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
521 : : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT),
522 : 3 : .max_buffer_texels = PL_MIN(limits.maxTexelBufferElements, max_size),
523 : 3 : .align_host_ptr = host_props.minImportedHostPointerAlignment,
524 : 3 : .host_cached = vk_malloc_avail(vk->ma, VK_MEMORY_PROPERTY_HOST_CACHED_BIT),
525 : : // pl_tex
526 : : .max_tex_1d_dim = limits.maxImageDimension1D,
527 : : .max_tex_2d_dim = limits.maxImageDimension2D,
528 : : .max_tex_3d_dim = limits.maxImageDimension3D,
529 : : .blittable_1d_3d = true,
530 : : .buf_transfer = true,
531 : : .align_tex_xfer_pitch = limits.optimalBufferCopyRowPitchAlignment,
532 : 3 : .align_tex_xfer_offset = pl_lcm(limits.optimalBufferCopyOffsetAlignment, 4),
533 : : // pl_pass
534 : : .max_variable_comps = 0, // vulkan doesn't support these at all
535 : : .max_constants = SIZE_MAX,
536 : 3 : .array_size_constants = !is_portability,
537 : 3 : .max_pushc_size = limits.maxPushConstantsSize,
538 : : #ifdef VK_KHR_portability_subset
539 : 3 : .align_vertex_stride = port_props.minVertexInputBindingStrideAlignment,
540 : : #else
541 : : .align_vertex_stride = 1,
542 : : #endif
543 : : .max_dispatch = {
544 : : limits.maxComputeWorkGroupCount[0],
545 : : limits.maxComputeWorkGroupCount[1],
546 : : limits.maxComputeWorkGroupCount[2],
547 : : },
548 : 3 : .fragment_queues = vk->pool_graphics->num_queues,
549 : 3 : .compute_queues = vk->pool_compute->num_queues,
550 : : };
551 : :
552 : 3 : gpu->export_caps.buf = vk_malloc_handle_caps(vk->ma, false);
553 : 3 : gpu->import_caps.buf = vk_malloc_handle_caps(vk->ma, true);
554 : 3 : gpu->export_caps.tex = vk_tex_handle_caps(vk, false);
555 : 3 : gpu->import_caps.tex = vk_tex_handle_caps(vk, true);
556 : 3 : gpu->export_caps.sync = vk_sync_handle_caps(vk);
557 [ - + ]: 3 : gpu->import_caps.sync = 0; // Not supported yet
558 : :
559 : : if (pl_gpu_supports_interop(gpu)) {
560 : : pl_static_assert(sizeof(gpu->uuid) == VK_UUID_SIZE);
561 : 3 : memcpy(gpu->uuid, id_props.deviceUUID, sizeof(gpu->uuid));
562 : :
563 : 3 : gpu->pci.domain = pci_props.pciDomain;
564 : 3 : gpu->pci.bus = pci_props.pciBus;
565 : 3 : gpu->pci.device = pci_props.pciDevice;
566 : 3 : gpu->pci.function = pci_props.pciFunction;
567 : : }
568 : :
569 [ + - ]: 3 : if (vk->CmdPushDescriptorSetKHR)
570 : 3 : p->max_push_descriptors = pushd_props.maxPushDescriptors;
571 : :
572 : 3 : vk_setup_formats(gpu);
573 : :
574 : : // Compute the correct minimum texture alignment
575 : 3 : p->min_texel_alignment = 1;
576 [ + + ]: 309 : for (int i = 0; i < gpu->num_formats; i++) {
577 [ + + ]: 306 : if (gpu->formats[i]->emulated || gpu->formats[i]->opaque)
578 : 84 : continue;
579 : 222 : size_t texel_size = gpu->formats[i]->texel_size;
580 : 222 : p->min_texel_alignment = pl_lcm(p->min_texel_alignment, texel_size);
581 : : }
582 : 3 : PL_DEBUG(gpu, "Minimum texel alignment: %zu", p->min_texel_alignment);
583 : :
584 : : // Initialize the samplers
585 [ + + ]: 9 : for (enum pl_tex_sample_mode s = 0; s < PL_TEX_SAMPLE_MODE_COUNT; s++) {
586 [ + + ]: 24 : for (enum pl_tex_address_mode a = 0; a < PL_TEX_ADDRESS_MODE_COUNT; a++) {
587 : : static const VkSamplerAddressMode modes[PL_TEX_ADDRESS_MODE_COUNT] = {
588 : : [PL_TEX_ADDRESS_CLAMP] = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE,
589 : : [PL_TEX_ADDRESS_REPEAT] = VK_SAMPLER_ADDRESS_MODE_REPEAT,
590 : : [PL_TEX_ADDRESS_MIRROR] = VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT,
591 : : };
592 : :
593 : 18 : VkSamplerCreateInfo sinfo = {
594 : : .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
595 : 18 : .magFilter = filters[s],
596 : : .minFilter = filters[s],
597 : 18 : .addressModeU = modes[a],
598 : : .addressModeV = modes[a],
599 : : .addressModeW = modes[a],
600 : : .maxAnisotropy = 1.0,
601 : : };
602 : :
603 [ - + ]: 18 : VK(vk->CreateSampler(vk->dev, &sinfo, PL_VK_ALLOC, &p->samplers[s][a]));
604 : : }
605 : : }
606 : :
607 : 3 : return pl_gpu_finalize(gpu);
608 : :
609 : 0 : error:
610 : 0 : vk_gpu_destroy(gpu);
611 : 0 : return NULL;
612 : : }
613 : :
614 : 4 : void pl_vulkan_sem_destroy(pl_gpu gpu, VkSemaphore *semaphore)
615 : : {
616 : 4 : VkSemaphore sem = *semaphore;
617 [ + - ]: 4 : if (!sem)
618 : : return;
619 : :
620 : 4 : struct pl_vk *p = PL_PRIV(gpu);
621 : 4 : struct vk_ctx *vk = p->vk;
622 : 4 : vk->DestroySemaphore(vk->dev, sem, PL_VK_ALLOC);
623 : 4 : *semaphore = VK_NULL_HANDLE;
624 : : }
625 : :
626 : 4 : VkSemaphore pl_vulkan_sem_create(pl_gpu gpu, const struct pl_vulkan_sem_params *params)
627 : : {
628 : 4 : struct pl_vk *p = PL_PRIV(gpu);
629 : 4 : struct vk_ctx *vk = p->vk;
630 : :
631 [ - + ]: 4 : pl_assert(PL_ISPOT(params->export_handle));
632 [ - + ]: 4 : if ((params->export_handle & gpu->export_caps.sync) != params->export_handle) {
633 : 0 : PL_ERR(gpu, "Invalid handle type 0x%"PRIx64" specified for "
634 : : "`pl_vulkan_sem_create`!", (uint64_t) params->export_handle);
635 : 0 : return VK_NULL_HANDLE;
636 : : }
637 : :
638 [ - - - + ]: 4 : switch (params->export_handle) {
639 : 0 : case PL_HANDLE_FD:
640 : 0 : params->out_handle->fd = -1;
641 : 0 : break;
642 : 0 : case PL_HANDLE_WIN32:
643 : : case PL_HANDLE_WIN32_KMT:
644 : 0 : params->out_handle->handle = NULL;
645 : 0 : break;
646 : : case PL_HANDLE_DMA_BUF:
647 : : case PL_HANDLE_HOST_PTR:
648 : : case PL_HANDLE_MTL_TEX:
649 : : case PL_HANDLE_IOSURFACE:
650 : 0 : pl_unreachable();
651 : : }
652 : :
653 : 8 : const VkExportSemaphoreCreateInfoKHR einfo = {
654 : : .sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR,
655 : 4 : .handleTypes = vk_sync_handle_type(params->export_handle),
656 : : };
657 : :
658 : 8 : const VkSemaphoreTypeCreateInfo stinfo = {
659 : : .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
660 [ + - ]: 4 : .pNext = params->export_handle ? &einfo : NULL,
661 : 4 : .semaphoreType = params->type,
662 : 4 : .initialValue = params->initial_value,
663 : : };
664 : :
665 : 4 : const VkSemaphoreCreateInfo sinfo = {
666 : : .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
667 : : .pNext = &stinfo,
668 : : };
669 : :
670 : 4 : VkSemaphore sem = VK_NULL_HANDLE;
671 [ - + ]: 4 : VK(vk->CreateSemaphore(vk->dev, &sinfo, PL_VK_ALLOC, &sem));
672 [ + - + - ]: 8 : PL_VK_NAME(SEMAPHORE, sem, PL_DEF(params->debug_tag, "pl_vulkan_sem"));
673 : :
674 : : #ifdef PL_HAVE_UNIX
675 [ - + ]: 4 : if (params->export_handle == PL_HANDLE_FD) {
676 : 0 : VkSemaphoreGetFdInfoKHR finfo = {
677 : : .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
678 : 0 : .handleType = einfo.handleTypes,
679 : : .semaphore = sem,
680 : : };
681 : :
682 [ # # ]: 0 : VK(vk->GetSemaphoreFdKHR(vk->dev, &finfo, ¶ms->out_handle->fd));
683 : : }
684 : : #endif
685 : :
686 : : #ifdef PL_HAVE_WIN32
687 : : if (params->export_handle == PL_HANDLE_WIN32 ||
688 : : params->export_handle == PL_HANDLE_WIN32_KMT)
689 : : {
690 : : VkSemaphoreGetWin32HandleInfoKHR handle_info = {
691 : : .sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR,
692 : : .handleType = einfo.handleTypes,
693 : : .semaphore = sem,
694 : : };
695 : :
696 : : VK(vk->GetSemaphoreWin32HandleKHR(vk->dev, &handle_info,
697 : : ¶ms->out_handle->handle));
698 : : }
699 : : #endif
700 : :
701 : 4 : return sem;
702 : :
703 : 0 : error:
704 : : #ifdef PL_HAVE_UNIX
705 [ # # ]: 0 : if (params->export_handle == PL_HANDLE_FD) {
706 [ # # ]: 0 : if (params->out_handle->fd > -1)
707 : 0 : close(params->out_handle->fd);
708 : : }
709 : : #endif
710 : : #ifdef PL_HAVE_WIN32
711 : : if (params->export_handle == PL_HANDLE_WIN32) {
712 : : if (params->out_handle->handle != NULL)
713 : : CloseHandle(params->out_handle->handle);
714 : : }
715 : : // PL_HANDLE_WIN32_KMT is just an identifier. It doesn't get closed.
716 : : #endif
717 : 0 : vk->DestroySemaphore(vk->dev, sem, PL_VK_ALLOC);
718 : 0 : return VK_NULL_HANDLE;
719 : : }
720 : :
721 : 91 : static void vk_gpu_flush(pl_gpu gpu)
722 : : {
723 : 91 : struct pl_vk *p = PL_PRIV(gpu);
724 : 91 : struct vk_ctx *vk = p->vk;
725 : 91 : CMD_SUBMIT(NULL);
726 : 91 : vk_rotate_queues(vk);
727 : 91 : vk_malloc_garbage_collect(vk->ma);
728 : 91 : }
729 : :
730 : 188 : static void vk_gpu_finish(pl_gpu gpu)
731 : : {
732 : 188 : struct pl_vk *p = PL_PRIV(gpu);
733 : 188 : struct vk_ctx *vk = p->vk;
734 : 188 : CMD_SUBMIT(NULL);
735 : 188 : vk_wait_idle(vk);
736 : 188 : }
737 : :
738 : 3 : static bool vk_gpu_is_failed(pl_gpu gpu)
739 : : {
740 : 3 : struct pl_vk *p = PL_PRIV(gpu);
741 : 3 : struct vk_ctx *vk = p->vk;
742 : 3 : return vk->failed;
743 : : }
744 : :
745 : 10 : struct vk_cmd *pl_vk_steal_cmd(pl_gpu gpu)
746 : : {
747 : 10 : struct pl_vk *p = PL_PRIV(gpu);
748 : 10 : struct vk_ctx *vk = p->vk;
749 : :
750 : 10 : pl_mutex_lock(&p->recording);
751 : 10 : struct vk_cmd *cmd = p->cmd;
752 : 10 : p->cmd = NULL;
753 : 10 : pl_mutex_unlock(&p->recording);
754 : :
755 : 10 : struct vk_cmdpool *pool = vk->pool_graphics;
756 [ - + - - ]: 10 : if (!cmd || cmd->pool != pool) {
757 : 10 : vk_cmd_submit(&cmd);
758 : 10 : cmd = vk_cmd_begin(pool, NULL);
759 : : }
760 : :
761 : 10 : return cmd;
762 : : }
763 : :
764 : 1 : void pl_vk_print_heap(pl_gpu gpu, enum pl_log_level lev)
765 : : {
766 : 1 : struct pl_vk *p = PL_PRIV(gpu);
767 : 1 : struct vk_ctx *vk = p->vk;
768 : 1 : vk_malloc_print_stats(vk->ma, lev);
769 : 1 : }
770 : :
771 : : static const struct pl_gpu_fns pl_fns_vk = {
772 : : .destroy = vk_gpu_destroy,
773 : : .tex_create = vk_tex_create,
774 : : .tex_destroy = vk_tex_deref,
775 : : .tex_invalidate = vk_tex_invalidate,
776 : : .tex_clear_ex = vk_tex_clear_ex,
777 : : .tex_blit = vk_tex_blit,
778 : : .tex_upload = vk_tex_upload,
779 : : .tex_download = vk_tex_download,
780 : : .tex_poll = vk_tex_poll,
781 : : .buf_create = vk_buf_create,
782 : : .buf_destroy = vk_buf_deref,
783 : : .buf_write = vk_buf_write,
784 : : .buf_read = vk_buf_read,
785 : : .buf_copy = vk_buf_copy,
786 : : .buf_export = vk_buf_export,
787 : : .buf_poll = vk_buf_poll,
788 : : .desc_namespace = vk_desc_namespace,
789 : : .pass_create = vk_pass_create,
790 : : .pass_destroy = vk_pass_destroy,
791 : : .pass_run = vk_pass_run,
792 : : .timer_create = vk_timer_create,
793 : : .timer_destroy = vk_timer_destroy,
794 : : .timer_query = vk_timer_query,
795 : : .gpu_flush = vk_gpu_flush,
796 : : .gpu_finish = vk_gpu_finish,
797 : : .gpu_is_failed = vk_gpu_is_failed,
798 : : };
|