Coverage Report

Created: 2025-08-11 06:44

/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, &params);
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
}