vo_opengl: add support for custom shaders

This commit is contained in:
Niklas Haas 2015-03-27 13:27:40 +01:00
parent bf4dd877e9
commit 4d6b9550fe
No known key found for this signature in database
GPG Key ID: 3BA77D4BFDB10BCE
10 changed files with 350 additions and 55 deletions

View File

@ -342,6 +342,9 @@ Available video output drivers are:
exchange for adding some blur. This filter is good at temporal
interpolation, and also known as "smoothmotion" (see ``tscale``).
``custom``
A user-defined custom shader (see ``scale-shader``).
There are some more filters, but most are not as useful. For a complete
list, pass ``help`` as value, e.g.::
@ -525,6 +528,70 @@ Available video output drivers are:
feature doesn't work correctly with different scale factors in
different directions.
``source-shader=<file>``, ``scale-shader=<file>``, ``pre-shaders=<files>``, ``post-shaders=<files>``
Custom GLSL fragment shaders.
source-shader
This gets applied directly onto the source planes, before
any sort of upscaling or conversion whatsoever. For YCbCr content,
this means it gets applied on the luma and chroma planes
separately. In general, this shader shouldn't be making any
assumptions about the colorspace. It could be RGB, YCbCr, XYZ or
something else entirely. It's used purely for fixing numerical
quirks of the input, eg. debanding or deblocking.
pre-shaders (list)
These get applied after conversion to RGB and before linearization
and upscaling. Operates on non-linear RGB (same as input). This is
the best place to put things like sharpen filters.
scale-shader
This gets used instead of scale/cscale when those options are set
to ``custom``. The colorspace it operates on depends on the values
of ``linear-scaling`` and ``sigmoid-upscaling``, so no assumptions
should be made here.
post-shaders (list)
These get applied after upscaling and subtitle blending (when
``blend-subtitles`` is enabled), but before color management.
Operates on linear RGB if ``linear-scaling`` is in effect,
otherwise non-linear RGB. This is the best place for colorspace
transformations (eg. saturation mapping).
These files must define a function with the following signature::
vec4 sample(sampler2D tex, vec2 pos, vec2 tex_size)
The meanings of the parameters are as follows:
sampler2D tex
The source texture for the shader.
vec2 pos
The position to be sampled, in coordinate space [0-1].
vec2 tex_size
The size of the texture, in pixels. This may differ from image_size,
eg. for subsampled content or for post-shaders.
In addition to these parameters, the following uniforms are also
globally available:
float random
A random number in the range [0-1], different per frame.
int frame
A simple count of frames rendered, increases by one per frame and
never resets (regardless of seeks).
vec2 image_size
The size in pixels of the input image.
float cmul (source-shader only)
The multiplier needed to pull colors up to the right bit depth. The
source-shader must multiply any sampled colors by this, in order
to normalize them to the full scale.
For example, a shader that inverts the colors could look like this::
vec4 sample(sampler2D tex, vec2 pos, vec2 tex_size)
{
vec4 color = texture(tex, pos);
return vec4(1.0 - color.rgb, color.a);
}
``sigmoid-upscaling``
When upscaling, use a sigmoidal color transform to avoid emphasizing
ringing artifacts. This also enables ``linear-scaling``.
@ -705,8 +772,9 @@ Available video output drivers are:
Blend subtitles directly onto upscaled video frames, before
interpolation and/or color management (default: no). Enabling this
causes subtitles to be affected by ``icc-profile``, ``target-prim``,
``target-trc``, ``interpolation``, ``gamma`` and ``linear-scaling``.
It also increases subtitle performance when using ``interpolation``.
``target-trc``, ``interpolation``, ``gamma``, ``post-shader`` and
``linear-scaling``. It also increases subtitle performance when using
``interpolation``.
The downside of enabling this is that it restricts subtitles to the
visible portion of the video, so you can't have subtitles exist in the

View File

@ -929,6 +929,20 @@ struct bstr stream_read_complete(struct stream *s, void *talloc_ctx,
return (struct bstr){buf, total_read};
}
struct bstr stream_read_file(const char *filename, void *talloc_ctx,
struct mpv_global *global, int max_size)
{
struct bstr res = {0};
char *fname = mp_get_user_path(NULL, global, filename);
stream_t *s = stream_open(fname, global);
if (s) {
res = stream_read_complete(s, talloc_ctx, max_size);
free_stream(s);
}
talloc_free(fname);
return res;
}
struct mp_cancel {
atomic_bool triggered;
#ifdef __MINGW32__

View File

@ -258,6 +258,8 @@ struct mpv_global;
struct bstr stream_read_complete(struct stream *s, void *talloc_ctx,
int max_size);
struct bstr stream_read_file(const char *filename, void *talloc_ctx,
struct mpv_global *global, int max_size);
int stream_control(stream_t *s, int cmd, void *arg);
void free_stream(stream_t *s);
struct stream *stream_create(const char *url, int flags,

View File

@ -102,18 +102,6 @@ static void lcms2_error_handler(cmsContext ctx, cmsUInt32Number code,
MP_ERR(p, "lcms2: %s\n", msg);
}
static struct bstr load_file(void *talloc_ctx, const char *filename,
struct mpv_global *global)
{
struct bstr res = {0};
stream_t *s = stream_open(filename, global);
if (s) {
res = stream_read_complete(s, talloc_ctx, 1000000000);
free_stream(s);
}
return res;
}
static bool load_profile(struct gl_lcms *p)
{
if (p->icc_data && p->icc_size)
@ -124,7 +112,8 @@ static bool load_profile(struct gl_lcms *p)
char *fname = mp_get_user_path(NULL, p->global, p->icc_path);
MP_VERBOSE(p, "Opening ICC profile '%s'\n", fname);
struct bstr iccdata = load_file(p, fname, p->global);
struct bstr iccdata = stream_read_file(fname, p, p->global,
100000000); // 100 MB
talloc_free(fname);
if (!iccdata.len)
return false;
@ -238,7 +227,8 @@ bool gl_lcms_get_lut3d(struct gl_lcms *p, struct lut3d **result_lut3d)
// check cache
if (cache_file) {
MP_VERBOSE(p, "Opening 3D LUT cache in file '%s'.\n", cache_file);
struct bstr cachedata = load_file(tmp, cache_file, p->global);
struct bstr cachedata = stream_read_file(cache_file, tmp, p->global,
1000000000); // 1 GB
if (cachedata.len == talloc_get_size(output)) {
memcpy(output, cachedata.start, cachedata.len);
goto done;

View File

@ -28,6 +28,7 @@
#include <stdarg.h>
#include <assert.h>
#include "stream/stream.h"
#include "common/common.h"
#include "gl_utils.h"
@ -464,6 +465,7 @@ void gl_set_debug_logger(GL *gl, struct mp_log *log)
#define SC_ENTRIES 16
#define SC_UNIFORM_ENTRIES 20
#define SC_FILE_ENTRIES 10
enum uniform_type {
UT_invalid,
@ -484,6 +486,11 @@ struct sc_uniform {
} v;
};
struct sc_file {
char *path;
char *body;
};
struct sc_entry {
GLuint gl_shader;
// the following fields define the shader's contents
@ -494,9 +501,11 @@ struct sc_entry {
struct gl_shader_cache {
GL *gl;
struct mp_log *log;
struct mpv_global *global;
// this is modified during use (gl_sc_add() etc.)
char *text;
char *header_text;
struct gl_vao *vao;
struct sc_entry entries[SC_ENTRIES];
@ -504,15 +513,21 @@ struct gl_shader_cache {
struct sc_uniform uniforms[SC_UNIFORM_ENTRIES];
int num_uniforms;
struct sc_file files[SC_FILE_ENTRIES];
int num_files;
};
struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log)
struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log,
struct mpv_global *global)
{
struct gl_shader_cache *sc = talloc_ptrtype(NULL, sc);
*sc = (struct gl_shader_cache){
.gl = gl,
.log = log,
.global = global,
.text = talloc_strdup(sc, ""),
.header_text = talloc_strdup(sc, ""),
};
return sc;
}
@ -520,6 +535,7 @@ struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log)
void gl_sc_reset(struct gl_shader_cache *sc)
{
sc->text[0] = '\0';
sc->header_text[0] = '\0';
for (int n = 0; n < sc->num_uniforms; n++)
talloc_free(sc->uniforms[n].name);
sc->num_uniforms = 0;
@ -555,6 +571,40 @@ void gl_sc_addf(struct gl_shader_cache *sc, const char *textf, ...)
va_end(ap);
}
void gl_sc_hadd(struct gl_shader_cache *sc, const char *text)
{
sc->header_text = talloc_strdup_append(sc->header_text, text);
}
const char *gl_sc_loadfile(struct gl_shader_cache *sc, const char *path)
{
if (!path || !path[0] || !sc->global)
return NULL;
for (int n = 0; n < sc->num_files; n++) {
if (strcmp(sc->files[n].path, path) == 0)
return sc->files[n].body;
}
// not found -> load it
if (sc->num_files == SC_FILE_ENTRIES) {
// empty cache when it overflows
for (int n = 0; n < sc->num_files; n++) {
talloc_free(sc->files[n].path);
talloc_free(sc->files[n].body);
}
sc->num_files = 0;
}
struct bstr s = stream_read_file(path, sc, sc->global, 100000); // 100 kB
if (s.len) {
struct sc_file *new = &sc->files[sc->num_files++];
*new = (struct sc_file) {
.path = talloc_strdup(NULL, path),
.body = s.start
};
return new->body;
}
return NULL;
}
static struct sc_uniform *find_uniform(struct gl_shader_cache *sc,
const char *name)
{
@ -594,6 +644,15 @@ void gl_sc_uniform_f(struct gl_shader_cache *sc, char *name, GLfloat f)
u->v.f[0] = f;
}
void gl_sc_uniform_i(struct gl_shader_cache *sc, char *name, GLint i)
{
struct sc_uniform *u = find_uniform(sc, name);
u->type = UT_i;
u->size = 1;
u->glsl_type = "int";
u->v.i[0] = i;
}
void gl_sc_uniform_vec2(struct gl_shader_cache *sc, char *name, GLfloat f[2])
{
struct sc_uniform *u = find_uniform(sc, name);
@ -762,6 +821,11 @@ static GLuint create_program(struct gl_shader_cache *sc, const char *vertex,
{
GL *gl = sc->gl;
MP_VERBOSE(sc, "recompiling a shader program:\n");
if (sc->header_text[0]) {
MP_VERBOSE(sc, "header:\n");
mp_log_source(sc->log, MSGL_V, sc->header_text);
MP_VERBOSE(sc, "body:\n");
}
mp_log_source(sc->log, MSGL_V, sc->text);
GLuint prog = gl->CreateProgram();
compile_attach_shader(sc, prog, GL_VERTEX_SHADER, vertex);
@ -838,6 +902,12 @@ void gl_sc_gen_shader_and_reset(struct gl_shader_cache *sc)
struct sc_uniform *u = &sc->uniforms[n];
ADD(frag, "uniform %s %s;\n", u->glsl_type, u->name);
}
// custom shader header
if (sc->header_text[0]) {
ADD(frag, "// header\n");
ADD(frag, "%s\n", sc->header_text);
ADD(frag, "// body\n");
}
ADD(frag, "void main() {\n");
ADD(frag, "%s", sc->text);
// we require _all_ frag shaders to write to a "vec4 color"

View File

@ -119,13 +119,17 @@ void gl_set_debug_logger(GL *gl, struct mp_log *log);
struct gl_shader_cache;
struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log);
struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log,
struct mpv_global *global);
void gl_sc_destroy(struct gl_shader_cache *sc);
void gl_sc_add(struct gl_shader_cache *sc, const char *text);
void gl_sc_addf(struct gl_shader_cache *sc, const char *textf, ...);
void gl_sc_hadd(struct gl_shader_cache *sc, const char *text);
const char *gl_sc_loadfile(struct gl_shader_cache *sc, const char *path);
void gl_sc_uniform_sampler(struct gl_shader_cache *sc, char *name, GLenum target,
int unit);
void gl_sc_uniform_f(struct gl_shader_cache *sc, char *name, GLfloat f);
void gl_sc_uniform_i(struct gl_shader_cache *sc, char *name, GLint f);
void gl_sc_uniform_vec2(struct gl_shader_cache *sc, char *name, GLfloat f[2]);
void gl_sc_uniform_vec3(struct gl_shader_cache *sc, char *name, GLfloat f[3]);
void gl_sc_uniform_mat2(struct gl_shader_cache *sc, char *name,

View File

@ -27,6 +27,7 @@
#include <assert.h>
#include <libavutil/common.h>
#include <libavutil/lfg.h>
#include "gl_video.h"
@ -59,6 +60,7 @@ static const char *const fixed_scale_filters[] = {
"sharpen3",
"sharpen5",
"oversample",
"custom",
NULL
};
static const char *const fixed_tscale_filters[] = {
@ -144,6 +146,7 @@ struct src_tex {
struct gl_video {
GL *gl;
struct mpv_global *global;
struct mp_log *log;
struct gl_video_opts opts;
bool gl_debug;
@ -179,11 +182,17 @@ struct gl_video {
struct video_image image;
struct fbotex indirect_fbo; // RGB target
struct fbotex chroma_merge_fbo;
struct fbotex source_fbo;
struct fbotex indirect_fbo;
struct fbotex blend_subs_fbo;
struct fbosurface surfaces[FBOSURFACES_MAX];
// these are duplicated so we can keep rendering back and forth between
// them to support an unlimited number of shader passes per step
struct fbotex pre_fbo[2];
struct fbotex post_fbo[2];
int surface_idx;
int surface_now;
bool is_interpolated;
@ -202,10 +211,13 @@ struct gl_video {
struct src_tex pass_tex[TEXUNIT_VIDEO_NUM];
bool use_indirect;
bool use_linear;
bool use_full_range;
float user_gamma;
struct fbotex copy_fbos[4];
int frames_uploaded;
int frames_rendered;
AVLFG lfg;
// Cached because computing it can take relatively long
int last_dither_matrix_size;
@ -437,6 +449,10 @@ const struct m_sub_options gl_video_conf = {
({"no", 0},
{"yes", 1},
{"video", 2})),
OPT_STRING("source-shader", source_shader, 0),
OPT_STRING("scale-shader", scale_shader, 0),
OPT_STRINGLIST("pre-shaders", pre_shaders, 0),
OPT_STRINGLIST("post-shaders", post_shaders, 0),
OPT_REMOVED("approx-gamma", "this is always enabled now"),
OPT_REMOVED("cscale-down", "chroma is never downscaled"),
@ -551,10 +567,16 @@ static void uninit_rendering(struct gl_video *p)
gl->DeleteTextures(1, &p->dither_texture);
p->dither_texture = 0;
fbotex_uninit(&p->indirect_fbo);
fbotex_uninit(&p->chroma_merge_fbo);
fbotex_uninit(&p->source_fbo);
fbotex_uninit(&p->indirect_fbo);
fbotex_uninit(&p->blend_subs_fbo);
for (int n = 0; n < 2; n++) {
fbotex_uninit(&p->pre_fbo[n]);
fbotex_uninit(&p->post_fbo[n]);
}
for (int n = 0; n < FBOSURFACES_MAX; n++)
fbotex_uninit(&p->surfaces[n].fbotex);
@ -691,6 +713,8 @@ static void init_video(struct gl_video *p)
eq_caps |= MP_CSP_EQ_CAPS_BRIGHTNESS;
p->video_eq.capabilities = eq_caps;
av_lfg_init(&p->lfg, 1);
debug_check_gl(p, "before video texture creation");
struct video_image *vimg = &p->image;
@ -874,6 +898,39 @@ static void uninit_scaler(struct gl_video *p, struct scaler *scaler)
scaler->initialized = false;
}
static void load_shader(struct gl_video *p, const char *body)
{
gl_sc_hadd(p->sc, body);
gl_sc_uniform_f(p->sc, "random", (double)av_lfg_get(&p->lfg) / UINT32_MAX);
gl_sc_uniform_f(p->sc, "frame", p->frames_uploaded);
gl_sc_uniform_vec2(p->sc, "image_size", (GLfloat[]){p->image_w, p->image_h});
}
// Applies an arbitrary number of shaders in sequence, using the given pair
// of FBOs as intermediate buffers. Returns whether any shaders were applied.
static bool apply_shaders(struct gl_video *p, char **shaders,
struct fbotex textures[2], int tex_num,
int tex_w, int tex_h)
{
if (!shaders)
return false;
bool success = false;
int tex = 0;
for (int n = 0; shaders[n]; n++) {
const char *body = gl_sc_loadfile(p->sc, shaders[n]);
if (!body)
continue;
finish_pass_fbo(p, &textures[tex], tex_w, tex_h, tex_num, 0);
load_shader(p, body);
GLSLF("// custom shader\n");
GLSLF("vec4 color = sample(texture%d, texcoord%d, texture_size%d);\n",
tex_num, tex_num, tex_num);
tex = (tex+1) % 2;
success = true;
}
return success;
}
// Semantic equality
static bool double_seq(double a, double b)
{
@ -1296,6 +1353,15 @@ static void pass_sample(struct gl_video *p, int src_tex, struct scaler *scaler,
pass_sample_sharpen5(p, scaler);
} else if (strcmp(name, "oversample") == 0) {
pass_sample_oversample(p, scaler, w, h);
} else if (strcmp(name, "custom") == 0) {
const char *body = gl_sc_loadfile(p->sc, p->opts.scale_shader);
if (body) {
load_shader(p, body);
GLSLF("// custom scale-shader\n");
GLSL(vec4 color = sample(tex, pos, size);)
} else {
p->opts.scale_shader = NULL;
}
} else if (scaler->kernel && scaler->kernel->polar) {
pass_sample_polar(p, scaler);
} else if (scaler->kernel) {
@ -1347,53 +1413,116 @@ static void pass_read_video(struct gl_video *p)
if (p->gl->version < 300 && p->pass_tex[0].gl_target == GL_TEXTURE_RECTANGLE)
pass_copy_from_rect(p);
// The custom shader logic is a bit tricky, but there are basically three
// different places it can occur: RGB, or chroma *and* luma (which are
// treated separately even for 4:4:4 content, but the minor speed loss
// is not worth the complexity it would require).
const char *shader = gl_sc_loadfile(p->sc, p->opts.source_shader);
// Since this is before normalization, we have to take into account
// the bit depth. Specifically, we want the shader to perform normalization
// to 16 bit because otherwise it results in bad quantization, especially
// with 8-bit FBOs (where it just destroys the image completely)
int in_bits = p->image_desc.component_bits,
tx_bits = (in_bits + 7) & ~7;
float cmul = ((1 << tx_bits) - 1.0) / ((1 << in_bits) - 1.0);
// Custom source shaders are required to output at the full range
p->use_full_range = shader != NULL;
// Special case for non-planar content
if (p->plane_count == 1) {
GLSL(vec4 color = texture(texture0, texcoord0);)
if (shader) {
load_shader(p, shader);
GLSLF("// custom source-shader (RGB)\n");
gl_sc_uniform_f(p->sc, "cmul", cmul);
GLSL(vec4 color = sample(texture0, texcoord0, texture_size0);)
p->use_indirect = true;
} else {
GLSL(vec4 color = texture(texture0, texcoord0);)
}
return;
}
// Chroma preprocessing (merging -> shaders -> scaling)
struct src_tex luma = p->pass_tex[0];
int c_w = p->pass_tex[1].src.x1 - p->pass_tex[1].src.x0;
int c_h = p->pass_tex[1].src.y1 - p->pass_tex[1].src.y0;
const struct scaler_config *cscale = &p->opts.scaler[2];
if (p->image_desc.flags & MP_IMGFLAG_SUBSAMPLED &&
strcmp(cscale->kernel.name, "bilinear") != 0) {
struct src_tex luma = p->pass_tex[0];
if (p->plane_count > 2) {
// For simplicity and performance, we merge the chroma planes
// into a single texture before scaling, so the scaler doesn't
// need to run multiple times.
GLSLF("// chroma merging\n");
GLSL(vec4 color = vec4(texture(texture1, texcoord1).r,
texture(texture2, texcoord2).r,
0.0, 1.0);)
int c_w = p->pass_tex[1].src.x1 - p->pass_tex[1].src.x0;
int c_h = p->pass_tex[1].src.y1 - p->pass_tex[1].src.y0;
assert(c_w == p->pass_tex[2].src.x1 - p->pass_tex[2].src.x0);
assert(c_h == p->pass_tex[2].src.y1 - p->pass_tex[2].src.y0);
finish_pass_fbo(p, &p->chroma_merge_fbo, c_w, c_h, 1, 0);
}
// Non-trivial sampling is needed on the chroma plane
bool nontrivial = p->image_desc.flags & MP_IMGFLAG_SUBSAMPLED &&
strcmp(cscale->kernel.name, "bilinear") != 0;
bool merged = false;
if (p->plane_count > 2 && (nontrivial || shader)) {
// For simplicity and performance, we merge the chroma planes
// into a single texture before scaling or shading, so the shader
// doesn't need to run multiple times.
GLSLF("// chroma merging\n");
GLSL(vec4 color = vec4(texture(texture1, texcoord1).r,
texture(texture2, texcoord2).r,
0.0, 1.0);)
// We also pull up here in this case to avoid the issues described
// above.
GLSLF("color.rg *= %f;\n", cmul);
p->use_full_range = true;
merged = true;
assert(c_w == p->pass_tex[2].src.x1 - p->pass_tex[2].src.x0);
assert(c_h == p->pass_tex[2].src.y1 - p->pass_tex[2].src.y0);
finish_pass_fbo(p, &p->chroma_merge_fbo, c_w, c_h, 1, 0);
}
if (shader) {
// Chroma plane shader logic
load_shader(p, shader);
gl_sc_uniform_f(p->sc, "cmul", merged ? 1.0 : cmul);
GLSLF("// custom source-shader (chroma)\n");
GLSL(vec4 color = sample(texture1, texcoord1, texture_size1);)
GLSL(color.ba = vec2(0.0, 1.0);) // skip unused
finish_pass_fbo(p, &p->source_fbo, c_w, c_h, 1, 0);
p->use_indirect = true;
}
if (p->image_desc.flags & MP_IMGFLAG_SUBSAMPLED && nontrivial) {
GLSLF("// chroma scaling\n");
pass_sample(p, 1, &p->scaler[2], cscale, 1.0, p->image_w, p->image_h,
chromafix);
GLSL(vec2 chroma = color.rg;)
// Always force rendering to a FBO before main scaling, or we would
// scale chroma incorrectly.
p->use_indirect = true;
p->pass_tex[0] = luma; // Restore luma after scaling
} else {
// No explicit scaling needed, either because it's trivial (ie.
// bilinear), or because there's no subsampling. We have to manually
// apply the fix to the chroma coordinates because it's not implied by
// pass_sample.
GLSL(vec4 color;)
if (p->plane_count == 2) {
gl_transform_rect(chromafix, &p->pass_tex[1].src);
GLSL(vec2 chroma = texture(texture1, texcoord1).rg;) // NV formats
} else {
gl_transform_rect(chromafix, &p->pass_tex[1].src);
gl_transform_rect(chromafix, &p->pass_tex[1].src);
if (p->plane_count > 2 && !merged) {
gl_transform_rect(chromafix, &p->pass_tex[2].src);
GLSL(vec2 chroma = vec2(texture(texture1, texcoord1).r,
texture(texture2, texcoord2).r);)
} else {
GLSL(vec2 chroma = texture(texture1, texcoord1).rg;)
}
}
GLSL(color = vec4(texture(texture0, texcoord0).r, chroma, 1.0);)
if (p->has_alpha && p->plane_count >= 4)
p->pass_tex[0] = luma; // Restore the luma plane
if (shader) {
load_shader(p, shader);
gl_sc_uniform_f(p->sc, "cmul", cmul);
GLSLF("// custom source-shader (luma)\n");
GLSL(float luma = sample(texture0, texcoord0, texture_size0).r;)
p->use_indirect = true;
} else {
GLSL(float luma = texture(texture0, texcoord0).r;)
if (p->use_full_range)
GLSLF("luma *= %f;\n", cmul);
}
GLSL(color = vec4(luma, chroma, 1.0);)
if (p->has_alpha && p->plane_count >= 4) {
GLSL(color.a = texture(texture3, texcoord3).r;)
if (p->use_full_range)
GLSLF("color.a *= %f;\n", cmul);
}
}
// yuv conversion, and any other conversions before main up/down-scaling
@ -1425,6 +1554,10 @@ static void pass_convert_yuv(struct gl_video *p)
GLSL(color.rgb = pow(color.rgb, vec3(2.6));)
}
// Something already took care of expansion
if (p->use_full_range)
cparams.input_bits = cparams.texture_bits;
// Conversion from Y'CbCr or other linear spaces to RGB
if (!p->is_rgb) {
struct mp_cmat m = {{{0}}};
@ -1870,12 +2003,18 @@ static void pass_render_frame(struct gl_video *p)
GLSL(vec4 color = texture(texture0, texcoord0);)
}
if (apply_shaders(p, p->opts.pre_shaders, &p->pre_fbo[0], 0,
p->image_w, p->image_h))
{
p->use_indirect = true;
}
pass_scale_main(p);
int vp_w = p->dst_rect.x1 - p->dst_rect.x0,
vp_h = p->dst_rect.y1 - p->dst_rect.y0;
if (p->osd && p->opts.blend_subs == 1) {
// Recreate the real video size from the src/dst rects
int vp_w = p->dst_rect.x1 - p->dst_rect.x0,
vp_h = p->dst_rect.y1 - p->dst_rect.y0;
struct mp_osd_res rect = {
.w = vp_w, .h = vp_h,
.ml = -p->src_rect.x0, .mr = p->src_rect.x1 - p->image_w,
@ -1897,6 +2036,8 @@ static void pass_render_frame(struct gl_video *p)
if (p->use_linear)
pass_linearize(p, p->image_params.gamma);
}
apply_shaders(p, p->opts.post_shaders, &p->post_fbo[0], 0, vp_w, vp_h);
}
static void pass_draw_to_screen(struct gl_video *p, int fbo)
@ -2170,6 +2311,7 @@ static void gl_video_upload_image(struct gl_video *p)
return;
vimg->needs_upload = false;
p->frames_uploaded++;
assert(mpi->num_planes == p->plane_count);
@ -2574,7 +2716,7 @@ void gl_video_set_osd_source(struct gl_video *p, struct osd_state *osd)
recreate_osd(p);
}
struct gl_video *gl_video_init(GL *gl, struct mp_log *log)
struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct mpv_global *g)
{
if (gl->version < 210 && gl->es < 200) {
mp_err(log, "At least OpenGL 2.1 or OpenGL ES 2.0 required.\n");
@ -2584,12 +2726,13 @@ struct gl_video *gl_video_init(GL *gl, struct mp_log *log)
struct gl_video *p = talloc_ptrtype(NULL, p);
*p = (struct gl_video) {
.gl = gl,
.global = g,
.log = log,
.opts = gl_video_opts_def,
.gl_target = GL_TEXTURE_2D,
.texture_16bit_depth = 16,
.scaler = {{.index = 0}, {.index = 1}, {.index = 2}, {.index = 3}},
.sc = gl_sc_create(gl, log),
.sc = gl_sc_create(gl, log, g),
};
gl_video_set_debug(p, true);
init_gl(p);

View File

@ -66,6 +66,10 @@ struct gl_video_opts {
struct m_color background;
int interpolation;
int blend_subs;
char *source_shader;
char *scale_shader;
char **pre_shaders;
char **post_shaders;
};
extern const struct m_sub_options gl_video_conf;
@ -74,7 +78,7 @@ extern const struct gl_video_opts gl_video_opts_def;
struct gl_video;
struct gl_video *gl_video_init(GL *gl, struct mp_log *log);
struct gl_video *gl_video_init(GL *gl, struct mp_log *log, struct mpv_global *g);
void gl_video_uninit(struct gl_video *p);
void gl_video_set_osd_source(struct gl_video *p, struct osd_state *osd);
void gl_video_set_options(struct gl_video *p, struct gl_video_opts *opts,

View File

@ -434,7 +434,7 @@ static int preinit(struct vo *vo)
}
p->current_swap_interval = p->swap_interval;
p->renderer = gl_video_init(p->gl, vo->log);
p->renderer = gl_video_init(p->gl, vo->log, vo->global);
if (!p->renderer)
goto err_out;
gl_video_set_osd_source(p->renderer, vo->osd);

View File

@ -233,7 +233,7 @@ int mpv_opengl_cb_init_gl(struct mpv_opengl_cb_context *ctx, const char *exts,
mpgl_load_functions2(ctx->gl, get_proc_address, get_proc_address_ctx,
exts, ctx->log);
ctx->renderer = gl_video_init(ctx->gl, ctx->log);
ctx->renderer = gl_video_init(ctx->gl, ctx->log, NULL);
if (!ctx->renderer)
return MPV_ERROR_UNSUPPORTED;