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