1 /*
2 * Copyright 2021 Google LLC
3 * SPDX-License-Identifier: MIT
4 */
5
6 #include "vn_ring.h"
7
8 #include "venus-protocol/vn_protocol_driver_transport.h"
9
10 #include "vn_cs.h"
11 #include "vn_instance.h"
12 #include "vn_renderer.h"
13
14 static_assert(ATOMIC_INT_LOCK_FREE == 2 && sizeof(atomic_uint) == 4,
15 "vn_ring_shared requires lock-free 32-bit atomic_uint");
16
17 /* pointers to a ring in a BO */
18 struct vn_ring_shared {
19 const volatile atomic_uint *head;
20 volatile atomic_uint *tail;
21 volatile atomic_uint *status;
22 void *buffer;
23 void *extra;
24 };
25
26 struct vn_ring {
27 uint64_t id;
28 struct vn_instance *instance;
29 struct vn_renderer_shmem *shmem;
30
31 uint32_t buffer_size;
32 uint32_t buffer_mask;
33
34 struct vn_ring_shared shared;
35 uint32_t cur;
36
37 /* This mutex ensures below:
38 * - atomic of ring submission
39 * - reply shmem resource set and ring submission are paired
40 */
41 mtx_t mutex;
42
43 /* size limit for cmd submission via ring shmem, derived from
44 * (buffer_size >> direct_order) upon vn_ring_create
45 */
46 uint32_t direct_size;
47
48 /* used for indirect submission of large command (non-VkCommandBuffer) */
49 struct vn_cs_encoder upload;
50
51 struct list_head submits;
52 struct list_head free_submits;
53 };
54
55 struct vn_ring_submit {
56 uint32_t seqno;
57
58 struct list_head head;
59
60 /* BOs to keep alive (TODO make sure shmems are pinned) */
61 uint32_t shmem_count;
62 struct vn_renderer_shmem *shmems[];
63 };
64
65 struct vn_ring_submission {
66 const struct vn_cs_encoder *cs;
67 struct vn_ring_submit *submit;
68
69 struct {
70 struct vn_cs_encoder cs;
71 struct vn_cs_encoder_buffer buffer;
72 uint32_t data[64];
73 } indirect;
74 };
75
76 static uint32_t
vn_ring_load_head(const struct vn_ring * ring)77 vn_ring_load_head(const struct vn_ring *ring)
78 {
79 /* the renderer is expected to store the head with memory_order_release,
80 * forming a release-acquire ordering
81 */
82 return atomic_load_explicit(ring->shared.head, memory_order_acquire);
83 }
84
85 static void
vn_ring_store_tail(struct vn_ring * ring)86 vn_ring_store_tail(struct vn_ring *ring)
87 {
88 /* the renderer is expected to load the tail with memory_order_acquire,
89 * forming a release-acquire ordering
90 */
91 return atomic_store_explicit(ring->shared.tail, ring->cur,
92 memory_order_release);
93 }
94
95 uint32_t
vn_ring_load_status(const struct vn_ring * ring)96 vn_ring_load_status(const struct vn_ring *ring)
97 {
98 /* must be called and ordered after vn_ring_store_tail for idle status */
99 return atomic_load_explicit(ring->shared.status, memory_order_seq_cst);
100 }
101
102 void
vn_ring_unset_status_bits(struct vn_ring * ring,uint32_t mask)103 vn_ring_unset_status_bits(struct vn_ring *ring, uint32_t mask)
104 {
105 atomic_fetch_and_explicit(ring->shared.status, ~mask,
106 memory_order_seq_cst);
107 }
108
109 static void
vn_ring_write_buffer(struct vn_ring * ring,const void * data,uint32_t size)110 vn_ring_write_buffer(struct vn_ring *ring, const void *data, uint32_t size)
111 {
112 assert(ring->cur + size - vn_ring_load_head(ring) <= ring->buffer_size);
113
114 const uint32_t offset = ring->cur & ring->buffer_mask;
115 if (offset + size <= ring->buffer_size) {
116 memcpy(ring->shared.buffer + offset, data, size);
117 } else {
118 const uint32_t s = ring->buffer_size - offset;
119 memcpy(ring->shared.buffer + offset, data, s);
120 memcpy(ring->shared.buffer, data + s, size - s);
121 }
122
123 ring->cur += size;
124 }
125
126 static bool
vn_ring_ge_seqno(const struct vn_ring * ring,uint32_t a,uint32_t b)127 vn_ring_ge_seqno(const struct vn_ring *ring, uint32_t a, uint32_t b)
128 {
129 /* this can return false negative when not called fast enough (e.g., when
130 * called once every couple hours), but following calls with larger a's
131 * will correct itself
132 *
133 * TODO use real seqnos?
134 */
135 if (a >= b)
136 return ring->cur >= a || ring->cur < b;
137 else
138 return ring->cur >= a && ring->cur < b;
139 }
140
141 static void
vn_ring_retire_submits(struct vn_ring * ring,uint32_t seqno)142 vn_ring_retire_submits(struct vn_ring *ring, uint32_t seqno)
143 {
144 struct vn_renderer *renderer = ring->instance->renderer;
145 list_for_each_entry_safe(struct vn_ring_submit, submit, &ring->submits,
146 head) {
147 if (!vn_ring_ge_seqno(ring, seqno, submit->seqno))
148 break;
149
150 for (uint32_t i = 0; i < submit->shmem_count; i++)
151 vn_renderer_shmem_unref(renderer, submit->shmems[i]);
152
153 list_move_to(&submit->head, &ring->free_submits);
154 }
155 }
156
157 bool
vn_ring_get_seqno_status(struct vn_ring * ring,uint32_t seqno)158 vn_ring_get_seqno_status(struct vn_ring *ring, uint32_t seqno)
159 {
160 return vn_ring_ge_seqno(ring, vn_ring_load_head(ring), seqno);
161 }
162
163 static void
vn_ring_wait_seqno(struct vn_ring * ring,uint32_t seqno)164 vn_ring_wait_seqno(struct vn_ring *ring, uint32_t seqno)
165 {
166 /* A renderer wait incurs several hops and the renderer might poll
167 * repeatedly anyway. Let's just poll here.
168 */
169 struct vn_relax_state relax_state =
170 vn_relax_init(ring->instance, "ring seqno");
171 do {
172 if (vn_ring_get_seqno_status(ring, seqno)) {
173 vn_relax_fini(&relax_state);
174 return;
175 }
176 vn_relax(&relax_state);
177 } while (true);
178 }
179
180 void
vn_ring_wait_all(struct vn_ring * ring)181 vn_ring_wait_all(struct vn_ring *ring)
182 {
183 /* load from tail rather than ring->cur for atomicity */
184 const uint32_t pending_seqno =
185 atomic_load_explicit(ring->shared.tail, memory_order_relaxed);
186 vn_ring_wait_seqno(ring, pending_seqno);
187 }
188
189 static bool
vn_ring_has_space(const struct vn_ring * ring,uint32_t size,uint32_t * out_head)190 vn_ring_has_space(const struct vn_ring *ring,
191 uint32_t size,
192 uint32_t *out_head)
193 {
194 const uint32_t head = vn_ring_load_head(ring);
195 if (likely(ring->cur + size - head <= ring->buffer_size)) {
196 *out_head = head;
197 return true;
198 }
199
200 return false;
201 }
202
203 static uint32_t
vn_ring_wait_space(struct vn_ring * ring,uint32_t size)204 vn_ring_wait_space(struct vn_ring *ring, uint32_t size)
205 {
206 assert(size <= ring->buffer_size);
207
208 uint32_t head;
209 if (likely(vn_ring_has_space(ring, size, &head)))
210 return head;
211
212 {
213 VN_TRACE_FUNC();
214
215 /* see the reasoning in vn_ring_wait_seqno */
216 struct vn_relax_state relax_state =
217 vn_relax_init(ring->instance, "ring space");
218 do {
219 vn_relax(&relax_state);
220 if (vn_ring_has_space(ring, size, &head)) {
221 vn_relax_fini(&relax_state);
222 return head;
223 }
224 } while (true);
225 }
226 }
227
228 void
vn_ring_get_layout(size_t buf_size,size_t extra_size,struct vn_ring_layout * layout)229 vn_ring_get_layout(size_t buf_size,
230 size_t extra_size,
231 struct vn_ring_layout *layout)
232 {
233 /* this can be changed/extended quite freely */
234 struct layout {
235 alignas(64) uint32_t head;
236 alignas(64) uint32_t tail;
237 alignas(64) uint32_t status;
238
239 alignas(64) uint8_t buffer[];
240 };
241
242 assert(buf_size && util_is_power_of_two_or_zero(buf_size));
243
244 layout->head_offset = offsetof(struct layout, head);
245 layout->tail_offset = offsetof(struct layout, tail);
246 layout->status_offset = offsetof(struct layout, status);
247
248 layout->buffer_offset = offsetof(struct layout, buffer);
249 layout->buffer_size = buf_size;
250
251 layout->extra_offset = layout->buffer_offset + layout->buffer_size;
252 layout->extra_size = extra_size;
253
254 layout->shmem_size = layout->extra_offset + layout->extra_size;
255 }
256
257 struct vn_ring *
vn_ring_create(struct vn_instance * instance,const struct vn_ring_layout * layout,uint8_t direct_order)258 vn_ring_create(struct vn_instance *instance,
259 const struct vn_ring_layout *layout,
260 uint8_t direct_order)
261 {
262 VN_TRACE_FUNC();
263
264 const VkAllocationCallbacks *alloc = &instance->base.base.alloc;
265
266 struct vn_ring *ring = vk_zalloc(alloc, sizeof(*ring), VN_DEFAULT_ALIGN,
267 VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
268 if (!ring)
269 return NULL;
270
271 ring->id = (uintptr_t)ring;
272 ring->instance = instance;
273 ring->shmem =
274 vn_renderer_shmem_create(instance->renderer, layout->shmem_size);
275 if (!ring->shmem) {
276 if (VN_DEBUG(INIT))
277 vn_log(instance, "failed to allocate/map ring shmem");
278 vk_free(alloc, ring);
279 return NULL;
280 }
281
282 void *shared = ring->shmem->mmap_ptr;
283 memset(shared, 0, layout->shmem_size);
284
285 assert(layout->buffer_size &&
286 util_is_power_of_two_or_zero(layout->buffer_size));
287 ring->buffer_size = layout->buffer_size;
288 ring->buffer_mask = ring->buffer_size - 1;
289
290 ring->shared.head = shared + layout->head_offset;
291 ring->shared.tail = shared + layout->tail_offset;
292 ring->shared.status = shared + layout->status_offset;
293 ring->shared.buffer = shared + layout->buffer_offset;
294 ring->shared.extra = shared + layout->extra_offset;
295
296 mtx_init(&ring->mutex, mtx_plain);
297
298 ring->direct_size = layout->buffer_size >> direct_order;
299 assert(ring->direct_size);
300
301 vn_cs_encoder_init(&ring->upload, instance,
302 VN_CS_ENCODER_STORAGE_SHMEM_ARRAY, 1 * 1024 * 1024);
303
304 list_inithead(&ring->submits);
305 list_inithead(&ring->free_submits);
306
307 const struct VkRingMonitorInfoMESA monitor_info = {
308 .sType = VK_STRUCTURE_TYPE_RING_MONITOR_INFO_MESA,
309 .maxReportingPeriodMicroseconds = VN_WATCHDOG_REPORT_PERIOD_US,
310 };
311 const struct VkRingCreateInfoMESA info = {
312 .sType = VK_STRUCTURE_TYPE_RING_CREATE_INFO_MESA,
313 .pNext = &monitor_info,
314 .resourceId = ring->shmem->res_id,
315 .size = layout->shmem_size,
316 .idleTimeout = 5ull * 1000 * 1000,
317 .headOffset = layout->head_offset,
318 .tailOffset = layout->tail_offset,
319 .statusOffset = layout->status_offset,
320 .bufferOffset = layout->buffer_offset,
321 .bufferSize = layout->buffer_size,
322 .extraOffset = layout->extra_offset,
323 .extraSize = layout->extra_size,
324 };
325
326 uint32_t create_ring_data[64];
327 struct vn_cs_encoder local_enc = VN_CS_ENCODER_INITIALIZER_LOCAL(
328 create_ring_data, sizeof(create_ring_data));
329 vn_encode_vkCreateRingMESA(&local_enc, 0, ring->id, &info);
330 vn_renderer_submit_simple(instance->renderer, create_ring_data,
331 vn_cs_encoder_get_len(&local_enc));
332
333 return ring;
334 }
335
336 void
vn_ring_destroy(struct vn_ring * ring)337 vn_ring_destroy(struct vn_ring *ring)
338 {
339 VN_TRACE_FUNC();
340
341 const VkAllocationCallbacks *alloc = &ring->instance->base.base.alloc;
342
343 uint32_t destroy_ring_data[4];
344 struct vn_cs_encoder local_enc = VN_CS_ENCODER_INITIALIZER_LOCAL(
345 destroy_ring_data, sizeof(destroy_ring_data));
346 vn_encode_vkDestroyRingMESA(&local_enc, 0, ring->id);
347 vn_renderer_submit_simple(ring->instance->renderer, destroy_ring_data,
348 vn_cs_encoder_get_len(&local_enc));
349
350 vn_ring_retire_submits(ring, ring->cur);
351 assert(list_is_empty(&ring->submits));
352
353 list_for_each_entry_safe(struct vn_ring_submit, submit,
354 &ring->free_submits, head)
355 vk_free(alloc, submit);
356
357 vn_cs_encoder_fini(&ring->upload);
358 vn_renderer_shmem_unref(ring->instance->renderer, ring->shmem);
359
360 mtx_destroy(&ring->mutex);
361
362 vk_free(alloc, ring);
363 }
364
365 uint64_t
vn_ring_get_id(struct vn_ring * ring)366 vn_ring_get_id(struct vn_ring *ring)
367 {
368 return ring->id;
369 }
370
371 static struct vn_ring_submit *
vn_ring_get_submit(struct vn_ring * ring,uint32_t shmem_count)372 vn_ring_get_submit(struct vn_ring *ring, uint32_t shmem_count)
373 {
374 const VkAllocationCallbacks *alloc = &ring->instance->base.base.alloc;
375 const uint32_t min_shmem_count = 2;
376 struct vn_ring_submit *submit;
377
378 /* TODO this could be simplified if we could omit shmem_count */
379 if (shmem_count <= min_shmem_count &&
380 !list_is_empty(&ring->free_submits)) {
381 submit =
382 list_first_entry(&ring->free_submits, struct vn_ring_submit, head);
383 list_del(&submit->head);
384 } else {
385 const size_t submit_size = offsetof(
386 struct vn_ring_submit, shmems[MAX2(shmem_count, min_shmem_count)]);
387 submit = vk_alloc(alloc, submit_size, VN_DEFAULT_ALIGN,
388 VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
389 }
390
391 return submit;
392 }
393
394 static bool
vn_ring_submit_internal(struct vn_ring * ring,struct vn_ring_submit * submit,const struct vn_cs_encoder * cs,uint32_t * seqno)395 vn_ring_submit_internal(struct vn_ring *ring,
396 struct vn_ring_submit *submit,
397 const struct vn_cs_encoder *cs,
398 uint32_t *seqno)
399 {
400 /* write cs to the ring */
401 assert(!vn_cs_encoder_is_empty(cs));
402
403 /* avoid -Wmaybe-unitialized */
404 uint32_t cur_seqno = 0;
405
406 for (uint32_t i = 0; i < cs->buffer_count; i++) {
407 const struct vn_cs_encoder_buffer *buf = &cs->buffers[i];
408 cur_seqno = vn_ring_wait_space(ring, buf->committed_size);
409 vn_ring_write_buffer(ring, buf->base, buf->committed_size);
410 }
411
412 vn_ring_store_tail(ring);
413 const VkRingStatusFlagsMESA status = vn_ring_load_status(ring);
414 if (status & VK_RING_STATUS_FATAL_BIT_MESA) {
415 vn_log(NULL, "vn_ring_submit abort on fatal");
416 abort();
417 }
418
419 vn_ring_retire_submits(ring, cur_seqno);
420
421 submit->seqno = ring->cur;
422 list_addtail(&submit->head, &ring->submits);
423
424 *seqno = submit->seqno;
425
426 /* notify renderer to wake up ring if idle */
427 return status & VK_RING_STATUS_IDLE_BIT_MESA;
428 }
429
430 static const struct vn_cs_encoder *
vn_ring_submission_get_cs(struct vn_ring * ring,struct vn_ring_submission * submit,const struct vn_cs_encoder * cs,bool direct)431 vn_ring_submission_get_cs(struct vn_ring *ring,
432 struct vn_ring_submission *submit,
433 const struct vn_cs_encoder *cs,
434 bool direct)
435 {
436 if (direct)
437 return cs;
438
439 STACK_ARRAY(VkCommandStreamDescriptionMESA, descs, cs->buffer_count);
440
441 uint32_t desc_count = 0;
442 for (uint32_t i = 0; i < cs->buffer_count; i++) {
443 const struct vn_cs_encoder_buffer *buf = &cs->buffers[i];
444 if (buf->committed_size) {
445 descs[desc_count++] = (VkCommandStreamDescriptionMESA){
446 .resourceId = buf->shmem->res_id,
447 .offset = buf->offset,
448 .size = buf->committed_size,
449 };
450 }
451 }
452
453 const size_t exec_size = vn_sizeof_vkExecuteCommandStreamsMESA(
454 desc_count, descs, NULL, 0, NULL, 0);
455 void *exec_data = submit->indirect.data;
456 if (exec_size > sizeof(submit->indirect.data)) {
457 const VkAllocationCallbacks *alloc = &ring->instance->base.base.alloc;
458 exec_data = vk_alloc(alloc, exec_size, VN_DEFAULT_ALIGN,
459 VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
460 if (!exec_data) {
461 STACK_ARRAY_FINISH(descs);
462 return NULL;
463 }
464 }
465
466 submit->indirect.buffer = VN_CS_ENCODER_BUFFER_INITIALIZER(exec_data);
467 submit->indirect.cs =
468 VN_CS_ENCODER_INITIALIZER(&submit->indirect.buffer, exec_size);
469 vn_encode_vkExecuteCommandStreamsMESA(&submit->indirect.cs, 0, desc_count,
470 descs, NULL, 0, NULL, 0);
471 vn_cs_encoder_commit(&submit->indirect.cs);
472
473 STACK_ARRAY_FINISH(descs);
474
475 return &submit->indirect.cs;
476 }
477
478 static struct vn_ring_submit *
vn_ring_submission_get_ring_submit(struct vn_ring * ring,const struct vn_cs_encoder * cs,struct vn_renderer_shmem * extra_shmem,bool direct)479 vn_ring_submission_get_ring_submit(struct vn_ring *ring,
480 const struct vn_cs_encoder *cs,
481 struct vn_renderer_shmem *extra_shmem,
482 bool direct)
483 {
484 struct vn_renderer *renderer = ring->instance->renderer;
485 const uint32_t shmem_count =
486 (direct ? 0 : cs->buffer_count) + (extra_shmem ? 1 : 0);
487 struct vn_ring_submit *submit = vn_ring_get_submit(ring, shmem_count);
488 if (!submit)
489 return NULL;
490
491 submit->shmem_count = shmem_count;
492 if (!direct) {
493 for (uint32_t i = 0; i < cs->buffer_count; i++) {
494 submit->shmems[i] =
495 vn_renderer_shmem_ref(renderer, cs->buffers[i].shmem);
496 }
497 }
498 if (extra_shmem) {
499 submit->shmems[shmem_count - 1] =
500 vn_renderer_shmem_ref(renderer, extra_shmem);
501 }
502
503 return submit;
504 }
505
506 static inline void
vn_ring_submission_cleanup(struct vn_ring * ring,struct vn_ring_submission * submit)507 vn_ring_submission_cleanup(struct vn_ring *ring,
508 struct vn_ring_submission *submit)
509 {
510 const VkAllocationCallbacks *alloc = &ring->instance->base.base.alloc;
511 if (submit->cs == &submit->indirect.cs &&
512 submit->indirect.buffer.base != submit->indirect.data)
513 vk_free(alloc, submit->indirect.buffer.base);
514 }
515
516 static VkResult
vn_ring_submission_prepare(struct vn_ring * ring,struct vn_ring_submission * submit,const struct vn_cs_encoder * cs,struct vn_renderer_shmem * extra_shmem,bool direct)517 vn_ring_submission_prepare(struct vn_ring *ring,
518 struct vn_ring_submission *submit,
519 const struct vn_cs_encoder *cs,
520 struct vn_renderer_shmem *extra_shmem,
521 bool direct)
522 {
523 submit->cs = vn_ring_submission_get_cs(ring, submit, cs, direct);
524 if (!submit->cs)
525 return VK_ERROR_OUT_OF_HOST_MEMORY;
526
527 submit->submit =
528 vn_ring_submission_get_ring_submit(ring, cs, extra_shmem, direct);
529 if (!submit->submit) {
530 vn_ring_submission_cleanup(ring, submit);
531 return VK_ERROR_OUT_OF_HOST_MEMORY;
532 }
533
534 return VK_SUCCESS;
535 }
536
537 static inline bool
vn_ring_submission_can_direct(const struct vn_ring * ring,const struct vn_cs_encoder * cs)538 vn_ring_submission_can_direct(const struct vn_ring *ring,
539 const struct vn_cs_encoder *cs)
540 {
541 return vn_cs_encoder_get_len(cs) <= ring->direct_size;
542 }
543
544 static struct vn_cs_encoder *
vn_ring_cs_upload_locked(struct vn_ring * ring,const struct vn_cs_encoder * cs)545 vn_ring_cs_upload_locked(struct vn_ring *ring, const struct vn_cs_encoder *cs)
546 {
547 VN_TRACE_FUNC();
548 assert(cs->storage_type == VN_CS_ENCODER_STORAGE_POINTER &&
549 cs->buffer_count == 1);
550 const void *cs_data = cs->buffers[0].base;
551 const size_t cs_size = cs->total_committed_size;
552 assert(cs_size == vn_cs_encoder_get_len(cs));
553
554 struct vn_cs_encoder *upload = &ring->upload;
555 vn_cs_encoder_reset(upload);
556
557 if (!vn_cs_encoder_reserve(upload, cs_size))
558 return NULL;
559
560 vn_cs_encoder_write(upload, cs_size, cs_data, cs_size);
561 vn_cs_encoder_commit(upload);
562
563 return upload;
564 }
565
566 static VkResult
vn_ring_submit_locked(struct vn_ring * ring,const struct vn_cs_encoder * cs,struct vn_renderer_shmem * extra_shmem,uint32_t * ring_seqno)567 vn_ring_submit_locked(struct vn_ring *ring,
568 const struct vn_cs_encoder *cs,
569 struct vn_renderer_shmem *extra_shmem,
570 uint32_t *ring_seqno)
571 {
572 const bool direct = vn_ring_submission_can_direct(ring, cs);
573 if (!direct && cs->storage_type == VN_CS_ENCODER_STORAGE_POINTER) {
574 cs = vn_ring_cs_upload_locked(ring, cs);
575 if (!cs)
576 return VK_ERROR_OUT_OF_HOST_MEMORY;
577 assert(cs->storage_type != VN_CS_ENCODER_STORAGE_POINTER);
578 }
579
580 struct vn_ring_submission submit;
581 VkResult result =
582 vn_ring_submission_prepare(ring, &submit, cs, extra_shmem, direct);
583 if (result != VK_SUCCESS)
584 return result;
585
586 uint32_t seqno;
587 const bool notify =
588 vn_ring_submit_internal(ring, submit.submit, submit.cs, &seqno);
589 if (notify) {
590 uint32_t notify_ring_data[8];
591 struct vn_cs_encoder local_enc = VN_CS_ENCODER_INITIALIZER_LOCAL(
592 notify_ring_data, sizeof(notify_ring_data));
593 vn_encode_vkNotifyRingMESA(&local_enc, 0, ring->id, seqno, 0);
594 vn_renderer_submit_simple(ring->instance->renderer, notify_ring_data,
595 vn_cs_encoder_get_len(&local_enc));
596 }
597
598 vn_ring_submission_cleanup(ring, &submit);
599
600 if (ring_seqno)
601 *ring_seqno = seqno;
602
603 return VK_SUCCESS;
604 }
605
606 VkResult
vn_ring_submit_command_simple(struct vn_ring * ring,const struct vn_cs_encoder * cs)607 vn_ring_submit_command_simple(struct vn_ring *ring,
608 const struct vn_cs_encoder *cs)
609 {
610 mtx_lock(&ring->mutex);
611 VkResult result = vn_ring_submit_locked(ring, cs, NULL, NULL);
612 mtx_unlock(&ring->mutex);
613
614 return result;
615 }
616
617 static inline void
vn_ring_set_reply_shmem_locked(struct vn_ring * ring,struct vn_renderer_shmem * shmem,size_t offset,size_t size)618 vn_ring_set_reply_shmem_locked(struct vn_ring *ring,
619 struct vn_renderer_shmem *shmem,
620 size_t offset,
621 size_t size)
622 {
623
624 uint32_t set_reply_command_stream_data[16];
625 struct vn_cs_encoder local_enc = VN_CS_ENCODER_INITIALIZER_LOCAL(
626 set_reply_command_stream_data, sizeof(set_reply_command_stream_data));
627 const struct VkCommandStreamDescriptionMESA stream = {
628 .resourceId = shmem->res_id,
629 .offset = offset,
630 .size = size,
631 };
632 vn_encode_vkSetReplyCommandStreamMESA(&local_enc, 0, &stream);
633 vn_cs_encoder_commit(&local_enc);
634 vn_ring_submit_locked(ring, &local_enc, NULL, NULL);
635 }
636
637 void
vn_ring_submit_command(struct vn_ring * ring,struct vn_ring_submit_command * submit)638 vn_ring_submit_command(struct vn_ring *ring,
639 struct vn_ring_submit_command *submit)
640 {
641 assert(!vn_cs_encoder_is_empty(&submit->command));
642
643 vn_cs_encoder_commit(&submit->command);
644
645 size_t reply_offset = 0;
646 if (submit->reply_size) {
647 submit->reply_shmem = vn_instance_reply_shmem_alloc(
648 ring->instance, submit->reply_size, &reply_offset);
649 if (!submit->reply_shmem)
650 return;
651 }
652
653 mtx_lock(&ring->mutex);
654 if (submit->reply_size) {
655 vn_ring_set_reply_shmem_locked(ring, submit->reply_shmem, reply_offset,
656 submit->reply_size);
657 }
658 submit->ring_seqno_valid =
659 VK_SUCCESS == vn_ring_submit_locked(ring, &submit->command,
660 submit->reply_shmem,
661 &submit->ring_seqno);
662 mtx_unlock(&ring->mutex);
663
664 if (submit->reply_size) {
665 if (likely(submit->ring_seqno_valid)) {
666 void *reply_ptr = submit->reply_shmem->mmap_ptr + reply_offset;
667 submit->reply =
668 VN_CS_DECODER_INITIALIZER(reply_ptr, submit->reply_size);
669 vn_ring_wait_seqno(ring, submit->ring_seqno);
670 } else {
671 vn_renderer_shmem_unref(ring->instance->renderer,
672 submit->reply_shmem);
673 submit->reply_shmem = NULL;
674 }
675 }
676 }
677
678 void
vn_ring_free_command_reply(struct vn_ring * ring,struct vn_ring_submit_command * submit)679 vn_ring_free_command_reply(struct vn_ring *ring,
680 struct vn_ring_submit_command *submit)
681 {
682 assert(submit->reply_shmem);
683 vn_renderer_shmem_unref(ring->instance->renderer, submit->reply_shmem);
684 }
685