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 static void
vn_cs_encoder_sanity_check(struct vn_cs_encoder * enc)12 vn_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
34 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)35 vn_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
55 static void
vn_cs_encoder_commit_buffer(struct vn_cs_encoder * enc)56 vn_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
70 static void
vn_cs_encoder_gc_buffers(struct vn_cs_encoder * enc)71 vn_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
90 void
vn_cs_encoder_init_indirect(struct vn_cs_encoder * enc,struct vn_instance * instance,size_t min_size)91 vn_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
101 void
vn_cs_encoder_fini(struct vn_cs_encoder * enc)102 vn_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 */
116 void
vn_cs_encoder_reset(struct vn_cs_encoder * enc)117 vn_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
124 static uint32_t
next_array_size(uint32_t cur_size,uint32_t min_size)125 next_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
131 static size_t
next_buffer_size(size_t cur_size,size_t min_size,size_t need)132 next_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
143 static bool
vn_cs_encoder_grow_buffer_array(struct vn_cs_encoder * enc)144 vn_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 */
163 bool
vn_cs_encoder_reserve_internal(struct vn_cs_encoder * enc,size_t size)164 vn_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 */
217 void
vn_cs_encoder_commit(struct vn_cs_encoder * enc)218 vn_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