1/* 2 * Copyright 2019 Google LLC 3 * SPDX-License-Identifier: MIT 4 */ 5 6#include "vn_cs.h" 7 8#include "vn_instance.h" 9#include "vn_renderer.h" 10 11static void 12vn_cs_encoder_sanity_check(struct vn_cs_encoder *enc) 13{ 14 assert(enc->buffer_count <= enc->buffer_max); 15 16 size_t total_committed_size = 0; 17 for (uint32_t i = 0; i < enc->buffer_count; i++) 18 total_committed_size += enc->buffers[i].committed_size; 19 assert(enc->total_committed_size == total_committed_size); 20 21 if (enc->buffer_count) { 22 const struct vn_cs_encoder_buffer *cur_buf = 23 &enc->buffers[enc->buffer_count - 1]; 24 assert(cur_buf->base <= enc->cur && enc->cur <= enc->end && 25 enc->end <= cur_buf->base + enc->current_buffer_size); 26 if (cur_buf->committed_size) 27 assert(enc->cur == enc->end); 28 } else { 29 assert(!enc->current_buffer_size); 30 assert(!enc->cur && !enc->end); 31 } 32} 33 34static void 35vn_cs_encoder_add_buffer(struct vn_cs_encoder *enc, 36 struct vn_renderer_shmem *shmem, 37 size_t offset, 38 void *base, 39 size_t size) 40{ 41 /* add a buffer and make it current */ 42 assert(enc->buffer_count < enc->buffer_max); 43 struct vn_cs_encoder_buffer *cur_buf = &enc->buffers[enc->buffer_count++]; 44 /* shmem ownership transferred */ 45 cur_buf->shmem = shmem; 46 cur_buf->offset = offset; 47 cur_buf->base = base; 48 cur_buf->committed_size = 0; 49 50 /* update the write pointer */ 51 enc->cur = base; 52 enc->end = base + size; 53} 54 55static void 56vn_cs_encoder_commit_buffer(struct vn_cs_encoder *enc) 57{ 58 assert(enc->buffer_count); 59 struct vn_cs_encoder_buffer *cur_buf = 60 &enc->buffers[enc->buffer_count - 1]; 61 const size_t written_size = enc->cur - cur_buf->base; 62 if (cur_buf->committed_size) { 63 assert(cur_buf->committed_size == written_size); 64 } else { 65 cur_buf->committed_size = written_size; 66 enc->total_committed_size += written_size; 67 } 68} 69 70static void 71vn_cs_encoder_gc_buffers(struct vn_cs_encoder *enc) 72{ 73 /* free all but the current buffer */ 74 assert(enc->buffer_count); 75 struct vn_cs_encoder_buffer *cur_buf = 76 &enc->buffers[enc->buffer_count - 1]; 77 for (uint32_t i = 0; i < enc->buffer_count - 1; i++) 78 vn_renderer_shmem_unref(enc->instance->renderer, enc->buffers[i].shmem); 79 80 /* move the current buffer to the beginning, skipping the used part */ 81 const size_t used = cur_buf->offset + cur_buf->committed_size; 82 enc->buffer_count = 0; 83 vn_cs_encoder_add_buffer(enc, cur_buf->shmem, used, 84 cur_buf->base + cur_buf->committed_size, 85 enc->current_buffer_size - used); 86 87 enc->total_committed_size = 0; 88} 89 90void 91vn_cs_encoder_init_indirect(struct vn_cs_encoder *enc, 92 struct vn_instance *instance, 93 size_t min_size) 94{ 95 memset(enc, 0, sizeof(*enc)); 96 enc->instance = instance; 97 enc->min_buffer_size = min_size; 98 enc->indirect = true; 99} 100 101void 102vn_cs_encoder_fini(struct vn_cs_encoder *enc) 103{ 104 if (unlikely(!enc->indirect)) 105 return; 106 107 for (uint32_t i = 0; i < enc->buffer_count; i++) 108 vn_renderer_shmem_unref(enc->instance->renderer, enc->buffers[i].shmem); 109 if (enc->buffers) 110 free(enc->buffers); 111} 112 113/** 114 * Reset a cs for reuse. 115 */ 116void 117vn_cs_encoder_reset(struct vn_cs_encoder *enc) 118{ 119 /* enc->error is sticky */ 120 if (likely(enc->buffer_count)) 121 vn_cs_encoder_gc_buffers(enc); 122} 123 124static uint32_t 125next_array_size(uint32_t cur_size, uint32_t min_size) 126{ 127 const uint32_t next_size = cur_size ? cur_size * 2 : min_size; 128 return next_size > cur_size ? next_size : 0; 129} 130 131static size_t 132next_buffer_size(size_t cur_size, size_t min_size, size_t need) 133{ 134 size_t next_size = cur_size ? cur_size * 2 : min_size; 135 while (next_size < need) { 136 next_size *= 2; 137 if (!next_size) 138 return 0; 139 } 140 return next_size; 141} 142 143static bool 144vn_cs_encoder_grow_buffer_array(struct vn_cs_encoder *enc) 145{ 146 const uint32_t buf_max = next_array_size(enc->buffer_max, 4); 147 if (!buf_max) 148 return false; 149 150 void *bufs = realloc(enc->buffers, sizeof(*enc->buffers) * buf_max); 151 if (!bufs) 152 return false; 153 154 enc->buffers = bufs; 155 enc->buffer_max = buf_max; 156 157 return true; 158} 159 160/** 161 * Add a new vn_cs_encoder_buffer to a cs. 162 */ 163bool 164vn_cs_encoder_reserve_internal(struct vn_cs_encoder *enc, size_t size) 165{ 166 if (unlikely(!enc->indirect)) 167 return false; 168 169 if (enc->buffer_count >= enc->buffer_max) { 170 if (!vn_cs_encoder_grow_buffer_array(enc)) 171 return false; 172 assert(enc->buffer_count < enc->buffer_max); 173 } 174 175 size_t buf_size = 0; 176 if (likely(enc->buffer_count)) { 177 vn_cs_encoder_commit_buffer(enc); 178 179 /* TODO better strategy to grow buffer size */ 180 const struct vn_cs_encoder_buffer *cur_buf = 181 &enc->buffers[enc->buffer_count - 1]; 182 if (cur_buf->offset) 183 buf_size = next_buffer_size(0, enc->current_buffer_size, size); 184 } 185 186 if (!buf_size) { 187 buf_size = next_buffer_size(enc->current_buffer_size, 188 enc->min_buffer_size, size); 189 if (!buf_size) 190 return false; 191 } 192 193 struct vn_renderer_shmem *shmem = 194 vn_renderer_shmem_create(enc->instance->renderer, buf_size); 195 if (!shmem) 196 return false; 197 198 uint32_t roundtrip; 199 VkResult result = vn_instance_submit_roundtrip(enc->instance, &roundtrip); 200 if (result != VK_SUCCESS) { 201 vn_renderer_shmem_unref(enc->instance->renderer, shmem); 202 return false; 203 } 204 205 vn_cs_encoder_add_buffer(enc, shmem, 0, shmem->mmap_ptr, buf_size); 206 enc->current_buffer_size = buf_size; 207 enc->current_buffer_roundtrip = roundtrip; 208 209 vn_cs_encoder_sanity_check(enc); 210 211 return true; 212} 213 214/* 215 * Commit written data. 216 */ 217void 218vn_cs_encoder_commit(struct vn_cs_encoder *enc) 219{ 220 if (likely(enc->buffer_count)) { 221 vn_cs_encoder_commit_buffer(enc); 222 223 /* trigger the slow path on next vn_cs_encoder_reserve */ 224 enc->end = enc->cur; 225 } 226 227 vn_cs_encoder_sanity_check(enc); 228} 229