#include "common/msg.h" #include "video/out/vo.h" #include "utils.h" // Standard parallel 2D projection, except y1 < y0 means that the coordinate // system is flipped, not the projection. void gl_transform_ortho(struct gl_transform *t, float x0, float x1, float y0, float y1) { if (y1 < y0) { float tmp = y0; y0 = tmp - y1; y1 = tmp; } t->m[0][0] = 2.0f / (x1 - x0); t->m[0][1] = 0.0f; t->m[1][0] = 0.0f; t->m[1][1] = 2.0f / (y1 - y0); t->t[0] = -(x1 + x0) / (x1 - x0); t->t[1] = -(y1 + y0) / (y1 - y0); } // Apply the effects of one transformation to another, transforming it in the // process. In other words: post-composes t onto x void gl_transform_trans(struct gl_transform t, struct gl_transform *x) { struct gl_transform xt = *x; x->m[0][0] = t.m[0][0] * xt.m[0][0] + t.m[0][1] * xt.m[1][0]; x->m[1][0] = t.m[1][0] * xt.m[0][0] + t.m[1][1] * xt.m[1][0]; x->m[0][1] = t.m[0][0] * xt.m[0][1] + t.m[0][1] * xt.m[1][1]; x->m[1][1] = t.m[1][0] * xt.m[0][1] + t.m[1][1] * xt.m[1][1]; gl_transform_vec(t, &x->t[0], &x->t[1]); } void gl_transform_ortho_fbo(struct gl_transform *t, struct ra_fbo fbo) { int y_dir = fbo.flip ? -1 : 1; gl_transform_ortho(t, 0, fbo.tex->params.w, 0, fbo.tex->params.h * y_dir); } void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool) { for (int i = 0; i < pool->num_buffers; i++) ra_buf_free(ra, &pool->buffers[i]); talloc_free(pool->buffers); *pool = (struct ra_buf_pool){0}; } static bool ra_buf_params_compatible(const struct ra_buf_params *new, const struct ra_buf_params *old) { return new->type == old->type && new->size <= old->size && new->host_mapped == old->host_mapped && new->host_mutable == old->host_mutable; } static bool ra_buf_pool_grow(struct ra *ra, struct ra_buf_pool *pool) { struct ra_buf *buf = ra_buf_create(ra, &pool->current_params); if (!buf) return false; MP_TARRAY_INSERT_AT(NULL, pool->buffers, pool->num_buffers, pool->index, buf); MP_VERBOSE(ra, "Resized buffer pool of type %u to size %d\n", pool->current_params.type, pool->num_buffers); return true; } struct ra_buf *ra_buf_pool_get(struct ra *ra, struct ra_buf_pool *pool, const struct ra_buf_params *params) { assert(!params->initial_data); if (!ra_buf_params_compatible(params, &pool->current_params)) { ra_buf_pool_uninit(ra, pool); pool->current_params = *params; } // Make sure we have at least one buffer available if (!pool->buffers && !ra_buf_pool_grow(ra, pool)) return NULL; // Make sure the next buffer is available for use if (!ra->fns->buf_poll(ra, pool->buffers[pool->index]) && !ra_buf_pool_grow(ra, pool)) { return NULL; } struct ra_buf *buf = pool->buffers[pool->index++]; pool->index %= pool->num_buffers; return buf; } bool ra_tex_upload_pbo(struct ra *ra, struct ra_buf_pool *pbo, const struct ra_tex_upload_params *params) { if (params->buf) return ra->fns->tex_upload(ra, params); struct ra_tex *tex = params->tex; size_t row_size = tex->params.dimensions == 2 ? params->stride : tex->params.w * tex->params.format->pixel_size; int height = tex->params.h; if (tex->params.dimensions == 2 && params->rc) height = mp_rect_h(*params->rc); struct ra_buf_params bufparams = { .type = RA_BUF_TYPE_TEX_UPLOAD, .size = row_size * height * tex->params.d, .host_mutable = true, }; struct ra_buf *buf = ra_buf_pool_get(ra, pbo, &bufparams); if (!buf) return false; ra->fns->buf_update(ra, buf, 0, params->src, bufparams.size); struct ra_tex_upload_params newparams = *params; newparams.buf = buf; newparams.src = NULL; return ra->fns->tex_upload(ra, &newparams); } struct ra_layout std140_layout(struct ra_renderpass_input *inp) { size_t el_size = ra_vartype_size(inp->type); // std140 packing rules: // 1. The alignment of generic values is their size in bytes // 2. The alignment of vectors is the vector length * the base count, with // the exception of vec3 which is always aligned like vec4 // 3. The alignment of arrays is that of the element size rounded up to // the nearest multiple of vec4 // 4. Matrices are treated like arrays of vectors // 5. Arrays/matrices are laid out with a stride equal to the alignment size_t stride = el_size * inp->dim_v; size_t align = stride; if (inp->dim_v == 3) align += el_size; if (inp->dim_m > 1) stride = align = MP_ALIGN_UP(stride, sizeof(float[4])); return (struct ra_layout) { .align = align, .stride = stride, .size = stride * inp->dim_m, }; } struct ra_layout std430_layout(struct ra_renderpass_input *inp) { size_t el_size = ra_vartype_size(inp->type); // std430 packing rules: like std140, except arrays/matrices are always // "tightly" packed, even arrays/matrices of vec3s size_t stride = el_size * inp->dim_v; size_t align = stride; if (inp->dim_v == 3 && inp->dim_m == 1) align += el_size; return (struct ra_layout) { .align = align, .stride = stride, .size = stride * inp->dim_m, }; } // Resize a texture to a new desired size and format if necessary bool ra_tex_resize(struct ra *ra, struct mp_log *log, struct ra_tex **tex, int w, int h, const struct ra_format *fmt) { if (*tex) { struct ra_tex_params cur_params = (*tex)->params; if (cur_params.w == w && cur_params.h == h && cur_params.format == fmt) return true; } mp_dbg(log, "Resizing texture: %dx%d\n", w, h); if (!fmt || !fmt->renderable || !fmt->linear_filter || !fmt->storable) { mp_err(log, "Format %s not supported.\n", fmt ? fmt->name : "(unset)"); return false; } ra_tex_free(ra, tex); struct ra_tex_params params = { .dimensions = 2, .w = w, .h = h, .d = 1, .format = fmt, .src_linear = true, .render_src = true, .render_dst = true, .storage_dst = true, .blit_src = true, }; *tex = ra_tex_create(ra, ¶ms); if (!*tex) mp_err(log, "Error: texture could not be created.\n"); return *tex; } struct timer_pool { struct ra *ra; ra_timer *timer; bool running; // detect invalid usage uint64_t samples[VO_PERF_SAMPLE_COUNT]; int sample_idx; int sample_count; uint64_t sum; uint64_t peak; }; struct timer_pool *timer_pool_create(struct ra *ra) { if (!ra->fns->timer_create) return NULL; ra_timer *timer = ra->fns->timer_create(ra); if (!timer) return NULL; struct timer_pool *pool = talloc(NULL, struct timer_pool); if (!pool) { ra->fns->timer_destroy(ra, timer); return NULL; } *pool = (struct timer_pool){ .ra = ra, .timer = timer }; return pool; } void timer_pool_destroy(struct timer_pool *pool) { if (!pool) return; pool->ra->fns->timer_destroy(pool->ra, pool->timer); talloc_free(pool); } void timer_pool_start(struct timer_pool *pool) { if (!pool) return; assert(!pool->running); pool->ra->fns->timer_start(pool->ra, pool->timer); pool->running = true; } void timer_pool_stop(struct timer_pool *pool) { if (!pool) return; assert(pool->running); uint64_t res = pool->ra->fns->timer_stop(pool->ra, pool->timer); pool->running = false; if (res) { // Input res into the buffer and grab the previous value uint64_t old = pool->samples[pool->sample_idx]; pool->sample_count = MPMIN(pool->sample_count + 1, VO_PERF_SAMPLE_COUNT); pool->samples[pool->sample_idx++] = res; pool->sample_idx %= VO_PERF_SAMPLE_COUNT; pool->sum = pool->sum + res - old; // Update peak if necessary if (res >= pool->peak) { pool->peak = res; } else if (pool->peak == old) { // It's possible that the last peak was the value we just removed, // if so we need to scan for the new peak uint64_t peak = res; for (int i = 0; i < VO_PERF_SAMPLE_COUNT; i++) peak = MPMAX(peak, pool->samples[i]); pool->peak = peak; } } } struct mp_pass_perf timer_pool_measure(struct timer_pool *pool) { if (!pool) return (struct mp_pass_perf){0}; struct mp_pass_perf res = { .peak = pool->peak, .count = pool->sample_count, }; int idx = pool->sample_idx - pool->sample_count + VO_PERF_SAMPLE_COUNT; for (int i = 0; i < res.count; i++) { idx %= VO_PERF_SAMPLE_COUNT; res.samples[i] = pool->samples[idx++]; } if (res.count > 0) { res.last = res.samples[res.count - 1]; res.avg = pool->sum / res.count; } return res; } void mp_log_source(struct mp_log *log, int lev, const char *src) { int line = 1; if (!src) return; while (*src) { const char *end = strchr(src, '\n'); const char *next = end + 1; if (!end) next = end = src + strlen(src); mp_msg(log, lev, "[%3d] %.*s\n", line, (int)(end - src), src); line++; src = next; } }