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