1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright 2021 Google LLC
3bf215546Sopenharmony_ci * SPDX-License-Identifier: MIT
4bf215546Sopenharmony_ci */
5bf215546Sopenharmony_ci
6bf215546Sopenharmony_ci#include "vn_ring.h"
7bf215546Sopenharmony_ci
8bf215546Sopenharmony_ci#include "vn_cs.h"
9bf215546Sopenharmony_ci#include "vn_renderer.h"
10bf215546Sopenharmony_ci
11bf215546Sopenharmony_cienum vn_ring_status_flag {
12bf215546Sopenharmony_ci   VN_RING_STATUS_IDLE = 1u << 0,
13bf215546Sopenharmony_ci};
14bf215546Sopenharmony_ci
15bf215546Sopenharmony_cistatic uint32_t
16bf215546Sopenharmony_civn_ring_load_head(const struct vn_ring *ring)
17bf215546Sopenharmony_ci{
18bf215546Sopenharmony_ci   /* the renderer is expected to store the head with memory_order_release,
19bf215546Sopenharmony_ci    * forming a release-acquire ordering
20bf215546Sopenharmony_ci    */
21bf215546Sopenharmony_ci   return atomic_load_explicit(ring->shared.head, memory_order_acquire);
22bf215546Sopenharmony_ci}
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_cistatic void
25bf215546Sopenharmony_civn_ring_store_tail(struct vn_ring *ring)
26bf215546Sopenharmony_ci{
27bf215546Sopenharmony_ci   /* the renderer is expected to load the tail with memory_order_acquire,
28bf215546Sopenharmony_ci    * forming a release-acquire ordering
29bf215546Sopenharmony_ci    */
30bf215546Sopenharmony_ci   return atomic_store_explicit(ring->shared.tail, ring->cur,
31bf215546Sopenharmony_ci                                memory_order_release);
32bf215546Sopenharmony_ci}
33bf215546Sopenharmony_ci
34bf215546Sopenharmony_cistatic uint32_t
35bf215546Sopenharmony_civn_ring_load_status(const struct vn_ring *ring)
36bf215546Sopenharmony_ci{
37bf215546Sopenharmony_ci   /* this must be called and ordered after vn_ring_store_tail */
38bf215546Sopenharmony_ci   return atomic_load_explicit(ring->shared.status, memory_order_seq_cst);
39bf215546Sopenharmony_ci}
40bf215546Sopenharmony_ci
41bf215546Sopenharmony_cistatic void
42bf215546Sopenharmony_civn_ring_write_buffer(struct vn_ring *ring, const void *data, uint32_t size)
43bf215546Sopenharmony_ci{
44bf215546Sopenharmony_ci   assert(ring->cur + size - vn_ring_load_head(ring) <= ring->buffer_size);
45bf215546Sopenharmony_ci
46bf215546Sopenharmony_ci   const uint32_t offset = ring->cur & ring->buffer_mask;
47bf215546Sopenharmony_ci   if (offset + size <= ring->buffer_size) {
48bf215546Sopenharmony_ci      memcpy(ring->shared.buffer + offset, data, size);
49bf215546Sopenharmony_ci   } else {
50bf215546Sopenharmony_ci      const uint32_t s = ring->buffer_size - offset;
51bf215546Sopenharmony_ci      memcpy(ring->shared.buffer + offset, data, s);
52bf215546Sopenharmony_ci      memcpy(ring->shared.buffer, data + s, size - s);
53bf215546Sopenharmony_ci   }
54bf215546Sopenharmony_ci
55bf215546Sopenharmony_ci   ring->cur += size;
56bf215546Sopenharmony_ci}
57bf215546Sopenharmony_ci
58bf215546Sopenharmony_cistatic bool
59bf215546Sopenharmony_civn_ring_ge_seqno(const struct vn_ring *ring, uint32_t a, uint32_t b)
60bf215546Sopenharmony_ci{
61bf215546Sopenharmony_ci   /* this can return false negative when not called fast enough (e.g., when
62bf215546Sopenharmony_ci    * called once every couple hours), but following calls with larger a's
63bf215546Sopenharmony_ci    * will correct itself
64bf215546Sopenharmony_ci    *
65bf215546Sopenharmony_ci    * TODO use real seqnos?
66bf215546Sopenharmony_ci    */
67bf215546Sopenharmony_ci   if (a >= b)
68bf215546Sopenharmony_ci      return ring->cur >= a || ring->cur < b;
69bf215546Sopenharmony_ci   else
70bf215546Sopenharmony_ci      return ring->cur >= a && ring->cur < b;
71bf215546Sopenharmony_ci}
72bf215546Sopenharmony_ci
73bf215546Sopenharmony_cistatic void
74bf215546Sopenharmony_civn_ring_retire_submits(struct vn_ring *ring, uint32_t seqno)
75bf215546Sopenharmony_ci{
76bf215546Sopenharmony_ci   list_for_each_entry_safe(struct vn_ring_submit, submit, &ring->submits,
77bf215546Sopenharmony_ci                            head) {
78bf215546Sopenharmony_ci      if (!vn_ring_ge_seqno(ring, seqno, submit->seqno))
79bf215546Sopenharmony_ci         break;
80bf215546Sopenharmony_ci
81bf215546Sopenharmony_ci      for (uint32_t i = 0; i < submit->shmem_count; i++)
82bf215546Sopenharmony_ci         vn_renderer_shmem_unref(ring->renderer, submit->shmems[i]);
83bf215546Sopenharmony_ci
84bf215546Sopenharmony_ci      list_del(&submit->head);
85bf215546Sopenharmony_ci      list_add(&submit->head, &ring->free_submits);
86bf215546Sopenharmony_ci   }
87bf215546Sopenharmony_ci}
88bf215546Sopenharmony_ci
89bf215546Sopenharmony_cistatic uint32_t
90bf215546Sopenharmony_civn_ring_wait_seqno(const struct vn_ring *ring, uint32_t seqno)
91bf215546Sopenharmony_ci{
92bf215546Sopenharmony_ci   /* A renderer wait incurs several hops and the renderer might poll
93bf215546Sopenharmony_ci    * repeatedly anyway.  Let's just poll here.
94bf215546Sopenharmony_ci    */
95bf215546Sopenharmony_ci   uint32_t iter = 0;
96bf215546Sopenharmony_ci   do {
97bf215546Sopenharmony_ci      const uint32_t head = vn_ring_load_head(ring);
98bf215546Sopenharmony_ci      if (vn_ring_ge_seqno(ring, head, seqno))
99bf215546Sopenharmony_ci         return head;
100bf215546Sopenharmony_ci      vn_relax(&iter, "ring seqno");
101bf215546Sopenharmony_ci   } while (true);
102bf215546Sopenharmony_ci}
103bf215546Sopenharmony_ci
104bf215546Sopenharmony_cistatic bool
105bf215546Sopenharmony_civn_ring_has_space(const struct vn_ring *ring,
106bf215546Sopenharmony_ci                  uint32_t size,
107bf215546Sopenharmony_ci                  uint32_t *out_head)
108bf215546Sopenharmony_ci{
109bf215546Sopenharmony_ci   const uint32_t head = vn_ring_load_head(ring);
110bf215546Sopenharmony_ci   if (likely(ring->cur + size - head <= ring->buffer_size)) {
111bf215546Sopenharmony_ci      *out_head = head;
112bf215546Sopenharmony_ci      return true;
113bf215546Sopenharmony_ci   }
114bf215546Sopenharmony_ci
115bf215546Sopenharmony_ci   return false;
116bf215546Sopenharmony_ci}
117bf215546Sopenharmony_ci
118bf215546Sopenharmony_cistatic uint32_t
119bf215546Sopenharmony_civn_ring_wait_space(const struct vn_ring *ring, uint32_t size)
120bf215546Sopenharmony_ci{
121bf215546Sopenharmony_ci   assert(size <= ring->buffer_size);
122bf215546Sopenharmony_ci
123bf215546Sopenharmony_ci   uint32_t head;
124bf215546Sopenharmony_ci   if (likely(vn_ring_has_space(ring, size, &head)))
125bf215546Sopenharmony_ci      return head;
126bf215546Sopenharmony_ci
127bf215546Sopenharmony_ci   {
128bf215546Sopenharmony_ci      VN_TRACE_FUNC();
129bf215546Sopenharmony_ci
130bf215546Sopenharmony_ci      /* see the reasoning in vn_ring_wait_seqno */
131bf215546Sopenharmony_ci      uint32_t iter = 0;
132bf215546Sopenharmony_ci      do {
133bf215546Sopenharmony_ci         vn_relax(&iter, "ring space");
134bf215546Sopenharmony_ci         if (vn_ring_has_space(ring, size, &head))
135bf215546Sopenharmony_ci            return head;
136bf215546Sopenharmony_ci      } while (true);
137bf215546Sopenharmony_ci   }
138bf215546Sopenharmony_ci}
139bf215546Sopenharmony_ci
140bf215546Sopenharmony_civoid
141bf215546Sopenharmony_civn_ring_get_layout(size_t buf_size,
142bf215546Sopenharmony_ci                   size_t extra_size,
143bf215546Sopenharmony_ci                   struct vn_ring_layout *layout)
144bf215546Sopenharmony_ci{
145bf215546Sopenharmony_ci   /* this can be changed/extended quite freely */
146bf215546Sopenharmony_ci   struct layout {
147bf215546Sopenharmony_ci      uint32_t head __attribute__((aligned(64)));
148bf215546Sopenharmony_ci      uint32_t tail __attribute__((aligned(64)));
149bf215546Sopenharmony_ci      uint32_t status __attribute__((aligned(64)));
150bf215546Sopenharmony_ci
151bf215546Sopenharmony_ci      uint8_t buffer[] __attribute__((aligned(64)));
152bf215546Sopenharmony_ci   };
153bf215546Sopenharmony_ci
154bf215546Sopenharmony_ci   assert(buf_size && util_is_power_of_two_or_zero(buf_size));
155bf215546Sopenharmony_ci
156bf215546Sopenharmony_ci   layout->head_offset = offsetof(struct layout, head);
157bf215546Sopenharmony_ci   layout->tail_offset = offsetof(struct layout, tail);
158bf215546Sopenharmony_ci   layout->status_offset = offsetof(struct layout, status);
159bf215546Sopenharmony_ci
160bf215546Sopenharmony_ci   layout->buffer_offset = offsetof(struct layout, buffer);
161bf215546Sopenharmony_ci   layout->buffer_size = buf_size;
162bf215546Sopenharmony_ci
163bf215546Sopenharmony_ci   layout->extra_offset = layout->buffer_offset + layout->buffer_size;
164bf215546Sopenharmony_ci   layout->extra_size = extra_size;
165bf215546Sopenharmony_ci
166bf215546Sopenharmony_ci   layout->shmem_size = layout->extra_offset + layout->extra_size;
167bf215546Sopenharmony_ci}
168bf215546Sopenharmony_ci
169bf215546Sopenharmony_civoid
170bf215546Sopenharmony_civn_ring_init(struct vn_ring *ring,
171bf215546Sopenharmony_ci             struct vn_renderer *renderer,
172bf215546Sopenharmony_ci             const struct vn_ring_layout *layout,
173bf215546Sopenharmony_ci             void *shared)
174bf215546Sopenharmony_ci{
175bf215546Sopenharmony_ci   memset(ring, 0, sizeof(*ring));
176bf215546Sopenharmony_ci   memset(shared, 0, layout->shmem_size);
177bf215546Sopenharmony_ci
178bf215546Sopenharmony_ci   ring->renderer = renderer;
179bf215546Sopenharmony_ci
180bf215546Sopenharmony_ci   assert(layout->buffer_size &&
181bf215546Sopenharmony_ci          util_is_power_of_two_or_zero(layout->buffer_size));
182bf215546Sopenharmony_ci   ring->buffer_size = layout->buffer_size;
183bf215546Sopenharmony_ci   ring->buffer_mask = ring->buffer_size - 1;
184bf215546Sopenharmony_ci
185bf215546Sopenharmony_ci   ring->shared.head = shared + layout->head_offset;
186bf215546Sopenharmony_ci   ring->shared.tail = shared + layout->tail_offset;
187bf215546Sopenharmony_ci   ring->shared.status = shared + layout->status_offset;
188bf215546Sopenharmony_ci   ring->shared.buffer = shared + layout->buffer_offset;
189bf215546Sopenharmony_ci   ring->shared.extra = shared + layout->extra_offset;
190bf215546Sopenharmony_ci
191bf215546Sopenharmony_ci   list_inithead(&ring->submits);
192bf215546Sopenharmony_ci   list_inithead(&ring->free_submits);
193bf215546Sopenharmony_ci}
194bf215546Sopenharmony_ci
195bf215546Sopenharmony_civoid
196bf215546Sopenharmony_civn_ring_fini(struct vn_ring *ring)
197bf215546Sopenharmony_ci{
198bf215546Sopenharmony_ci   vn_ring_retire_submits(ring, ring->cur);
199bf215546Sopenharmony_ci   assert(list_is_empty(&ring->submits));
200bf215546Sopenharmony_ci
201bf215546Sopenharmony_ci   list_for_each_entry_safe(struct vn_ring_submit, submit,
202bf215546Sopenharmony_ci                            &ring->free_submits, head)
203bf215546Sopenharmony_ci      free(submit);
204bf215546Sopenharmony_ci}
205bf215546Sopenharmony_ci
206bf215546Sopenharmony_cistruct vn_ring_submit *
207bf215546Sopenharmony_civn_ring_get_submit(struct vn_ring *ring, uint32_t shmem_count)
208bf215546Sopenharmony_ci{
209bf215546Sopenharmony_ci   const uint32_t min_shmem_count = 2;
210bf215546Sopenharmony_ci   struct vn_ring_submit *submit;
211bf215546Sopenharmony_ci
212bf215546Sopenharmony_ci   /* TODO this could be simplified if we could omit shmem_count */
213bf215546Sopenharmony_ci   if (shmem_count <= min_shmem_count &&
214bf215546Sopenharmony_ci       !list_is_empty(&ring->free_submits)) {
215bf215546Sopenharmony_ci      submit =
216bf215546Sopenharmony_ci         list_first_entry(&ring->free_submits, struct vn_ring_submit, head);
217bf215546Sopenharmony_ci      list_del(&submit->head);
218bf215546Sopenharmony_ci   } else {
219bf215546Sopenharmony_ci      shmem_count = MAX2(shmem_count, min_shmem_count);
220bf215546Sopenharmony_ci      submit =
221bf215546Sopenharmony_ci         malloc(sizeof(*submit) + sizeof(submit->shmems[0]) * shmem_count);
222bf215546Sopenharmony_ci   }
223bf215546Sopenharmony_ci
224bf215546Sopenharmony_ci   return submit;
225bf215546Sopenharmony_ci}
226bf215546Sopenharmony_ci
227bf215546Sopenharmony_cibool
228bf215546Sopenharmony_civn_ring_submit(struct vn_ring *ring,
229bf215546Sopenharmony_ci               struct vn_ring_submit *submit,
230bf215546Sopenharmony_ci               const struct vn_cs_encoder *cs,
231bf215546Sopenharmony_ci               uint32_t *seqno)
232bf215546Sopenharmony_ci{
233bf215546Sopenharmony_ci   /* write cs to the ring */
234bf215546Sopenharmony_ci   assert(!vn_cs_encoder_is_empty(cs));
235bf215546Sopenharmony_ci   uint32_t cur_seqno;
236bf215546Sopenharmony_ci   for (uint32_t i = 0; i < cs->buffer_count; i++) {
237bf215546Sopenharmony_ci      const struct vn_cs_encoder_buffer *buf = &cs->buffers[i];
238bf215546Sopenharmony_ci      cur_seqno = vn_ring_wait_space(ring, buf->committed_size);
239bf215546Sopenharmony_ci      vn_ring_write_buffer(ring, buf->base, buf->committed_size);
240bf215546Sopenharmony_ci   }
241bf215546Sopenharmony_ci
242bf215546Sopenharmony_ci   vn_ring_store_tail(ring);
243bf215546Sopenharmony_ci   const bool notify = vn_ring_load_status(ring) & VN_RING_STATUS_IDLE;
244bf215546Sopenharmony_ci
245bf215546Sopenharmony_ci   vn_ring_retire_submits(ring, cur_seqno);
246bf215546Sopenharmony_ci
247bf215546Sopenharmony_ci   submit->seqno = ring->cur;
248bf215546Sopenharmony_ci   list_addtail(&submit->head, &ring->submits);
249bf215546Sopenharmony_ci
250bf215546Sopenharmony_ci   *seqno = submit->seqno;
251bf215546Sopenharmony_ci   return notify;
252bf215546Sopenharmony_ci}
253bf215546Sopenharmony_ci
254bf215546Sopenharmony_ci/**
255bf215546Sopenharmony_ci * This is thread-safe.
256bf215546Sopenharmony_ci */
257bf215546Sopenharmony_civoid
258bf215546Sopenharmony_civn_ring_wait(const struct vn_ring *ring, uint32_t seqno)
259bf215546Sopenharmony_ci{
260bf215546Sopenharmony_ci   vn_ring_wait_seqno(ring, seqno);
261bf215546Sopenharmony_ci}
262bf215546Sopenharmony_ci
263bf215546Sopenharmony_civoid
264bf215546Sopenharmony_civn_ring_wait_all(const struct vn_ring *ring)
265bf215546Sopenharmony_ci{
266bf215546Sopenharmony_ci   /* load from tail rather than ring->cur for atomicity */
267bf215546Sopenharmony_ci   const uint32_t pending_seqno =
268bf215546Sopenharmony_ci      atomic_load_explicit(ring->shared.tail, memory_order_relaxed);
269bf215546Sopenharmony_ci   vn_ring_wait(ring, pending_seqno);
270bf215546Sopenharmony_ci}
271