1 /*
2 * Copyright © 2018 Google, Inc.
3 * Copyright © 2015 Intel Corporation
4 * SPDX-License-Identifier: MIT
5 */
6
7 #include "tu_drm.h"
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <sys/ioctl.h>
12 #include <sys/mman.h>
13 #include <xf86drm.h>
14
15 #ifdef MAJOR_IN_MKDEV
16 #include <sys/mkdev.h>
17 #endif
18 #ifdef MAJOR_IN_SYSMACROS
19 #include <sys/sysmacros.h>
20 #endif
21
22 #include "vk_util.h"
23
24 #include "drm-uapi/msm_drm.h"
25 #include "util/debug.h"
26 #include "util/timespec.h"
27 #include "util/os_time.h"
28
29 #include "tu_cmd_buffer.h"
30 #include "tu_cs.h"
31 #include "tu_device.h"
32 #include "tu_dynamic_rendering.h"
33
34 struct tu_queue_submit
35 {
36 struct vk_queue_submit *vk_submit;
37 struct tu_u_trace_submission_data *u_trace_submission_data;
38
39 struct tu_cmd_buffer **cmd_buffers;
40 struct drm_msm_gem_submit_cmd *cmds;
41 struct drm_msm_gem_submit_syncobj *in_syncobjs;
42 struct drm_msm_gem_submit_syncobj *out_syncobjs;
43
44 uint32_t nr_cmd_buffers;
45 uint32_t nr_in_syncobjs;
46 uint32_t nr_out_syncobjs;
47 uint32_t entry_count;
48 uint32_t perf_pass_index;
49
50 bool autotune_fence;
51 };
52
53 struct tu_u_trace_syncobj
54 {
55 uint32_t msm_queue_id;
56 uint32_t fence;
57 };
58
59 static int
tu_drm_get_param(const struct tu_physical_device * dev,uint32_t param,uint64_t * value)60 tu_drm_get_param(const struct tu_physical_device *dev,
61 uint32_t param,
62 uint64_t *value)
63 {
64 /* Technically this requires a pipe, but the kernel only supports one pipe
65 * anyway at the time of writing and most of these are clearly pipe
66 * independent. */
67 struct drm_msm_param req = {
68 .pipe = MSM_PIPE_3D0,
69 .param = param,
70 };
71
72 int ret = drmCommandWriteRead(dev->local_fd, DRM_MSM_GET_PARAM, &req,
73 sizeof(req));
74 if (ret)
75 return ret;
76
77 *value = req.value;
78
79 return 0;
80 }
81
82 static int
tu_drm_get_gpu_id(const struct tu_physical_device * dev,uint32_t * id)83 tu_drm_get_gpu_id(const struct tu_physical_device *dev, uint32_t *id)
84 {
85 uint64_t value;
86 int ret = tu_drm_get_param(dev, MSM_PARAM_GPU_ID, &value);
87 if (ret)
88 return ret;
89
90 *id = value;
91 return 0;
92 }
93
94 static int
tu_drm_get_gmem_size(const struct tu_physical_device * dev,uint32_t * size)95 tu_drm_get_gmem_size(const struct tu_physical_device *dev, uint32_t *size)
96 {
97 uint64_t value;
98 int ret = tu_drm_get_param(dev, MSM_PARAM_GMEM_SIZE, &value);
99 if (ret)
100 return ret;
101
102 *size = value;
103 return 0;
104 }
105
106 static int
tu_drm_get_gmem_base(const struct tu_physical_device * dev,uint64_t * base)107 tu_drm_get_gmem_base(const struct tu_physical_device *dev, uint64_t *base)
108 {
109 return tu_drm_get_param(dev, MSM_PARAM_GMEM_BASE, base);
110 }
111
112 int
tu_device_get_gpu_timestamp(struct tu_device * dev,uint64_t * ts)113 tu_device_get_gpu_timestamp(struct tu_device *dev, uint64_t *ts)
114 {
115 return tu_drm_get_param(dev->physical_device, MSM_PARAM_TIMESTAMP, ts);
116 }
117
118 int
tu_device_get_suspend_count(struct tu_device * dev,uint64_t * suspend_count)119 tu_device_get_suspend_count(struct tu_device *dev, uint64_t *suspend_count)
120 {
121 int ret = tu_drm_get_param(dev->physical_device, MSM_PARAM_SUSPENDS, suspend_count);
122 return ret;
123 }
124
125 VkResult
tu_device_check_status(struct vk_device * vk_device)126 tu_device_check_status(struct vk_device *vk_device)
127 {
128 struct tu_device *device = container_of(vk_device, struct tu_device, vk);
129 struct tu_physical_device *physical_device = device->physical_device;
130
131 uint64_t last_fault_count = physical_device->fault_count;
132 int ret = tu_drm_get_param(physical_device, MSM_PARAM_FAULTS, &physical_device->fault_count);
133 if (ret != 0)
134 return vk_device_set_lost(&device->vk, "error getting GPU fault count: %d", ret);
135
136 if (last_fault_count != physical_device->fault_count)
137 return vk_device_set_lost(&device->vk, "GPU faulted or hung");
138
139 return VK_SUCCESS;
140 }
141
142 int
tu_drm_submitqueue_new(const struct tu_device * dev,int priority,uint32_t * queue_id)143 tu_drm_submitqueue_new(const struct tu_device *dev,
144 int priority,
145 uint32_t *queue_id)
146 {
147 uint64_t nr_rings = 1;
148 tu_drm_get_param(dev->physical_device, MSM_PARAM_NR_RINGS, &nr_rings);
149
150 struct drm_msm_submitqueue req = {
151 .flags = 0,
152 .prio = MIN2(priority, MAX2(nr_rings, 1) - 1),
153 };
154
155 int ret = drmCommandWriteRead(dev->fd,
156 DRM_MSM_SUBMITQUEUE_NEW, &req, sizeof(req));
157 if (ret)
158 return ret;
159
160 *queue_id = req.id;
161 return 0;
162 }
163
164 void
tu_drm_submitqueue_close(const struct tu_device * dev,uint32_t queue_id)165 tu_drm_submitqueue_close(const struct tu_device *dev, uint32_t queue_id)
166 {
167 drmCommandWrite(dev->fd, DRM_MSM_SUBMITQUEUE_CLOSE,
168 &queue_id, sizeof(uint32_t));
169 }
170
171 static void
tu_gem_close(const struct tu_device * dev,uint32_t gem_handle)172 tu_gem_close(const struct tu_device *dev, uint32_t gem_handle)
173 {
174 struct drm_gem_close req = {
175 .handle = gem_handle,
176 };
177
178 drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req);
179 }
180
181 /** Helper for DRM_MSM_GEM_INFO, returns 0 on error. */
182 static uint64_t
tu_gem_info(const struct tu_device * dev,uint32_t gem_handle,uint32_t info)183 tu_gem_info(const struct tu_device *dev, uint32_t gem_handle, uint32_t info)
184 {
185 struct drm_msm_gem_info req = {
186 .handle = gem_handle,
187 .info = info,
188 };
189
190 int ret = drmCommandWriteRead(dev->fd,
191 DRM_MSM_GEM_INFO, &req, sizeof(req));
192 if (ret < 0)
193 return 0;
194
195 return req.value;
196 }
197
198 static VkResult
tu_bo_init(struct tu_device * dev,struct tu_bo * bo,uint32_t gem_handle,uint64_t size,bool dump)199 tu_bo_init(struct tu_device *dev,
200 struct tu_bo *bo,
201 uint32_t gem_handle,
202 uint64_t size,
203 bool dump)
204 {
205 uint64_t iova = tu_gem_info(dev, gem_handle, MSM_INFO_GET_IOVA);
206 if (!iova) {
207 tu_gem_close(dev, gem_handle);
208 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
209 }
210
211 mtx_lock(&dev->bo_mutex);
212 uint32_t idx = dev->bo_count++;
213
214 /* grow the bo list if needed */
215 if (idx >= dev->bo_list_size) {
216 uint32_t new_len = idx + 64;
217 struct drm_msm_gem_submit_bo *new_ptr =
218 vk_realloc(&dev->vk.alloc, dev->bo_list, new_len * sizeof(*dev->bo_list),
219 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
220 if (!new_ptr)
221 goto fail_bo_list;
222
223 dev->bo_list = new_ptr;
224 dev->bo_list_size = new_len;
225 }
226
227 dev->bo_list[idx] = (struct drm_msm_gem_submit_bo) {
228 .flags = MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE |
229 COND(dump, MSM_SUBMIT_BO_DUMP),
230 .handle = gem_handle,
231 .presumed = iova,
232 };
233
234 *bo = (struct tu_bo) {
235 .gem_handle = gem_handle,
236 .size = size,
237 .iova = iova,
238 .refcnt = 1,
239 .bo_list_idx = idx,
240 };
241
242 mtx_unlock(&dev->bo_mutex);
243
244 return VK_SUCCESS;
245
246 fail_bo_list:
247 tu_gem_close(dev, gem_handle);
248 return VK_ERROR_OUT_OF_HOST_MEMORY;
249 }
250
251 VkResult
tu_bo_init_new(struct tu_device * dev,struct tu_bo ** out_bo,uint64_t size,enum tu_bo_alloc_flags flags)252 tu_bo_init_new(struct tu_device *dev, struct tu_bo **out_bo, uint64_t size,
253 enum tu_bo_alloc_flags flags)
254 {
255 /* TODO: Choose better flags. As of 2018-11-12, freedreno/drm/msm_bo.c
256 * always sets `flags = MSM_BO_WC`, and we copy that behavior here.
257 */
258 struct drm_msm_gem_new req = {
259 .size = size,
260 .flags = MSM_BO_WC
261 };
262
263 if (flags & TU_BO_ALLOC_GPU_READ_ONLY)
264 req.flags |= MSM_BO_GPU_READONLY;
265
266 int ret = drmCommandWriteRead(dev->fd,
267 DRM_MSM_GEM_NEW, &req, sizeof(req));
268 if (ret)
269 return vk_error(dev, VK_ERROR_OUT_OF_DEVICE_MEMORY);
270
271 struct tu_bo* bo = tu_device_lookup_bo(dev, req.handle);
272 assert(bo && bo->gem_handle == 0);
273
274 VkResult result =
275 tu_bo_init(dev, bo, req.handle, size, flags & TU_BO_ALLOC_ALLOW_DUMP);
276
277 if (result != VK_SUCCESS)
278 memset(bo, 0, sizeof(*bo));
279 else
280 *out_bo = bo;
281
282 return result;
283 }
284
285 VkResult
tu_bo_init_dmabuf(struct tu_device * dev,struct tu_bo ** out_bo,uint64_t size,int prime_fd)286 tu_bo_init_dmabuf(struct tu_device *dev,
287 struct tu_bo **out_bo,
288 uint64_t size,
289 int prime_fd)
290 {
291 /* lseek() to get the real size */
292 off_t real_size = lseek(prime_fd, 0, SEEK_END);
293 lseek(prime_fd, 0, SEEK_SET);
294 if (real_size < 0 || (uint64_t) real_size < size)
295 return vk_error(dev, VK_ERROR_INVALID_EXTERNAL_HANDLE);
296
297 /* Importing the same dmabuf several times would yield the same
298 * gem_handle. Thus there could be a race when destroying
299 * BO and importing the same dmabuf from different threads.
300 * We must not permit the creation of dmabuf BO and its release
301 * to happen in parallel.
302 */
303 u_rwlock_wrlock(&dev->dma_bo_lock);
304
305 uint32_t gem_handle;
306 int ret = drmPrimeFDToHandle(dev->fd, prime_fd,
307 &gem_handle);
308 if (ret) {
309 u_rwlock_wrunlock(&dev->dma_bo_lock);
310 return vk_error(dev, VK_ERROR_INVALID_EXTERNAL_HANDLE);
311 }
312
313 struct tu_bo* bo = tu_device_lookup_bo(dev, gem_handle);
314
315 if (bo->refcnt != 0) {
316 p_atomic_inc(&bo->refcnt);
317 u_rwlock_wrunlock(&dev->dma_bo_lock);
318
319 *out_bo = bo;
320 return VK_SUCCESS;
321 }
322
323 VkResult result = tu_bo_init(dev, bo, gem_handle, size, false);
324
325 if (result != VK_SUCCESS)
326 memset(bo, 0, sizeof(*bo));
327 else
328 *out_bo = bo;
329
330 u_rwlock_wrunlock(&dev->dma_bo_lock);
331
332 return result;
333 }
334
335 int
tu_bo_export_dmabuf(struct tu_device * dev,struct tu_bo * bo)336 tu_bo_export_dmabuf(struct tu_device *dev, struct tu_bo *bo)
337 {
338 int prime_fd;
339 int ret = drmPrimeHandleToFD(dev->fd, bo->gem_handle,
340 DRM_CLOEXEC | DRM_RDWR, &prime_fd);
341
342 return ret == 0 ? prime_fd : -1;
343 }
344
345 VkResult
tu_bo_map(struct tu_device * dev,struct tu_bo * bo)346 tu_bo_map(struct tu_device *dev, struct tu_bo *bo)
347 {
348 if (bo->map)
349 return VK_SUCCESS;
350
351 uint64_t offset = tu_gem_info(dev, bo->gem_handle, MSM_INFO_GET_OFFSET);
352 if (!offset)
353 return vk_error(dev, VK_ERROR_OUT_OF_DEVICE_MEMORY);
354
355 /* TODO: Should we use the wrapper os_mmap() like Freedreno does? */
356 void *map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
357 dev->fd, offset);
358 if (map == MAP_FAILED)
359 return vk_error(dev, VK_ERROR_MEMORY_MAP_FAILED);
360
361 bo->map = map;
362 return VK_SUCCESS;
363 }
364
365 void
tu_bo_finish(struct tu_device * dev,struct tu_bo * bo)366 tu_bo_finish(struct tu_device *dev, struct tu_bo *bo)
367 {
368 assert(bo->gem_handle);
369
370 u_rwlock_rdlock(&dev->dma_bo_lock);
371
372 if (!p_atomic_dec_zero(&bo->refcnt)) {
373 u_rwlock_rdunlock(&dev->dma_bo_lock);
374 return;
375 }
376
377 if (bo->map)
378 munmap(bo->map, bo->size);
379
380 mtx_lock(&dev->bo_mutex);
381 dev->bo_count--;
382 dev->bo_list[bo->bo_list_idx] = dev->bo_list[dev->bo_count];
383
384 struct tu_bo* exchanging_bo = tu_device_lookup_bo(dev, dev->bo_list[bo->bo_list_idx].handle);
385 exchanging_bo->bo_list_idx = bo->bo_list_idx;
386
387 if (bo->implicit_sync)
388 dev->implicit_sync_bo_count--;
389
390 mtx_unlock(&dev->bo_mutex);
391
392 /* Our BO structs are stored in a sparse array in the physical device,
393 * so we don't want to free the BO pointer, instead we want to reset it
394 * to 0, to signal that array entry as being free.
395 */
396 uint32_t gem_handle = bo->gem_handle;
397 memset(bo, 0, sizeof(*bo));
398
399 tu_gem_close(dev, gem_handle);
400
401 u_rwlock_rdunlock(&dev->dma_bo_lock);
402 }
403
404 extern const struct vk_sync_type tu_timeline_sync_type;
405
406 static inline bool
vk_sync_is_tu_timeline_sync(const struct vk_sync * sync)407 vk_sync_is_tu_timeline_sync(const struct vk_sync *sync)
408 {
409 return sync->type == &tu_timeline_sync_type;
410 }
411
412 static struct tu_timeline_sync *
to_tu_timeline_sync(struct vk_sync * sync)413 to_tu_timeline_sync(struct vk_sync *sync)
414 {
415 assert(sync->type == &tu_timeline_sync_type);
416 return container_of(sync, struct tu_timeline_sync, base);
417 }
418
419 static uint32_t
tu_syncobj_from_vk_sync(struct vk_sync * sync)420 tu_syncobj_from_vk_sync(struct vk_sync *sync)
421 {
422 uint32_t syncobj = -1;
423 if (vk_sync_is_tu_timeline_sync(sync)) {
424 syncobj = to_tu_timeline_sync(sync)->syncobj;
425 } else if (vk_sync_type_is_drm_syncobj(sync->type)) {
426 syncobj = vk_sync_as_drm_syncobj(sync)->syncobj;
427 }
428
429 assert(syncobj != -1);
430
431 return syncobj;
432 }
433
434 static VkResult
tu_timeline_sync_init(struct vk_device * vk_device,struct vk_sync * vk_sync,uint64_t initial_value)435 tu_timeline_sync_init(struct vk_device *vk_device,
436 struct vk_sync *vk_sync,
437 uint64_t initial_value)
438 {
439 struct tu_device *device = container_of(vk_device, struct tu_device, vk);
440 struct tu_timeline_sync *sync = to_tu_timeline_sync(vk_sync);
441 uint32_t flags = 0;
442
443 assert(device->fd >= 0);
444
445 int err = drmSyncobjCreate(device->fd, flags, &sync->syncobj);
446
447 if (err < 0) {
448 return vk_error(device, VK_ERROR_DEVICE_LOST);
449 }
450
451 sync->state = initial_value ? TU_TIMELINE_SYNC_STATE_SIGNALED :
452 TU_TIMELINE_SYNC_STATE_RESET;
453
454 return VK_SUCCESS;
455 }
456
457 static void
tu_timeline_sync_finish(struct vk_device * vk_device,struct vk_sync * vk_sync)458 tu_timeline_sync_finish(struct vk_device *vk_device,
459 struct vk_sync *vk_sync)
460 {
461 struct tu_device *dev = container_of(vk_device, struct tu_device, vk);
462 struct tu_timeline_sync *sync = to_tu_timeline_sync(vk_sync);
463
464 assert(dev->fd >= 0);
465 ASSERTED int err = drmSyncobjDestroy(dev->fd, sync->syncobj);
466 assert(err == 0);
467 }
468
469 static VkResult
tu_timeline_sync_reset(struct vk_device * vk_device,struct vk_sync * vk_sync)470 tu_timeline_sync_reset(struct vk_device *vk_device,
471 struct vk_sync *vk_sync)
472 {
473 struct tu_device *dev = container_of(vk_device, struct tu_device, vk);
474 struct tu_timeline_sync *sync = to_tu_timeline_sync(vk_sync);
475
476 int err = drmSyncobjReset(dev->fd, &sync->syncobj, 1);
477 if (err) {
478 return vk_errorf(dev, VK_ERROR_UNKNOWN,
479 "DRM_IOCTL_SYNCOBJ_RESET failed: %m");
480 } else {
481 sync->state = TU_TIMELINE_SYNC_STATE_RESET;
482 }
483
484 return VK_SUCCESS;
485 }
486
487 static VkResult
drm_syncobj_wait(struct tu_device * device,uint32_t * handles,uint32_t count_handles,uint64_t timeout_nsec,bool wait_all)488 drm_syncobj_wait(struct tu_device *device,
489 uint32_t *handles, uint32_t count_handles,
490 uint64_t timeout_nsec, bool wait_all)
491 {
492 uint32_t syncobj_wait_flags = DRM_SYNCOBJ_WAIT_FLAGS_WAIT_FOR_SUBMIT;
493 if (wait_all) syncobj_wait_flags |= DRM_SYNCOBJ_WAIT_FLAGS_WAIT_ALL;
494
495 /* syncobj absolute timeouts are signed. clamp OS_TIMEOUT_INFINITE down. */
496 timeout_nsec = MIN2(timeout_nsec, (uint64_t)INT64_MAX);
497
498 int err = drmSyncobjWait(device->fd, handles,
499 count_handles, timeout_nsec,
500 syncobj_wait_flags,
501 NULL /* first_signaled */);
502 if (err && errno == ETIME) {
503 return VK_TIMEOUT;
504 } else if (err) {
505 return vk_errorf(device, VK_ERROR_UNKNOWN,
506 "DRM_IOCTL_SYNCOBJ_WAIT failed: %m");
507 }
508
509 return VK_SUCCESS;
510 }
511
512 /* Based on anv_bo_sync_wait */
513 static VkResult
tu_timeline_sync_wait(struct vk_device * vk_device,uint32_t wait_count,const struct vk_sync_wait * waits,enum vk_sync_wait_flags wait_flags,uint64_t abs_timeout_ns)514 tu_timeline_sync_wait(struct vk_device *vk_device,
515 uint32_t wait_count,
516 const struct vk_sync_wait *waits,
517 enum vk_sync_wait_flags wait_flags,
518 uint64_t abs_timeout_ns)
519 {
520 struct tu_device *dev = container_of(vk_device, struct tu_device, vk);
521 bool wait_all = !(wait_flags & VK_SYNC_WAIT_ANY);
522
523 uint32_t handles[wait_count];
524 uint32_t submit_count;
525 VkResult ret = VK_SUCCESS;
526 uint32_t pending = wait_count;
527 struct tu_timeline_sync *submitted_syncs[wait_count];
528
529 while (pending) {
530 pending = 0;
531 submit_count = 0;
532
533 for (unsigned i = 0; i < wait_count; ++i) {
534 struct tu_timeline_sync *sync = to_tu_timeline_sync(waits[i].sync);
535
536 if (sync->state == TU_TIMELINE_SYNC_STATE_RESET) {
537 assert(!(wait_flags & VK_SYNC_WAIT_PENDING));
538 pending++;
539 } else if (sync->state == TU_TIMELINE_SYNC_STATE_SIGNALED) {
540 if (wait_flags & VK_SYNC_WAIT_ANY)
541 return VK_SUCCESS;
542 } else if (sync->state == TU_TIMELINE_SYNC_STATE_SUBMITTED) {
543 if (!(wait_flags & VK_SYNC_WAIT_PENDING)) {
544 handles[submit_count] = sync->syncobj;
545 submitted_syncs[submit_count++] = sync;
546 }
547 }
548 }
549
550 if (submit_count > 0) {
551 do {
552 ret = drm_syncobj_wait(dev, handles, submit_count, abs_timeout_ns, wait_all);
553 } while (ret == VK_TIMEOUT && os_time_get_nano() < abs_timeout_ns);
554
555 if (ret == VK_SUCCESS) {
556 for (unsigned i = 0; i < submit_count; ++i) {
557 struct tu_timeline_sync *sync = submitted_syncs[i];
558 sync->state = TU_TIMELINE_SYNC_STATE_SIGNALED;
559 }
560 } else {
561 /* return error covering timeout */
562 return ret;
563 }
564 } else if (pending > 0) {
565 /* If we've hit this then someone decided to vkWaitForFences before
566 * they've actually submitted any of them to a queue. This is a
567 * fairly pessimal case, so it's ok to lock here and use a standard
568 * pthreads condition variable.
569 */
570 pthread_mutex_lock(&dev->submit_mutex);
571
572 /* It's possible that some of the fences have changed state since the
573 * last time we checked. Now that we have the lock, check for
574 * pending fences again and don't wait if it's changed.
575 */
576 uint32_t now_pending = 0;
577 for (uint32_t i = 0; i < wait_count; i++) {
578 struct tu_timeline_sync *sync = to_tu_timeline_sync(waits[i].sync);
579 if (sync->state == TU_TIMELINE_SYNC_STATE_RESET)
580 now_pending++;
581 }
582 assert(now_pending <= pending);
583
584 if (now_pending == pending) {
585 struct timespec abstime = {
586 .tv_sec = abs_timeout_ns / NSEC_PER_SEC,
587 .tv_nsec = abs_timeout_ns % NSEC_PER_SEC,
588 };
589
590 ASSERTED int ret;
591 ret = pthread_cond_timedwait(&dev->timeline_cond,
592 &dev->submit_mutex, &abstime);
593 assert(ret != EINVAL);
594 if (os_time_get_nano() >= abs_timeout_ns) {
595 pthread_mutex_unlock(&dev->submit_mutex);
596 return VK_TIMEOUT;
597 }
598 }
599
600 pthread_mutex_unlock(&dev->submit_mutex);
601 }
602 }
603
604 return ret;
605 }
606
607 const struct vk_sync_type tu_timeline_sync_type = {
608 .size = sizeof(struct tu_timeline_sync),
609 .features = VK_SYNC_FEATURE_BINARY |
610 VK_SYNC_FEATURE_GPU_WAIT |
611 VK_SYNC_FEATURE_GPU_MULTI_WAIT |
612 VK_SYNC_FEATURE_CPU_WAIT |
613 VK_SYNC_FEATURE_CPU_RESET |
614 VK_SYNC_FEATURE_WAIT_ANY |
615 VK_SYNC_FEATURE_WAIT_PENDING,
616 .init = tu_timeline_sync_init,
617 .finish = tu_timeline_sync_finish,
618 .reset = tu_timeline_sync_reset,
619 .wait_many = tu_timeline_sync_wait,
620 };
621
622 static VkResult
tu_drm_device_init(struct tu_physical_device * device,struct tu_instance * instance,drmDevicePtr drm_device)623 tu_drm_device_init(struct tu_physical_device *device,
624 struct tu_instance *instance,
625 drmDevicePtr drm_device)
626 {
627 const char *primary_path = drm_device->nodes[DRM_NODE_PRIMARY];
628 const char *path = drm_device->nodes[DRM_NODE_RENDER];
629 VkResult result = VK_SUCCESS;
630 drmVersionPtr version;
631 int fd;
632 int master_fd = -1;
633
634 fd = open(path, O_RDWR | O_CLOEXEC);
635 if (fd < 0) {
636 return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
637 "failed to open device %s", path);
638 }
639
640 /* Version 1.6 added SYNCOBJ support. */
641 const int min_version_major = 1;
642 const int min_version_minor = 6;
643
644 version = drmGetVersion(fd);
645 if (!version) {
646 close(fd);
647 return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
648 "failed to query kernel driver version for device %s",
649 path);
650 }
651
652 if (strcmp(version->name, "msm")) {
653 drmFreeVersion(version);
654 close(fd);
655 return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
656 "device %s does not use the msm kernel driver",
657 path);
658 }
659
660 if (version->version_major != min_version_major ||
661 version->version_minor < min_version_minor) {
662 result = vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
663 "kernel driver for device %s has version %d.%d, "
664 "but Vulkan requires version >= %d.%d",
665 path,
666 version->version_major, version->version_minor,
667 min_version_major, min_version_minor);
668 drmFreeVersion(version);
669 close(fd);
670 return result;
671 }
672
673 device->msm_major_version = version->version_major;
674 device->msm_minor_version = version->version_minor;
675
676 drmFreeVersion(version);
677
678 if (instance->debug_flags & TU_DEBUG_STARTUP)
679 mesa_logi("Found compatible device '%s'.", path);
680
681 device->instance = instance;
682
683 if (instance->vk.enabled_extensions.KHR_display) {
684 master_fd = open(primary_path, O_RDWR | O_CLOEXEC);
685 if (master_fd >= 0) {
686 /* TODO: free master_fd is accel is not working? */
687 }
688 }
689
690 device->master_fd = master_fd;
691 device->local_fd = fd;
692
693 if (tu_drm_get_gpu_id(device, &device->dev_id.gpu_id)) {
694 result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
695 "could not get GPU ID");
696 goto fail;
697 }
698
699 if (tu_drm_get_param(device, MSM_PARAM_CHIP_ID, &device->dev_id.chip_id)) {
700 result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
701 "could not get CHIP ID");
702 goto fail;
703 }
704
705 if (tu_drm_get_gmem_size(device, &device->gmem_size)) {
706 result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
707 "could not get GMEM size");
708 goto fail;
709 }
710 device->gmem_size = env_var_as_unsigned("TU_GMEM", device->gmem_size);
711
712 if (tu_drm_get_gmem_base(device, &device->gmem_base)) {
713 result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
714 "could not get GMEM size");
715 goto fail;
716 }
717
718 struct stat st;
719
720 if (stat(primary_path, &st) == 0) {
721 device->has_master = true;
722 device->master_major = major(st.st_rdev);
723 device->master_minor = minor(st.st_rdev);
724 } else {
725 device->has_master = false;
726 device->master_major = 0;
727 device->master_minor = 0;
728 }
729
730 if (stat(path, &st) == 0) {
731 device->has_local = true;
732 device->local_major = major(st.st_rdev);
733 device->local_minor = minor(st.st_rdev);
734 } else {
735 result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
736 "failed to stat DRM render node %s", path);
737 goto fail;
738 }
739
740 int ret = tu_drm_get_param(device, MSM_PARAM_FAULTS, &device->fault_count);
741 if (ret != 0) {
742 result = vk_startup_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
743 "Failed to get initial fault count: %d", ret);
744 goto fail;
745 }
746
747 device->syncobj_type = vk_drm_syncobj_get_type(fd);
748 /* we don't support DRM_CAP_SYNCOBJ_TIMELINE, but drm-shim does */
749 if (!(device->syncobj_type.features & VK_SYNC_FEATURE_TIMELINE))
750 device->timeline_type = vk_sync_timeline_get_type(&tu_timeline_sync_type);
751
752 device->sync_types[0] = &device->syncobj_type;
753 device->sync_types[1] = &device->timeline_type.sync;
754 device->sync_types[2] = NULL;
755
756 device->heap.size = tu_get_system_heap_size();
757 device->heap.used = 0u;
758 device->heap.flags = VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;
759
760 result = tu_physical_device_init(device, instance);
761
762 if (result == VK_SUCCESS)
763 return result;
764
765 fail:
766 close(fd);
767 if (master_fd != -1)
768 close(master_fd);
769 return result;
770 }
771
772 VkResult
tu_enumerate_devices(struct tu_instance * instance)773 tu_enumerate_devices(struct tu_instance *instance)
774 {
775 /* TODO: Check for more devices ? */
776 drmDevicePtr devices[8];
777 VkResult result = VK_ERROR_INCOMPATIBLE_DRIVER;
778 int max_devices;
779
780 instance->physical_device_count = 0;
781
782 max_devices = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
783
784 if (instance->debug_flags & TU_DEBUG_STARTUP) {
785 if (max_devices < 0)
786 mesa_logi("drmGetDevices2 returned error: %s\n", strerror(max_devices));
787 else
788 mesa_logi("Found %d drm nodes", max_devices);
789 }
790
791 if (max_devices < 1)
792 return vk_startup_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
793 "No DRM devices found");
794
795 for (unsigned i = 0; i < (unsigned) max_devices; i++) {
796 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
797 devices[i]->bustype == DRM_BUS_PLATFORM) {
798
799 result = tu_drm_device_init(
800 instance->physical_devices + instance->physical_device_count,
801 instance, devices[i]);
802 if (result == VK_SUCCESS)
803 ++instance->physical_device_count;
804 else if (result != VK_ERROR_INCOMPATIBLE_DRIVER)
805 break;
806 }
807 }
808 drmFreeDevices(devices, max_devices);
809
810 return result;
811 }
812
813 static VkResult
tu_queue_submit_create_locked(struct tu_queue * queue,struct vk_queue_submit * vk_submit,const uint32_t nr_in_syncobjs,const uint32_t nr_out_syncobjs,uint32_t perf_pass_index,struct tu_queue_submit * new_submit)814 tu_queue_submit_create_locked(struct tu_queue *queue,
815 struct vk_queue_submit *vk_submit,
816 const uint32_t nr_in_syncobjs,
817 const uint32_t nr_out_syncobjs,
818 uint32_t perf_pass_index,
819 struct tu_queue_submit *new_submit)
820 {
821 VkResult result;
822
823 bool u_trace_enabled = u_trace_context_actively_tracing(&queue->device->trace_context);
824 bool has_trace_points = false;
825
826 struct vk_command_buffer **vk_cmd_buffers = vk_submit->command_buffers;
827
828 memset(new_submit, 0, sizeof(struct tu_queue_submit));
829
830 new_submit->cmd_buffers = (void *)vk_cmd_buffers;
831 new_submit->nr_cmd_buffers = vk_submit->command_buffer_count;
832 tu_insert_dynamic_cmdbufs(queue->device, &new_submit->cmd_buffers,
833 &new_submit->nr_cmd_buffers);
834
835 uint32_t entry_count = 0;
836 for (uint32_t j = 0; j < new_submit->nr_cmd_buffers; ++j) {
837 struct tu_cmd_buffer *cmdbuf = new_submit->cmd_buffers[j];
838
839 if (perf_pass_index != ~0)
840 entry_count++;
841
842 entry_count += cmdbuf->cs.entry_count;
843
844 if (u_trace_enabled && u_trace_has_points(&cmdbuf->trace)) {
845 if (!(cmdbuf->usage_flags & VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT))
846 entry_count++;
847
848 has_trace_points = true;
849 }
850 }
851
852 new_submit->autotune_fence =
853 tu_autotune_submit_requires_fence(new_submit->cmd_buffers, new_submit->nr_cmd_buffers);
854 if (new_submit->autotune_fence)
855 entry_count++;
856
857 new_submit->cmds = vk_zalloc(&queue->device->vk.alloc,
858 entry_count * sizeof(*new_submit->cmds), 8,
859 VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
860
861 if (new_submit->cmds == NULL) {
862 result = vk_error(queue, VK_ERROR_OUT_OF_HOST_MEMORY);
863 goto fail_cmds;
864 }
865
866 if (has_trace_points) {
867 result =
868 tu_u_trace_submission_data_create(
869 queue->device, new_submit->cmd_buffers,
870 new_submit->nr_cmd_buffers,
871 &new_submit->u_trace_submission_data);
872
873 if (result != VK_SUCCESS) {
874 goto fail_u_trace_submission_data;
875 }
876 }
877
878 /* Allocate without wait timeline semaphores */
879 new_submit->in_syncobjs = vk_zalloc(&queue->device->vk.alloc,
880 nr_in_syncobjs * sizeof(*new_submit->in_syncobjs), 8,
881 VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
882
883 if (new_submit->in_syncobjs == NULL) {
884 result = vk_error(queue, VK_ERROR_OUT_OF_HOST_MEMORY);
885 goto fail_in_syncobjs;
886 }
887
888 /* Allocate with signal timeline semaphores considered */
889 new_submit->out_syncobjs = vk_zalloc(&queue->device->vk.alloc,
890 nr_out_syncobjs * sizeof(*new_submit->out_syncobjs), 8,
891 VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
892
893 if (new_submit->out_syncobjs == NULL) {
894 result = vk_error(queue, VK_ERROR_OUT_OF_HOST_MEMORY);
895 goto fail_out_syncobjs;
896 }
897
898 new_submit->entry_count = entry_count;
899 new_submit->nr_in_syncobjs = nr_in_syncobjs;
900 new_submit->nr_out_syncobjs = nr_out_syncobjs;
901 new_submit->perf_pass_index = perf_pass_index;
902 new_submit->vk_submit = vk_submit;
903
904 return VK_SUCCESS;
905
906 fail_out_syncobjs:
907 vk_free(&queue->device->vk.alloc, new_submit->in_syncobjs);
908 fail_in_syncobjs:
909 if (new_submit->u_trace_submission_data)
910 tu_u_trace_submission_data_finish(queue->device,
911 new_submit->u_trace_submission_data);
912 fail_u_trace_submission_data:
913 vk_free(&queue->device->vk.alloc, new_submit->cmds);
914 fail_cmds:
915 return result;
916 }
917
918 static void
tu_queue_submit_finish(struct tu_queue * queue,struct tu_queue_submit * submit)919 tu_queue_submit_finish(struct tu_queue *queue, struct tu_queue_submit *submit)
920 {
921 vk_free(&queue->device->vk.alloc, submit->cmds);
922 vk_free(&queue->device->vk.alloc, submit->in_syncobjs);
923 vk_free(&queue->device->vk.alloc, submit->out_syncobjs);
924 if (submit->cmd_buffers != (void *) submit->vk_submit->command_buffers)
925 vk_free(&queue->device->vk.alloc, submit->cmd_buffers);
926 }
927
928 static void
tu_fill_msm_gem_submit(struct tu_device * dev,struct drm_msm_gem_submit_cmd * cmd,struct tu_cs_entry * cs_entry)929 tu_fill_msm_gem_submit(struct tu_device *dev,
930 struct drm_msm_gem_submit_cmd *cmd,
931 struct tu_cs_entry *cs_entry)
932 {
933 cmd->type = MSM_SUBMIT_CMD_BUF;
934 cmd->submit_idx = cs_entry->bo->bo_list_idx;
935 cmd->submit_offset = cs_entry->offset;
936 cmd->size = cs_entry->size;
937 cmd->pad = 0;
938 cmd->nr_relocs = 0;
939 cmd->relocs = 0;
940 }
941
942 static void
tu_queue_build_msm_gem_submit_cmds(struct tu_queue * queue,struct tu_queue_submit * submit,struct tu_cs * autotune_cs)943 tu_queue_build_msm_gem_submit_cmds(struct tu_queue *queue,
944 struct tu_queue_submit *submit,
945 struct tu_cs *autotune_cs)
946 {
947 struct tu_device *dev = queue->device;
948 struct drm_msm_gem_submit_cmd *cmds = submit->cmds;
949
950 uint32_t entry_idx = 0;
951 for (uint32_t j = 0; j < submit->nr_cmd_buffers; ++j) {
952 struct tu_device *dev = queue->device;
953 struct tu_cmd_buffer *cmdbuf = submit->cmd_buffers[j];
954 struct tu_cs *cs = &cmdbuf->cs;
955
956 if (submit->perf_pass_index != ~0) {
957 struct tu_cs_entry *perf_cs_entry =
958 &dev->perfcntrs_pass_cs_entries[submit->perf_pass_index];
959
960 tu_fill_msm_gem_submit(dev, &cmds[entry_idx], perf_cs_entry);
961 entry_idx++;
962 }
963
964 for (unsigned i = 0; i < cs->entry_count; ++i, ++entry_idx) {
965 tu_fill_msm_gem_submit(dev, &cmds[entry_idx], &cs->entries[i]);
966 }
967
968 if (submit->u_trace_submission_data) {
969 struct tu_cs *ts_cs =
970 submit->u_trace_submission_data->cmd_trace_data[j].timestamp_copy_cs;
971 if (ts_cs) {
972 tu_fill_msm_gem_submit(dev, &cmds[entry_idx], &ts_cs->entries[0]);
973 entry_idx++;
974 }
975 }
976 }
977
978 if (autotune_cs) {
979 assert(autotune_cs->entry_count == 1);
980 tu_fill_msm_gem_submit(dev, &cmds[entry_idx], &autotune_cs->entries[0]);
981 entry_idx++;
982 }
983 }
984
985 static VkResult
tu_queue_submit_locked(struct tu_queue * queue,struct tu_queue_submit * submit)986 tu_queue_submit_locked(struct tu_queue *queue, struct tu_queue_submit *submit)
987 {
988 queue->device->submit_count++;
989
990 struct tu_cs *autotune_cs = NULL;
991 if (submit->autotune_fence) {
992 autotune_cs = tu_autotune_on_submit(queue->device,
993 &queue->device->autotune,
994 submit->cmd_buffers,
995 submit->nr_cmd_buffers);
996 }
997
998 uint32_t flags = MSM_PIPE_3D0;
999
1000 if (submit->vk_submit->wait_count)
1001 flags |= MSM_SUBMIT_SYNCOBJ_IN;
1002
1003 if (submit->vk_submit->signal_count)
1004 flags |= MSM_SUBMIT_SYNCOBJ_OUT;
1005
1006 mtx_lock(&queue->device->bo_mutex);
1007
1008 if (queue->device->implicit_sync_bo_count == 0)
1009 flags |= MSM_SUBMIT_NO_IMPLICIT;
1010
1011 /* drm_msm_gem_submit_cmd requires index of bo which could change at any
1012 * time when bo_mutex is not locked. So we build submit cmds here the real
1013 * place to submit.
1014 */
1015 tu_queue_build_msm_gem_submit_cmds(queue, submit, autotune_cs);
1016
1017 struct drm_msm_gem_submit req = {
1018 .flags = flags,
1019 .queueid = queue->msm_queue_id,
1020 .bos = (uint64_t)(uintptr_t) queue->device->bo_list,
1021 .nr_bos = submit->entry_count ? queue->device->bo_count : 0,
1022 .cmds = (uint64_t)(uintptr_t)submit->cmds,
1023 .nr_cmds = submit->entry_count,
1024 .in_syncobjs = (uint64_t)(uintptr_t)submit->in_syncobjs,
1025 .out_syncobjs = (uint64_t)(uintptr_t)submit->out_syncobjs,
1026 .nr_in_syncobjs = submit->nr_in_syncobjs,
1027 .nr_out_syncobjs = submit->nr_out_syncobjs,
1028 .syncobj_stride = sizeof(struct drm_msm_gem_submit_syncobj),
1029 };
1030
1031 int ret = drmCommandWriteRead(queue->device->fd,
1032 DRM_MSM_GEM_SUBMIT,
1033 &req, sizeof(req));
1034
1035 mtx_unlock(&queue->device->bo_mutex);
1036
1037 if (ret)
1038 return vk_device_set_lost(&queue->device->vk, "submit failed: %m");
1039
1040 #if HAVE_PERFETTO
1041 tu_perfetto_submit(queue->device, queue->device->submit_count);
1042 #endif
1043
1044 if (submit->u_trace_submission_data) {
1045 struct tu_u_trace_submission_data *submission_data =
1046 submit->u_trace_submission_data;
1047 submission_data->submission_id = queue->device->submit_count;
1048 /* We have to allocate it here since it is different between drm/kgsl */
1049 submission_data->syncobj =
1050 vk_alloc(&queue->device->vk.alloc, sizeof(struct tu_u_trace_syncobj),
1051 8, VK_SYSTEM_ALLOCATION_SCOPE_DEVICE);
1052 submission_data->syncobj->fence = req.fence;
1053 submission_data->syncobj->msm_queue_id = queue->msm_queue_id;
1054
1055 submit->u_trace_submission_data = NULL;
1056
1057 for (uint32_t i = 0; i < submission_data->cmd_buffer_count; i++) {
1058 bool free_data = i == submission_data->last_buffer_with_tracepoints;
1059 if (submission_data->cmd_trace_data[i].trace)
1060 u_trace_flush(submission_data->cmd_trace_data[i].trace,
1061 submission_data, free_data);
1062
1063 if (!submission_data->cmd_trace_data[i].timestamp_copy_cs) {
1064 /* u_trace is owned by cmd_buffer */
1065 submission_data->cmd_trace_data[i].trace = NULL;
1066 }
1067 }
1068 }
1069
1070 for (uint32_t i = 0; i < submit->vk_submit->wait_count; i++) {
1071 if (!vk_sync_is_tu_timeline_sync(submit->vk_submit->waits[i].sync))
1072 continue;
1073
1074 struct tu_timeline_sync *sync =
1075 container_of(submit->vk_submit->waits[i].sync, struct tu_timeline_sync, base);
1076
1077 assert(sync->state != TU_TIMELINE_SYNC_STATE_RESET);
1078
1079 /* Set SIGNALED to the state of the wait timeline sync since this means the syncobj
1080 * is done and ready again so this can be garbage-collectioned later.
1081 */
1082 sync->state = TU_TIMELINE_SYNC_STATE_SIGNALED;
1083 }
1084
1085 for (uint32_t i = 0; i < submit->vk_submit->signal_count; i++) {
1086 if (!vk_sync_is_tu_timeline_sync(submit->vk_submit->signals[i].sync))
1087 continue;
1088
1089 struct tu_timeline_sync *sync =
1090 container_of(submit->vk_submit->signals[i].sync, struct tu_timeline_sync, base);
1091
1092 assert(sync->state == TU_TIMELINE_SYNC_STATE_RESET);
1093 /* Set SUBMITTED to the state of the signal timeline sync so we could wait for
1094 * this timeline sync until completed if necessary.
1095 */
1096 sync->state = TU_TIMELINE_SYNC_STATE_SUBMITTED;
1097 }
1098
1099 pthread_cond_broadcast(&queue->device->timeline_cond);
1100
1101 return VK_SUCCESS;
1102 }
1103
1104 static inline void
get_abs_timeout(struct drm_msm_timespec * tv,uint64_t ns)1105 get_abs_timeout(struct drm_msm_timespec *tv, uint64_t ns)
1106 {
1107 struct timespec t;
1108 clock_gettime(CLOCK_MONOTONIC, &t);
1109 tv->tv_sec = t.tv_sec + ns / 1000000000;
1110 tv->tv_nsec = t.tv_nsec + ns % 1000000000;
1111 }
1112
1113 VkResult
tu_device_wait_u_trace(struct tu_device * dev,struct tu_u_trace_syncobj * syncobj)1114 tu_device_wait_u_trace(struct tu_device *dev, struct tu_u_trace_syncobj *syncobj)
1115 {
1116 struct drm_msm_wait_fence req = {
1117 .fence = syncobj->fence,
1118 .queueid = syncobj->msm_queue_id,
1119 };
1120 int ret;
1121
1122 get_abs_timeout(&req.timeout, 1000000000);
1123
1124 ret = drmCommandWrite(dev->fd, DRM_MSM_WAIT_FENCE, &req, sizeof(req));
1125 if (ret && (ret != -ETIMEDOUT)) {
1126 fprintf(stderr, "wait-fence failed! %d (%s)", ret, strerror(errno));
1127 return VK_TIMEOUT;
1128 }
1129
1130 return VK_SUCCESS;
1131 }
1132
1133 VkResult
tu_queue_submit(struct vk_queue * vk_queue,struct vk_queue_submit * submit)1134 tu_queue_submit(struct vk_queue *vk_queue, struct vk_queue_submit *submit)
1135 {
1136 struct tu_queue *queue = container_of(vk_queue, struct tu_queue, vk);
1137 uint32_t perf_pass_index = queue->device->perfcntrs_pass_cs ?
1138 submit->perf_pass_index : ~0;
1139 struct tu_queue_submit submit_req;
1140
1141 if (unlikely(queue->device->physical_device->instance->debug_flags &
1142 TU_DEBUG_LOG_SKIP_GMEM_OPS)) {
1143 tu_dbg_log_gmem_load_store_skips(queue->device);
1144 }
1145
1146 pthread_mutex_lock(&queue->device->submit_mutex);
1147
1148 VkResult ret = tu_queue_submit_create_locked(queue, submit,
1149 submit->wait_count, submit->signal_count,
1150 perf_pass_index, &submit_req);
1151
1152 if (ret != VK_SUCCESS) {
1153 pthread_mutex_unlock(&queue->device->submit_mutex);
1154 return ret;
1155 }
1156
1157 /* note: assuming there won't be any very large semaphore counts */
1158 struct drm_msm_gem_submit_syncobj *in_syncobjs = submit_req.in_syncobjs;
1159 struct drm_msm_gem_submit_syncobj *out_syncobjs = submit_req.out_syncobjs;
1160
1161 uint32_t nr_in_syncobjs = 0, nr_out_syncobjs = 0;
1162
1163 for (uint32_t i = 0; i < submit->wait_count; i++) {
1164 struct vk_sync *sync = submit->waits[i].sync;
1165
1166 in_syncobjs[nr_in_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {
1167 .handle = tu_syncobj_from_vk_sync(sync),
1168 .flags = 0,
1169 };
1170 }
1171
1172 for (uint32_t i = 0; i < submit->signal_count; i++) {
1173 struct vk_sync *sync = submit->signals[i].sync;
1174
1175 out_syncobjs[nr_out_syncobjs++] = (struct drm_msm_gem_submit_syncobj) {
1176 .handle = tu_syncobj_from_vk_sync(sync),
1177 .flags = 0,
1178 };
1179 }
1180
1181 ret = tu_queue_submit_locked(queue, &submit_req);
1182
1183 pthread_mutex_unlock(&queue->device->submit_mutex);
1184 tu_queue_submit_finish(queue, &submit_req);
1185
1186 if (ret != VK_SUCCESS)
1187 return ret;
1188
1189 u_trace_context_process(&queue->device->trace_context, true);
1190
1191 return VK_SUCCESS;
1192 }
1193
1194 int
tu_syncobj_to_fd(struct tu_device * device,struct vk_sync * sync)1195 tu_syncobj_to_fd(struct tu_device *device, struct vk_sync *sync)
1196 {
1197 VkResult ret;
1198 int fd;
1199 ret = vk_sync_export_opaque_fd(&device->vk, sync, &fd);
1200 return ret ? -1 : fd;
1201 }
1202