1 #include "common/msg.h"
2 #include "video/out/vo.h"
3 #include "utils.h"
4 
5 // Standard parallel 2D projection, except y1 < y0 means that the coordinate
6 // system is flipped, not the projection.
gl_transform_ortho(struct gl_transform * t,float x0,float x1,float y0,float y1)7 void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
8                         float y0, float y1)
9 {
10     if (y1 < y0) {
11         float tmp = y0;
12         y0 = tmp - y1;
13         y1 = tmp;
14     }
15 
16     t->m[0][0] = 2.0f / (x1 - x0);
17     t->m[0][1] = 0.0f;
18     t->m[1][0] = 0.0f;
19     t->m[1][1] = 2.0f / (y1 - y0);
20     t->t[0] = -(x1 + x0) / (x1 - x0);
21     t->t[1] = -(y1 + y0) / (y1 - y0);
22 }
23 
24 // Apply the effects of one transformation to another, transforming it in the
25 // process. In other words: post-composes t onto x
gl_transform_trans(struct gl_transform t,struct gl_transform * x)26 void gl_transform_trans(struct gl_transform t, struct gl_transform *x)
27 {
28     struct gl_transform xt = *x;
29     x->m[0][0] = t.m[0][0] * xt.m[0][0] + t.m[0][1] * xt.m[1][0];
30     x->m[1][0] = t.m[1][0] * xt.m[0][0] + t.m[1][1] * xt.m[1][0];
31     x->m[0][1] = t.m[0][0] * xt.m[0][1] + t.m[0][1] * xt.m[1][1];
32     x->m[1][1] = t.m[1][0] * xt.m[0][1] + t.m[1][1] * xt.m[1][1];
33     gl_transform_vec(t, &x->t[0], &x->t[1]);
34 }
35 
gl_transform_ortho_fbo(struct gl_transform * t,struct ra_fbo fbo)36 void gl_transform_ortho_fbo(struct gl_transform *t, struct ra_fbo fbo)
37 {
38     int y_dir = fbo.flip ? -1 : 1;
39     gl_transform_ortho(t, 0, fbo.tex->params.w, 0, fbo.tex->params.h * y_dir);
40 }
41 
ra_buf_pool_uninit(struct ra * ra,struct ra_buf_pool * pool)42 void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool)
43 {
44     for (int i = 0; i < pool->num_buffers; i++)
45         ra_buf_free(ra, &pool->buffers[i]);
46 
47     talloc_free(pool->buffers);
48     *pool = (struct ra_buf_pool){0};
49 }
50 
ra_buf_params_compatible(const struct ra_buf_params * new,const struct ra_buf_params * old)51 static bool ra_buf_params_compatible(const struct ra_buf_params *new,
52                                      const struct ra_buf_params *old)
53 {
54     return new->type == old->type &&
55            new->size <= old->size &&
56            new->host_mapped  == old->host_mapped &&
57            new->host_mutable == old->host_mutable;
58 }
59 
ra_buf_pool_grow(struct ra * ra,struct ra_buf_pool * pool)60 static bool ra_buf_pool_grow(struct ra *ra, struct ra_buf_pool *pool)
61 {
62     struct ra_buf *buf = ra_buf_create(ra, &pool->current_params);
63     if (!buf)
64         return false;
65 
66     MP_TARRAY_INSERT_AT(NULL, pool->buffers, pool->num_buffers, pool->index, buf);
67     MP_VERBOSE(ra, "Resized buffer pool of type %u to size %d\n",
68                pool->current_params.type, pool->num_buffers);
69     return true;
70 }
71 
ra_buf_pool_get(struct ra * ra,struct ra_buf_pool * pool,const struct ra_buf_params * params)72 struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool,
73                                const struct ra_buf_params *params)
74 {
75     assert(!params->initial_data);
76 
77     if (!ra_buf_params_compatible(params, &pool->current_params)) {
78         ra_buf_pool_uninit(ra, pool);
79         pool->current_params = *params;
80     }
81 
82     // Make sure we have at least one buffer available
83     if (!pool->buffers && !ra_buf_pool_grow(ra, pool))
84         return NULL;
85 
86     // Make sure the next buffer is available for use
87     if (!ra->fns->buf_poll(ra, pool->buffers[pool->index]) &&
88         !ra_buf_pool_grow(ra, pool))
89     {
90         return NULL;
91     }
92 
93     struct ra_buf *buf = pool->buffers[pool->index++];
94     pool->index %= pool->num_buffers;
95 
96     return buf;
97 }
98 
ra_tex_upload_pbo(struct ra * ra,struct ra_buf_pool * pbo,const struct ra_tex_upload_params * params)99 bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo,
100                        const struct ra_tex_upload_params *params)
101 {
102     if (params->buf)
103         return ra->fns->tex_upload(ra, params);
104 
105     struct ra_tex *tex = params->tex;
106     size_t row_size = tex->params.dimensions == 2 ? params->stride :
107                       tex->params.w * tex->params.format->pixel_size;
108 
109     int height = tex->params.h;
110     if (tex->params.dimensions == 2 && params->rc)
111         height = mp_rect_h(*params->rc);
112 
113     struct ra_buf_params bufparams = {
114         .type = RA_BUF_TYPE_TEX_UPLOAD,
115         .size = row_size * height * tex->params.d,
116         .host_mutable = true,
117     };
118 
119     struct ra_buf *buf = ra_buf_pool_get(ra, pbo, &bufparams);
120     if (!buf)
121         return false;
122 
123     ra->fns->buf_update(ra, buf, 0, params->src, bufparams.size);
124 
125     struct ra_tex_upload_params newparams = *params;
126     newparams.buf = buf;
127     newparams.src = NULL;
128 
129     return ra->fns->tex_upload(ra, &newparams);
130 }
131 
std140_layout(struct ra_renderpass_input * inp)132 struct ra_layout std140_layout(struct ra_renderpass_input *inp)
133 {
134     size_t el_size = ra_vartype_size(inp->type);
135 
136     // std140 packing rules:
137     // 1. The alignment of generic values is their size in bytes
138     // 2. The alignment of vectors is the vector length * the base count, with
139     // the exception of vec3 which is always aligned like vec4
140     // 3. The alignment of arrays is that of the element size rounded up to
141     // the nearest multiple of vec4
142     // 4. Matrices are treated like arrays of vectors
143     // 5. Arrays/matrices are laid out with a stride equal to the alignment
144     size_t stride = el_size * inp->dim_v;
145     size_t align = stride;
146     if (inp->dim_v == 3)
147         align += el_size;
148     if (inp->dim_m > 1)
149         stride = align = MP_ALIGN_UP(stride, sizeof(float[4]));
150 
151     return (struct ra_layout) {
152         .align  = align,
153         .stride = stride,
154         .size   = stride * inp->dim_m,
155     };
156 }
157 
std430_layout(struct ra_renderpass_input * inp)158 struct ra_layout std430_layout(struct ra_renderpass_input *inp)
159 {
160     size_t el_size = ra_vartype_size(inp->type);
161 
162     // std430 packing rules: like std140, except arrays/matrices are always
163     // "tightly" packed, even arrays/matrices of vec3s
164     size_t stride = el_size * inp->dim_v;
165     size_t align = stride;
166     if (inp->dim_v == 3 && inp->dim_m == 1)
167         align += el_size;
168 
169     return (struct ra_layout) {
170         .align  = align,
171         .stride = stride,
172         .size   = stride * inp->dim_m,
173     };
174 }
175 
176 // Resize a texture to a new desired size and format if necessary
ra_tex_resize(struct ra * ra,struct mp_log * log,struct ra_tex ** tex,int w,int h,const struct ra_format * fmt)177 bool ra_tex_resize(struct ra *ra, struct mp_log *log, struct ra_tex **tex,
178                    int w, int h, const struct ra_format *fmt)
179 {
180     if (*tex) {
181         struct ra_tex_params cur_params = (*tex)->params;
182         if (cur_params.w == w && cur_params.h == h && cur_params.format == fmt)
183             return true;
184     }
185 
186     mp_dbg(log, "Resizing texture: %dx%d\n", w, h);
187 
188     if (!fmt || !fmt->renderable || !fmt->linear_filter) {
189         mp_err(log, "Format %s not supported.\n", fmt ? fmt->name : "(unset)");
190         return false;
191     }
192 
193     ra_tex_free(ra, tex);
194     struct ra_tex_params params = {
195         .dimensions = 2,
196         .w = w,
197         .h = h,
198         .d = 1,
199         .format = fmt,
200         .src_linear = true,
201         .render_src = true,
202         .render_dst = true,
203         .storage_dst = fmt->storable,
204         .blit_src = true,
205     };
206 
207     *tex = ra_tex_create(ra, &params);
208     if (!*tex)
209         mp_err(log, "Error: texture could not be created.\n");
210 
211     return *tex;
212 }
213 
214 struct timer_pool {
215     struct ra *ra;
216     ra_timer *timer;
217     bool running; // detect invalid usage
218 
219     uint64_t samples[VO_PERF_SAMPLE_COUNT];
220     int sample_idx;
221     int sample_count;
222 
223     uint64_t sum;
224     uint64_t peak;
225 };
226 
timer_pool_create(struct ra * ra)227 struct timer_pool *timer_pool_create(struct ra *ra)
228 {
229     if (!ra->fns->timer_create)
230         return NULL;
231 
232     ra_timer *timer = ra->fns->timer_create(ra);
233     if (!timer)
234         return NULL;
235 
236     struct timer_pool *pool = talloc(NULL, struct timer_pool);
237     if (!pool) {
238         ra->fns->timer_destroy(ra, timer);
239         return NULL;
240     }
241 
242     *pool = (struct timer_pool){ .ra = ra, .timer = timer };
243     return pool;
244 }
245 
timer_pool_destroy(struct timer_pool * pool)246 void timer_pool_destroy(struct timer_pool *pool)
247 {
248     if (!pool)
249         return;
250 
251     pool->ra->fns->timer_destroy(pool->ra, pool->timer);
252     talloc_free(pool);
253 }
254 
timer_pool_start(struct timer_pool * pool)255 void timer_pool_start(struct timer_pool *pool)
256 {
257     if (!pool)
258         return;
259 
260     assert(!pool->running);
261     pool->ra->fns->timer_start(pool->ra, pool->timer);
262     pool->running = true;
263 }
264 
timer_pool_stop(struct timer_pool * pool)265 void timer_pool_stop(struct timer_pool *pool)
266 {
267     if (!pool)
268         return;
269 
270     assert(pool->running);
271     uint64_t res = pool->ra->fns->timer_stop(pool->ra, pool->timer);
272     pool->running = false;
273 
274     if (res) {
275         // Input res into the buffer and grab the previous value
276         uint64_t old = pool->samples[pool->sample_idx];
277         pool->sample_count = MPMIN(pool->sample_count + 1, VO_PERF_SAMPLE_COUNT);
278         pool->samples[pool->sample_idx++] = res;
279         pool->sample_idx %= VO_PERF_SAMPLE_COUNT;
280         pool->sum = pool->sum + res - old;
281 
282         // Update peak if necessary
283         if (res >= pool->peak) {
284             pool->peak = res;
285         } else if (pool->peak == old) {
286             // It's possible that the last peak was the value we just removed,
287             // if so we need to scan for the new peak
288             uint64_t peak = res;
289             for (int i = 0; i < VO_PERF_SAMPLE_COUNT; i++)
290                 peak = MPMAX(peak, pool->samples[i]);
291             pool->peak = peak;
292         }
293     }
294 }
295 
timer_pool_measure(struct timer_pool * pool)296 struct mp_pass_perf timer_pool_measure(struct timer_pool *pool)
297 {
298     if (!pool)
299         return (struct mp_pass_perf){0};
300 
301     struct mp_pass_perf res = {
302         .peak = pool->peak,
303         .count = pool->sample_count,
304     };
305 
306     int idx = pool->sample_idx - pool->sample_count + VO_PERF_SAMPLE_COUNT;
307     for (int i = 0; i < res.count; i++) {
308         idx %= VO_PERF_SAMPLE_COUNT;
309         res.samples[i] = pool->samples[idx++];
310     }
311 
312     if (res.count > 0) {
313         res.last = res.samples[res.count - 1];
314         res.avg = pool->sum / res.count;
315     }
316 
317     return res;
318 }
319 
mp_log_source(struct mp_log * log,int lev,const char * src)320 void mp_log_source(struct mp_log *log, int lev, const char *src)
321 {
322     int line = 1;
323     if (!src)
324         return;
325     while (*src) {
326         const char *end = strchr(src, '\n');
327         const char *next = end + 1;
328         if (!end)
329             next = end = src + strlen(src);
330         mp_msg(log, lev, "[%3d] %.*s\n", line, (int)(end - src), src);
331         line++;
332         src = next;
333     }
334 }
335