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
11 struct vn_cs_renderer_protocol_info _vn_cs_renderer_protocol_info = {
12 .mutex = _SIMPLE_MTX_INITIALIZER_NP,
13 };
14
15 static void
vn_cs_renderer_protocol_info_init_once(struct vn_instance * instance)16 vn_cs_renderer_protocol_info_init_once(struct vn_instance *instance)
17 {
18 const struct vn_renderer_info *renderer_info = &instance->renderer->info;
19 /* assume renderer protocol supports all extensions if bit 0 is not set */
20 const bool support_all_exts =
21 !vn_info_extension_mask_test(renderer_info->vk_extension_mask, 0);
22
23 _vn_cs_renderer_protocol_info.api_version = renderer_info->vk_xml_version;
24
25 STATIC_ASSERT(sizeof(renderer_info->vk_extension_mask) >=
26 sizeof(_vn_cs_renderer_protocol_info.extension_bitset));
27
28 for (uint32_t i = 1; i <= VN_INFO_EXTENSION_MAX_NUMBER; i++) {
29 /* use protocl helper to ensure mask decoding matches encoding */
30 if (support_all_exts ||
31 vn_info_extension_mask_test(renderer_info->vk_extension_mask, i))
32 BITSET_SET(_vn_cs_renderer_protocol_info.extension_bitset, i);
33 }
34 }
35
36 void
vn_cs_renderer_protocol_info_init(struct vn_instance * instance)37 vn_cs_renderer_protocol_info_init(struct vn_instance *instance)
38 {
39 simple_mtx_lock(&_vn_cs_renderer_protocol_info.mutex);
40 if (_vn_cs_renderer_protocol_info.init_once) {
41 simple_mtx_unlock(&_vn_cs_renderer_protocol_info.mutex);
42 return;
43 }
44
45 vn_cs_renderer_protocol_info_init_once(instance);
46
47 _vn_cs_renderer_protocol_info.init_once = true;
48 simple_mtx_unlock(&_vn_cs_renderer_protocol_info.mutex);
49 }
50
51 static void
vn_cs_encoder_sanity_check(struct vn_cs_encoder * enc)52 vn_cs_encoder_sanity_check(struct vn_cs_encoder *enc)
53 {
54 assert(enc->buffer_count <= enc->buffer_max);
55
56 size_t total_committed_size = 0;
57 for (uint32_t i = 0; i < enc->buffer_count; i++)
58 total_committed_size += enc->buffers[i].committed_size;
59 assert(enc->total_committed_size == total_committed_size);
60
61 if (enc->buffer_count) {
62 const struct vn_cs_encoder_buffer *cur_buf =
63 &enc->buffers[enc->buffer_count - 1];
64 assert(cur_buf->base <= enc->cur && enc->cur <= enc->end &&
65 enc->end <= cur_buf->base + enc->current_buffer_size);
66 if (cur_buf->committed_size)
67 assert(enc->cur == enc->end);
68 } else {
69 assert(!enc->current_buffer_size);
70 assert(!enc->cur && !enc->end);
71 }
72 }
73
74 static void
vn_cs_encoder_add_buffer(struct vn_cs_encoder * enc,struct vn_renderer_shmem * shmem,size_t offset,void * base,size_t size)75 vn_cs_encoder_add_buffer(struct vn_cs_encoder *enc,
76 struct vn_renderer_shmem *shmem,
77 size_t offset,
78 void *base,
79 size_t size)
80 {
81 /* add a buffer and make it current */
82 assert(enc->buffer_count < enc->buffer_max);
83 struct vn_cs_encoder_buffer *cur_buf = &enc->buffers[enc->buffer_count++];
84 /* shmem ownership transferred */
85 cur_buf->shmem = shmem;
86 cur_buf->offset = offset;
87 cur_buf->base = base;
88 cur_buf->committed_size = 0;
89
90 /* update the write pointer */
91 enc->cur = base;
92 enc->end = base + size;
93 }
94
95 static void
vn_cs_encoder_commit_buffer(struct vn_cs_encoder * enc)96 vn_cs_encoder_commit_buffer(struct vn_cs_encoder *enc)
97 {
98 assert(enc->buffer_count);
99 struct vn_cs_encoder_buffer *cur_buf =
100 &enc->buffers[enc->buffer_count - 1];
101 const size_t written_size = enc->cur - cur_buf->base;
102 if (cur_buf->committed_size) {
103 assert(cur_buf->committed_size == written_size);
104 } else {
105 cur_buf->committed_size = written_size;
106 enc->total_committed_size += written_size;
107 }
108 }
109
110 static void
vn_cs_encoder_gc_buffers(struct vn_cs_encoder * enc)111 vn_cs_encoder_gc_buffers(struct vn_cs_encoder *enc)
112 {
113 /* when the shmem pool is used, no need to cache the shmem in cs */
114 if (enc->storage_type == VN_CS_ENCODER_STORAGE_SHMEM_POOL) {
115 for (uint32_t i = 0; i < enc->buffer_count; i++) {
116 vn_renderer_shmem_unref(enc->instance->renderer,
117 enc->buffers[i].shmem);
118 }
119
120 enc->buffer_count = 0;
121 enc->total_committed_size = 0;
122 enc->current_buffer_size = 0;
123
124 enc->cur = NULL;
125 enc->end = NULL;
126
127 return;
128 }
129
130 /* free all but the current buffer */
131 assert(enc->buffer_count);
132 struct vn_cs_encoder_buffer *cur_buf =
133 &enc->buffers[enc->buffer_count - 1];
134 for (uint32_t i = 0; i < enc->buffer_count - 1; i++)
135 vn_renderer_shmem_unref(enc->instance->renderer, enc->buffers[i].shmem);
136
137 /* move the current buffer to the beginning, skipping the used part */
138 const size_t used = cur_buf->offset + cur_buf->committed_size;
139 enc->buffer_count = 0;
140 vn_cs_encoder_add_buffer(enc, cur_buf->shmem, used,
141 cur_buf->base + cur_buf->committed_size,
142 enc->current_buffer_size - used);
143
144 enc->total_committed_size = 0;
145 }
146
147 void
vn_cs_encoder_init(struct vn_cs_encoder * enc,struct vn_instance * instance,enum vn_cs_encoder_storage_type storage_type,size_t min_size)148 vn_cs_encoder_init(struct vn_cs_encoder *enc,
149 struct vn_instance *instance,
150 enum vn_cs_encoder_storage_type storage_type,
151 size_t min_size)
152 {
153 /* VN_CS_ENCODER_INITIALIZER* should be used instead */
154 assert(storage_type != VN_CS_ENCODER_STORAGE_POINTER);
155
156 memset(enc, 0, sizeof(*enc));
157 enc->instance = instance;
158 enc->storage_type = storage_type;
159 enc->min_buffer_size = min_size;
160 }
161
162 void
vn_cs_encoder_fini(struct vn_cs_encoder * enc)163 vn_cs_encoder_fini(struct vn_cs_encoder *enc)
164 {
165 if (unlikely(enc->storage_type == VN_CS_ENCODER_STORAGE_POINTER))
166 return;
167
168 for (uint32_t i = 0; i < enc->buffer_count; i++)
169 vn_renderer_shmem_unref(enc->instance->renderer, enc->buffers[i].shmem);
170 if (enc->buffers)
171 free(enc->buffers);
172 }
173
174 /**
175 * Reset a cs for reuse.
176 */
177 void
vn_cs_encoder_reset(struct vn_cs_encoder * enc)178 vn_cs_encoder_reset(struct vn_cs_encoder *enc)
179 {
180 /* enc->error is sticky */
181 if (likely(enc->buffer_count))
182 vn_cs_encoder_gc_buffers(enc);
183 }
184
185 static uint32_t
next_array_size(uint32_t cur_size,uint32_t min_size)186 next_array_size(uint32_t cur_size, uint32_t min_size)
187 {
188 const uint32_t next_size = cur_size ? cur_size * 2 : min_size;
189 return next_size > cur_size ? next_size : 0;
190 }
191
192 static size_t
next_buffer_size(size_t cur_size,size_t min_size,size_t need)193 next_buffer_size(size_t cur_size, size_t min_size, size_t need)
194 {
195 size_t next_size = cur_size ? cur_size * 2 : min_size;
196 while (next_size < need) {
197 next_size *= 2;
198 if (!next_size)
199 return 0;
200 }
201 return next_size;
202 }
203
204 static bool
vn_cs_encoder_grow_buffer_array(struct vn_cs_encoder * enc)205 vn_cs_encoder_grow_buffer_array(struct vn_cs_encoder *enc)
206 {
207 const uint32_t buf_max = next_array_size(enc->buffer_max, 4);
208 if (!buf_max)
209 return false;
210
211 void *bufs = realloc(enc->buffers, sizeof(*enc->buffers) * buf_max);
212 if (!bufs)
213 return false;
214
215 enc->buffers = bufs;
216 enc->buffer_max = buf_max;
217
218 return true;
219 }
220
221 /**
222 * Add a new vn_cs_encoder_buffer to a cs.
223 */
224 bool
vn_cs_encoder_reserve_internal(struct vn_cs_encoder * enc,size_t size)225 vn_cs_encoder_reserve_internal(struct vn_cs_encoder *enc, size_t size)
226 {
227 VN_TRACE_FUNC();
228 if (unlikely(enc->storage_type == VN_CS_ENCODER_STORAGE_POINTER))
229 return false;
230
231 if (enc->buffer_count >= enc->buffer_max) {
232 if (!vn_cs_encoder_grow_buffer_array(enc))
233 return false;
234 assert(enc->buffer_count < enc->buffer_max);
235 }
236
237 size_t buf_size = 0;
238 if (likely(enc->buffer_count)) {
239 vn_cs_encoder_commit_buffer(enc);
240
241 if (enc->storage_type == VN_CS_ENCODER_STORAGE_SHMEM_ARRAY) {
242 /* if the current buffer is reused from the last vn_cs_encoder_reset
243 * (i.e., offset != 0), do not double the size
244 *
245 * TODO better strategy to grow buffer size
246 */
247 const struct vn_cs_encoder_buffer *cur_buf =
248 &enc->buffers[enc->buffer_count - 1];
249 if (cur_buf->offset)
250 buf_size = next_buffer_size(0, enc->current_buffer_size, size);
251 }
252 }
253
254 if (!buf_size) {
255 /* double the size */
256 buf_size = next_buffer_size(enc->current_buffer_size,
257 enc->min_buffer_size, size);
258 if (!buf_size)
259 return false;
260 }
261
262 struct vn_renderer_shmem *shmem;
263 size_t buf_offset;
264 if (enc->storage_type == VN_CS_ENCODER_STORAGE_SHMEM_ARRAY) {
265 shmem = vn_renderer_shmem_create(enc->instance->renderer, buf_size);
266 buf_offset = 0;
267 } else {
268 assert(enc->storage_type == VN_CS_ENCODER_STORAGE_SHMEM_POOL);
269 shmem =
270 vn_instance_cs_shmem_alloc(enc->instance, buf_size, &buf_offset);
271 }
272 if (!shmem)
273 return false;
274
275 if (unlikely(!enc->instance->renderer->info.supports_blob_id_0)) {
276 uint32_t roundtrip;
277 VkResult result =
278 vn_instance_submit_roundtrip(enc->instance, &roundtrip);
279 if (result != VK_SUCCESS) {
280 vn_renderer_shmem_unref(enc->instance->renderer, shmem);
281 return false;
282 }
283
284 enc->current_buffer_roundtrip = roundtrip;
285 }
286
287 vn_cs_encoder_add_buffer(enc, shmem, buf_offset,
288 shmem->mmap_ptr + buf_offset, buf_size);
289 enc->current_buffer_size = buf_size;
290
291 vn_cs_encoder_sanity_check(enc);
292
293 return true;
294 }
295
296 /*
297 * Commit written data.
298 */
299 void
vn_cs_encoder_commit(struct vn_cs_encoder * enc)300 vn_cs_encoder_commit(struct vn_cs_encoder *enc)
301 {
302 if (likely(enc->buffer_count)) {
303 vn_cs_encoder_commit_buffer(enc);
304
305 /* trigger the slow path on next vn_cs_encoder_reserve */
306 enc->end = enc->cur;
307 }
308
309 vn_cs_encoder_sanity_check(enc);
310 }
311