diff options
Diffstat (limited to 'video/out/opengl/video.c')
-rw-r--r-- | video/out/opengl/video.c | 438 |
1 files changed, 156 insertions, 282 deletions
diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index f46fdc1c9f..468bee90b5 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -41,7 +41,6 @@ #include "user_shaders.h" #include "video/out/filter_kernels.h" #include "video/out/aspect.h" -#include "video/out/bitmap_packer.h" #include "video/out/dither.h" #include "video/out/vo.h" @@ -97,13 +96,13 @@ struct texplane { GLenum gl_format; GLenum gl_type; GLuint gl_texture; - int gl_buffer; char swizzle[5]; + bool flipped; + struct gl_pbo_upload pbo; }; struct video_image { struct texplane planes[4]; - bool image_flipped; struct mp_image *mpi; // original input image bool hwdec_mapped; }; @@ -676,7 +675,7 @@ static int pass_bind(struct gl_video *p, struct img_tex tex) } // Rotation by 90° and flipping. -static void get_plane_source_transform(struct gl_video *p, int w, int h, +static void get_plane_source_transform(struct gl_video *p, struct texplane *t, struct gl_transform *out_tr) { struct gl_transform tr = identity_trans; @@ -689,11 +688,11 @@ static void get_plane_source_transform(struct gl_video *p, int w, int h, // basically, recenter to keep the whole image in view float b[2] = {1, 1}; gl_transform_vec(rot, &b[0], &b[1]); - tr.t[0] += b[0] < 0 ? w : 0; - tr.t[1] += b[1] < 0 ? h : 0; + tr.t[0] += b[0] < 0 ? t->w : 0; + tr.t[1] += b[1] < 0 ? t->h : 0; - if (p->image.image_flipped) { - struct gl_transform flip = {{{1, 0}, {0, -1}}, {0, h}}; + if (t->flipped) { + struct gl_transform flip = {{{1, 0}, {0, -1}}, {0, t->h}}; gl_transform_trans(flip, &tr); } @@ -730,7 +729,7 @@ static void pass_get_img_tex(struct gl_video *p, struct video_image *vimg, // The existing code assumes we just have a single tex multiplier for // all of the planes. This may change in the future - float tex_mul = 1.0 / mp_get_csp_mul(p->image_params.colorspace, + float tex_mul = 1.0 / mp_get_csp_mul(p->image_params.color.space, p->image_desc.component_bits, p->image_desc.component_full_bits); @@ -764,7 +763,7 @@ static void pass_get_img_tex(struct gl_video *p, struct video_image *vimg, .components = p->image_desc.components[n], }; snprintf(tex[n].swizzle, sizeof(tex[n].swizzle), "%s", t->swizzle); - get_plane_source_transform(p, t->w, t->h, &tex[n].transform); + get_plane_source_transform(p, t, &tex[n].transform); if (p->image_params.rotate % 180 == 90) MPSWAP(int, tex[n].w, tex[n].h); @@ -794,7 +793,7 @@ static void init_video(struct gl_video *p) mp_image_params_guess_csp(&p->image_params); int eq_caps = MP_CSP_EQ_CAPS_GAMMA; - if (p->image_params.colorspace != MP_CSP_BT_2020_C) + if (p->image_params.color.space != MP_CSP_BT_2020_C) eq_caps |= MP_CSP_EQ_CAPS_COLORMATRIX; if (p->image_desc.flags & MP_IMGFLAG_XYZ) eq_caps |= MP_CSP_EQ_CAPS_BRIGHTNESS; @@ -879,7 +878,7 @@ static void uninit_video(struct gl_video *p) struct texplane *plane = &vimg->planes[n]; gl->DeleteTextures(1, &plane->gl_texture); - gl->DeleteBuffers(1, &plane->gl_buffer); + gl_pbo_upload_uninit(&plane->pbo); } *vimg = (struct video_image){0}; @@ -1239,6 +1238,9 @@ static void load_shader(struct gl_video *p, struct bstr body) gl_sc_uniform_f(p->sc, "frame", p->frames_uploaded); gl_sc_uniform_vec2(p->sc, "image_size", (GLfloat[]){p->image_params.w, p->image_params.h}); + gl_sc_uniform_vec2(p->sc, "target_size", + (GLfloat[]){p->dst_rect.x1 - p->dst_rect.x0, + p->dst_rect.y1 - p->dst_rect.y0}); } static const char *get_custom_shader_fn(struct gl_video *p, const char *body) @@ -1542,112 +1544,40 @@ static void user_hook_old(struct gl_video *p, struct img_tex tex, GLSLF("color = %s(HOOKED_raw, HOOKED_pos, HOOKED_size);\n", fn_name); } -// Returns whether successful. 'result' is left untouched on failure -static bool eval_szexpr(struct gl_video *p, struct img_tex tex, - struct szexp expr[MAX_SZEXP_SIZE], - float *result) -{ - float stack[MAX_SZEXP_SIZE] = {0}; - int idx = 0; // points to next element to push - - for (int i = 0; i < MAX_SZEXP_SIZE; i++) { - switch (expr[i].tag) { - case SZEXP_END: - goto done; - - case SZEXP_CONST: - // Since our SZEXPs are bound by MAX_SZEXP_SIZE, it should be - // impossible to overflow the stack - assert(idx < MAX_SZEXP_SIZE); - stack[idx++] = expr[i].val.cval; - continue; - - case SZEXP_OP1: - if (idx < 1) { - MP_WARN(p, "Stack underflow in RPN expression!\n"); - return false; - } - - switch (expr[i].val.op) { - case SZEXP_OP_NOT: stack[idx-1] = !stack[idx-1]; break; - default: abort(); - } - continue; - - case SZEXP_OP2: - if (idx < 2) { - MP_WARN(p, "Stack underflow in RPN expression!\n"); - return false; - } - - // Pop the operands in reverse order - float op2 = stack[--idx]; - float op1 = stack[--idx]; - float res = 0.0; - switch (expr[i].val.op) { - case SZEXP_OP_ADD: res = op1 + op2; break; - case SZEXP_OP_SUB: res = op1 - op2; break; - case SZEXP_OP_MUL: res = op1 * op2; break; - case SZEXP_OP_DIV: res = op1 / op2; break; - case SZEXP_OP_GT: res = op1 > op2; break; - case SZEXP_OP_LT: res = op1 < op2; break; - default: abort(); - } - - if (!isfinite(res)) { - MP_WARN(p, "Illegal operation in RPN expression!\n"); - return false; - } - - stack[idx++] = res; - continue; - - case SZEXP_VAR_W: - case SZEXP_VAR_H: { - struct bstr name = expr[i].val.varname; - struct img_tex var_tex; - - // The size of OUTPUT is determined. It could be useful for certain - // user shaders to skip passes. - if (bstr_equals0(name, "OUTPUT")) { - int vp_w = p->dst_rect.x1 - p->dst_rect.x0; - int vp_h = p->dst_rect.y1 - p->dst_rect.y0; - stack[idx++] = (expr[i].tag == SZEXP_VAR_W) ? vp_w : vp_h; - continue; - } - - // HOOKED is a special case - if (bstr_equals0(name, "HOOKED")) { - var_tex = tex; - goto found_tex; - } +struct szexp_ctx { + struct gl_video *p; + struct img_tex tex; +}; - for (int o = 0; o < p->saved_tex_num; o++) { - if (bstr_equals0(name, p->saved_tex[o].name)) { - var_tex = p->saved_tex[o].tex; - goto found_tex; - } - } +static bool szexp_lookup(void *priv, struct bstr var, float size[2]) +{ + struct szexp_ctx *ctx = priv; + struct gl_video *p = ctx->p; - MP_WARN(p, "Texture %.*s not found in RPN expression!\n", BSTR_P(name)); - return false; + // The size of OUTPUT is determined. It could be useful for certain + // user shaders to skip passes. + if (bstr_equals0(var, "OUTPUT")) { + size[0] = p->dst_rect.x1 - p->dst_rect.x0; + size[1] = p->dst_rect.y1 - p->dst_rect.y0; + return true; + } -found_tex: - stack[idx++] = (expr[i].tag == SZEXP_VAR_W) ? var_tex.w : var_tex.h; - continue; - } - } + // HOOKED is a special case + if (bstr_equals0(var, "HOOKED")) { + size[0] = ctx->tex.w; + size[1] = ctx->tex.h; + return true; } -done: - // Return the single stack element - if (idx != 1) { - MP_WARN(p, "Malformed stack after RPN expression!\n"); - return false; + for (int o = 0; o < p->saved_tex_num; o++) { + if (bstr_equals0(var, p->saved_tex[o].name)) { + size[0] = p->saved_tex[o].tex.w; + size[1] = p->saved_tex[o].tex.h; + return true; + } } - *result = stack[0]; - return true; + return false; } static bool user_hook_cond(struct gl_video *p, struct img_tex tex, void *priv) @@ -1656,7 +1586,7 @@ static bool user_hook_cond(struct gl_video *p, struct img_tex tex, void *priv) assert(shader); float res = false; - eval_szexpr(p, tex, shader->cond, &res); + eval_szexpr(p->log, &(struct szexp_ctx){p, tex}, szexp_lookup, shader->cond, &res); return res; } @@ -1674,8 +1604,8 @@ static void user_hook(struct gl_video *p, struct img_tex tex, // to do this and display an error message than just crash OpenGL float w = 1.0, h = 1.0; - eval_szexpr(p, tex, shader->width, &w); - eval_szexpr(p, tex, shader->height, &h); + eval_szexpr(p->log, &(struct szexp_ctx){p, tex}, szexp_lookup, shader->width, &w); + eval_szexpr(p->log, &(struct szexp_ctx){p, tex}, szexp_lookup, shader->height, &h); *trans = (struct gl_transform){{{w / tex.w, 0}, {0, h / tex.h}}}; gl_transform_trans(shader->offset, trans); @@ -1983,7 +1913,7 @@ static void pass_convert_yuv(struct gl_video *p) GLSLF("color = color.%s;\n", p->color_swizzle); // Pre-colormatrix input gamma correction - if (cparams.colorspace == MP_CSP_XYZ) + if (cparams.color.space == MP_CSP_XYZ) GLSL(color.rgb = pow(color.rgb, vec3(2.6));) // linear light // We always explicitly normalize the range in pass_read_video @@ -1998,7 +1928,7 @@ static void pass_convert_yuv(struct gl_video *p) GLSL(color.rgb = mat3(colormatrix) * color.rgb + colormatrix_c;) - if (p->image_params.colorspace == MP_CSP_BT_2020_C) { + if (p->image_params.color.space == MP_CSP_BT_2020_C) { // Conversion for C'rcY'cC'bc via the BT.2020 CL system: // C'bc = (B'-Y'c) / 1.9404 | C'bc <= 0 // = (B'-Y'c) / 1.5816 | C'bc > 0 @@ -2109,7 +2039,7 @@ static void pass_scale_main(struct gl_video *p) // Pre-conversion, like linear light/sigmoidization GLSLF("// scaler pre-conversion\n"); if (p->use_linear) { - pass_linearize(p->sc, p->image_params.gamma); + pass_linearize(p->sc, p->image_params.color.gamma); pass_opt_hook_point(p, "LINEAR", NULL); } @@ -2156,107 +2086,104 @@ static void pass_scale_main(struct gl_video *p) } } -// Adapts the colors from the given color space to the display device's native -// gamut. -static void pass_colormanage(struct gl_video *p, float peak_src, - enum mp_csp_prim prim_src, - enum mp_csp_trc trc_src) +// Adapts the colors to the right output color space. (Final pass during +// rendering) +// If OSD is true, ignore any changes that may have been made to the video +// by previous passes (i.e. linear scaling) +static void pass_colormanage(struct gl_video *p, struct mp_colorspace src, bool osd) { - GLSLF("// color management\n"); - enum mp_csp_trc trc_dst = p->opts.target_trc; - enum mp_csp_prim prim_dst = p->opts.target_prim; - float peak_dst = p->opts.target_brightness; + struct mp_colorspace ref = src; + + if (p->use_linear && !osd) + src.gamma = MP_CSP_TRC_LINEAR; + + // Figure out the target color space from the options, or auto-guess if + // none were set + struct mp_colorspace dst = { + .gamma = p->opts.target_trc, + .primaries = p->opts.target_prim, + .nom_peak = mp_csp_trc_nom_peak(p->opts.target_trc, p->opts.target_brightness), + }; if (p->use_lut_3d) { - // The 3DLUT is always generated against the original source space - enum mp_csp_prim prim_orig = p->image_params.primaries; - enum mp_csp_trc trc_orig = p->image_params.gamma; - - // One exception: SMPTE ST.2084 is not implemented by LittleCMS - // for technical limitation reasons, so we use a gamma 2.2 input curve - // here instead. We could pick any value we want here, the difference - // is just coding efficiency. - if (trc_orig == MP_CSP_TRC_SMPTE_ST2084) + // The 3DLUT is always generated against the video's original source + // space, *not* the reference space. (To avoid having to regenerate + // the 3DLUT for the OSD on every frame) + enum mp_csp_prim prim_orig = p->image_params.color.primaries; + enum mp_csp_trc trc_orig = p->image_params.color.gamma; + + // One exception: HDR is not implemented by LittleCMS for technical + // limitation reasons, so we use a gamma 2.2 input curve here instead. + // We could pick any value we want here, the difference is just coding + // efficiency. + if (trc_orig == MP_CSP_TRC_SMPTE_ST2084 || + trc_orig == MP_CSP_TRC_ARIB_STD_B67 || + trc_orig == MP_CSP_TRC_V_LOG) + { trc_orig = MP_CSP_TRC_GAMMA22; + } if (gl_video_get_lut3d(p, prim_orig, trc_orig)) { - prim_dst = prim_orig; - trc_dst = trc_orig; + dst.primaries = prim_orig; + dst.gamma = trc_orig; } } - // When auto-guessing the output color params, just pick the source color - // params to preserve the authentic "look and feel" of wrong/naive players. - // Some exceptions apply to source spaces that even hardcore technoluddites - // would probably not enjoy viewing unaltered - if (prim_dst == MP_CSP_PRIM_AUTO) { - prim_dst = prim_src; + if (dst.primaries == MP_CSP_PRIM_AUTO) { + // The vast majority of people are on sRGB or BT.709 displays, so pick + // this as the default output color space. + dst.primaries = MP_CSP_PRIM_BT_709; - // Avoid outputting very wide gamut content automatically, since the - // majority target audience has standard gamut displays - if (prim_dst == MP_CSP_PRIM_BT_2020 || prim_dst == MP_CSP_PRIM_PRO_PHOTO) - prim_dst = MP_CSP_PRIM_BT_709; + if (ref.primaries == MP_CSP_PRIM_BT_601_525 || + ref.primaries == MP_CSP_PRIM_BT_601_625) + { + // Since we auto-pick BT.601 and BT.709 based on the dimensions, + // combined with the fact that they're very similar to begin with, + // and to avoid confusing the average user, just don't adapt BT.601 + // content automatically at all. + dst.primaries = ref.gamma; + } } - if (trc_dst == MP_CSP_TRC_AUTO) { - trc_dst = trc_src; - // Avoid outputting linear light at all costs. First try - // falling back to the image gamma (e.g. in the case that the input - // was linear light due to linear-scaling) - if (trc_dst == MP_CSP_TRC_LINEAR) - trc_dst = p->image_params.gamma; - - // Failing that, pick gamma 2.2 as a reasonable default. This is also - // picked as a default for outputting HDR content - if (trc_dst == MP_CSP_TRC_LINEAR || trc_dst == MP_CSP_TRC_SMPTE_ST2084) - trc_dst = MP_CSP_TRC_GAMMA22; - } + if (dst.gamma == MP_CSP_TRC_AUTO) { + // Most people seem to complain when the image is darker or brighter + // than what they're "used to", so just avoid changing the gamma + // altogether by default. The only exceptions to this rule apply to + // very unusual TRCs, which even hardcode technoluddites would probably + // not enjoy viewing unaltered. + dst.gamma = ref.gamma; - if (!peak_src) { - // If the source has no information known, it's display-referred - // (and should be treated relative to the specified desired peak_dst) - peak_src = peak_dst; + // Avoid outputting linear light or HDR content "by default". For these + // just pick gamma 2.2 as a default, since it's a good estimate for + // the response of typical displays + if (dst.gamma == MP_CSP_TRC_LINEAR || mp_trc_is_hdr(dst.gamma)) + dst.gamma = MP_CSP_TRC_GAMMA22; } - // All operations from here on require linear light as a starting point, - // so we linearize even if trc_src == trc_dst when one of the other - // operations needs it - bool need_gamma = trc_src != trc_dst || prim_src != prim_dst || - peak_src != peak_dst; - if (need_gamma) - pass_linearize(p->sc, trc_src); - - // Adapt and tone map for a different reference peak brightness - if (peak_src != peak_dst) - { - GLSLF("// HDR tone mapping\n"); - float rel_peak = peak_src / peak_dst; - // Normalize such that 1 is the target brightness (and values above - // 1 are out of range) - GLSLF("color.rgb *= vec3(%f);\n", rel_peak); - // Tone map back down to the range [0,1] - pass_tone_map(p->sc, rel_peak, p->opts.hdr_tone_mapping, - p->opts.tone_mapping_param); + // For the src peaks, the correct brightness metadata may be present for + // sig_peak, nom_peak, both, or neither. To handle everything in a generic + // way, it's important to never automatically infer a sig_peak that is + // below the nom_peak (since we don't know what bits the image contains, + // doing so would potentially badly clip). The only time in which this + // may be the case is when the mastering metadata explicitly says so, i.e. + // the sig_peak was already set. So to simplify the logic as much as + // possible, make sure the nom_peak is present and correct first, and just + // set sig_peak = nom_peak if missing. + if (!src.nom_peak) { + // For display-referred colorspaces, we treat it as relative to + // target_brightness + src.nom_peak = mp_csp_trc_nom_peak(src.gamma, p->opts.target_brightness); } - // Adapt to the right colorspace if necessary - if (prim_src != prim_dst) { - struct mp_csp_primaries csp_src = mp_get_csp_primaries(prim_src), - csp_dst = mp_get_csp_primaries(prim_dst); - float m[3][3] = {{0}}; - mp_get_cms_matrix(csp_src, csp_dst, MP_INTENT_RELATIVE_COLORIMETRIC, m); - gl_sc_uniform_mat3(p->sc, "cms_matrix", true, &m[0][0]); - GLSL(color.rgb = cms_matrix * color.rgb;) - } + if (!src.sig_peak) + src.sig_peak = src.nom_peak; - if (need_gamma) { - // If the target encoding function has a fixed peak, we need to - // un-normalize back to the encoding signal range - if (trc_dst == MP_CSP_TRC_SMPTE_ST2084) - GLSLF("color.rgb *= vec3(%f);\n", peak_dst / 10000); + MP_DBG(p, "HDR src nom: %f sig: %f, dst: %f\n", + src.nom_peak, src.sig_peak, dst.nom_peak); - pass_delinearize(p->sc, trc_dst); - } + // Adapt from src to dst as necessary + pass_color_map(p->sc, src, dst, p->opts.hdr_tone_mapping, + p->opts.tone_mapping_param); if (p->use_lut_3d) { gl_sc_uniform_sampler(p->sc, "lut_3d", GL_TEXTURE_3D, TEXUNIT_3DLUT); @@ -2397,11 +2324,15 @@ static void pass_draw_osd(struct gl_video *p, int draw_flags, double pts, default: abort(); } - // Subtitle color management, they're assumed to be display-referred - // sRGB by default + // When subtitles need to be color managed, assume they're in sRGB + // (for lack of anything saner to do) if (cms) { - pass_colormanage(p, p->opts.target_brightness, - MP_CSP_PRIM_BT_709, MP_CSP_TRC_SRGB); + static const struct mp_colorspace csp_srgb = { + .primaries = MP_CSP_PRIM_BT_709, + .gamma = MP_CSP_TRC_SRGB, + }; + + pass_colormanage(p, csp_srgb, true); } gl_sc_set_vao(p->sc, mpgl_osd_get_vao(p->osd)); gl_sc_gen_shader_and_reset(p->sc); @@ -2503,7 +2434,7 @@ static void pass_render_frame(struct gl_video *p) rect.mt *= scale[1]; rect.mb *= scale[1]; // We should always blend subtitles in non-linear light if (p->use_linear) { - pass_delinearize(p->sc, p->image_params.gamma); + pass_delinearize(p->sc, p->image_params.color.gamma); p->use_linear = false; } finish_pass_fbo(p, &p->blend_subs_fbo, p->texture_w, p->texture_h, @@ -2532,8 +2463,7 @@ static void pass_draw_to_screen(struct gl_video *p, int fbo) GLSL(color.rgb = pow(color.rgb, vec3(user_gamma));) } - pass_colormanage(p, p->image_params.peak, p->image_params.primaries, - p->use_linear ? MP_CSP_TRC_LINEAR : p->image_params.gamma); + pass_colormanage(p, p->image_params.color, false); // Draw checkerboard pattern to indicate transparency if (p->has_alpha && p->opts.alpha_mode == ALPHA_BLEND_TILES) { @@ -2564,7 +2494,7 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, if (t->still) gl_video_reset_surfaces(p); - // First of all, figure out if we have a frame availble at all, and draw + // First of all, figure out if we have a frame available at all, and draw // it manually + reset the queue if not if (p->surfaces[p->surface_now].pts == MP_NOPTS_VALUE) { if (!gl_video_upload_image(p, t->current)) @@ -2577,7 +2507,7 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, } // Find the right frame for this instant - if (t->current&& t->current->pts != MP_NOPTS_VALUE) { + if (t->current && t->current->pts != MP_NOPTS_VALUE) { int next = fbosurface_wrap(p->surface_now + 1); while (p->surfaces[next].pts != MP_NOPTS_VALUE && p->surfaces[next].pts > p->surfaces[p->surface_now].pts && @@ -2615,7 +2545,7 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, // this should be done before the step where we find the right frame, but // it only barely matters at the very beginning of playback, and this way // makes the code much more linear. - int surface_dst = fbosurface_wrap(p->surface_idx+1); + int surface_dst = fbosurface_wrap(p->surface_idx + 1); for (int i = 0; i < t->num_frames; i++) { // Avoid overwriting data we might still need if (surface_dst == surface_bse - 1) @@ -2634,7 +2564,7 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, vp_w, vp_h, FBOTEX_FUZZY); p->surfaces[surface_dst].pts = f->pts; p->surface_idx = surface_dst; - surface_dst = fbosurface_wrap(surface_dst+1); + surface_dst = fbosurface_wrap(surface_dst + 1); } } @@ -2645,7 +2575,7 @@ static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, // end of playback or start of playback. bool valid = true; for (int i = surface_bse, ii; valid && i != surface_end; i = ii) { - ii = fbosurface_wrap(i+1); + ii = fbosurface_wrap(i + 1); if (p->surfaces[i].pts == MP_NOPTS_VALUE || p->surfaces[ii].pts == MP_NOPTS_VALUE) { @@ -2737,6 +2667,11 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) GL *gl = p->gl; struct video_image *vimg = &p->image; + if (fbo && !(gl->mpgl_caps & MPGL_CAP_FB)) { + MP_FATAL(p, "Rendering to FBO requested, but no FBO extension found!\n"); + return; + } + p->broken_frame = false; gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); @@ -2773,7 +2708,7 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) goto done; pass_render_frame(p); - // For the non-interplation case, we draw to a single "cache" + // For the non-interpolation case, we draw to a single "cache" // FBO to speed up subsequent re-draws (if any exist) int dest_fbo = fbo; if (frame->num_vsyncs > 1 && frame->display_synced && @@ -2781,7 +2716,7 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) { fbotex_change(&p->output_fbo, p->gl, p->log, p->vp_w, abs(p->vp_h), - p->opts.fbo_format, 0); + p->opts.fbo_format, FBOTEX_FUZZY); dest_fbo = p->output_fbo.fbo; p->output_fbo_valid = true; } @@ -2880,54 +2815,6 @@ struct voctrl_performance_data gl_video_perfdata(struct gl_video *p) }; } -static bool unmap_image(struct gl_video *p, struct mp_image *mpi) -{ - GL *gl = p->gl; - bool ok = true; - struct video_image *vimg = &p->image; - for (int n = 0; n < p->plane_count; n++) { - struct texplane *plane = &vimg->planes[n]; - gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer); - ok = gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER) && ok; - mpi->planes[n] = NULL; // PBO offset 0 - } - gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - return ok; -} - -static bool map_image(struct gl_video *p, struct mp_image *mpi) -{ - GL *gl = p->gl; - - if (!p->opts.pbo) - return false; - - struct video_image *vimg = &p->image; - - for (int n = 0; n < p->plane_count; n++) { - struct texplane *plane = &vimg->planes[n]; - mpi->stride[n] = mp_image_plane_w(mpi, n) * p->image_desc.bytes[n]; - size_t buffer_size = mp_image_plane_h(mpi, n) * mpi->stride[n]; - if (!plane->gl_buffer) { - gl->GenBuffers(1, &plane->gl_buffer); - gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer); - gl->BufferData(GL_PIXEL_UNPACK_BUFFER, buffer_size, - NULL, GL_DYNAMIC_DRAW); - } - gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer); - mpi->planes[n] = gl->MapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, - buffer_size, GL_MAP_WRITE_BIT | - GL_MAP_INVALIDATE_BUFFER_BIT); - gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - if (!mpi->planes[n]) { - unmap_image(p, mpi); - return false; - } - } - memset(mpi->bufs, 0, sizeof(mpi->bufs)); - return true; -} - // This assumes nv12, with textures set to GL_NEAREST filtering. static void reinterleave_vdpau(struct gl_video *p, struct gl_hwdec_frame *frame) { @@ -3024,32 +2911,19 @@ static bool gl_video_upload_image(struct gl_video *p, struct mp_image *mpi) gl_timer_start(p->upload_timer); - mp_image_t pbo_mpi = *mpi; - bool pbo = map_image(p, &pbo_mpi); - if (pbo) { - mp_image_copy(&pbo_mpi, mpi); - if (unmap_image(p, &pbo_mpi)) { - mpi = &pbo_mpi; - } else { - MP_FATAL(p, "Video PBO upload failed. Disabling PBOs.\n"); - pbo = false; - p->opts.pbo = 0; - } - } - vimg->image_flipped = mpi->stride[0] < 0; for (int n = 0; n < p->plane_count; n++) { struct texplane *plane = &vimg->planes[n]; - if (pbo) - gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, plane->gl_buffer); - gl->ActiveTexture(GL_TEXTURE0 + n); + + plane->flipped = mpi->stride[0] < 0; + gl->BindTexture(plane->gl_target, plane->gl_texture); - gl_upload_tex(gl, plane->gl_target, plane->gl_format, plane->gl_type, - mpi->planes[n], mpi->stride[n], 0, 0, plane->w, plane->h); + gl_pbo_upload_tex(&plane->pbo, gl, p->opts.pbo, plane->gl_target, + plane->gl_format, plane->gl_type, plane->w, plane->h, + mpi->planes[n], mpi->stride[n], + 0, 0, plane->w, plane->h); + gl->BindTexture(plane->gl_target, 0); } - gl->ActiveTexture(GL_TEXTURE0); - if (pbo) - gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); gl_timer_stop(p->upload_timer); |