• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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