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