summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-03-12 23:21:21 +0100
committerwm4 <wm4@nowhere>2015-03-12 23:21:21 +0100
commit73cf6155557abb27917371ec7d4f87f5fefb961f (patch)
tree382713c02863c460e5c9b4007bf4bf8b4d89e49e
parentae6019cbc98cfad2613e89a80bee79ce6b2f1319 (diff)
parent3974a5ca5e55ce00e8177a672e0627bfabee4118 (diff)
downloadmpv-73cf6155557abb27917371ec7d4f87f5fefb961f.tar.bz2
mpv-73cf6155557abb27917371ec7d4f87f5fefb961f.tar.xz
Merge branch 'gl_rework'
-rw-r--r--.gitignore1
-rw-r--r--DOCS/man/vo.rst68
-rw-r--r--old-makefile5
-rw-r--r--video/csputils.c3
-rw-r--r--video/csputils.h1
-rw-r--r--video/out/gl_osd.c200
-rw-r--r--video/out/gl_osd.h39
-rw-r--r--video/out/gl_utils.c502
-rw-r--r--video/out/gl_utils.h53
-rw-r--r--video/out/gl_video.c2443
-rw-r--r--video/out/gl_video.h11
-rw-r--r--video/out/gl_video_shaders.glsl542
-rw-r--r--video/out/vo_opengl.c5
-rw-r--r--video/out/vo_opengl_cb.c20
-rw-r--r--wscript_build.py4
15 files changed, 1852 insertions, 2045 deletions
diff --git a/.gitignore b/.gitignore
index b8ff3a8426..159e409d7d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,7 +14,6 @@
/input/input_conf.h
/tags
/TAGS
-/video/out/gl_video_shaders.h
/video/out/x11_icon.inc
/demux/ebml_defs.c
/demux/ebml_types.h
diff --git a/DOCS/man/vo.rst b/DOCS/man/vo.rst
index e73f1d578a..82611e5a19 100644
--- a/DOCS/man/vo.rst
+++ b/DOCS/man/vo.rst
@@ -352,6 +352,10 @@ Available video output drivers are:
blurrier. Defaults to 1. Note that setting this too low (eg. 0.5)
leads to bad results. It's recommended to stay between 0.9 and 1.1.
+ ``sharpen3``, ``sharpen5``
+ Sharpening strength. Increasing this makes the image sharper but
+ adds more ringing and aliasing. Defaults to 0.5.
+
``scale-radius=<r>``
Set radius for filters listed below, must be a float number between 1.0
and 16.0. Defaults to be 3.0 if not specified.
@@ -377,21 +381,6 @@ Available video output drivers are:
will reproduce the source image perfectly if no scaling is performed.
Note that this option never affects ``cscale``.
- ``srgb``
- Convert and color correct the output to sRGB before displaying it on
- the screen. This option enables ``linear-scaling``.
-
- This option is equivalent to using ``icc-profile`` with an sRGB ICC
- profile, but it is implemented without a 3DLUT and does not require
- LittleCMS 2. If both ``srgb`` and ``icc-profile`` are present, the
- latter takes precedence, as they are somewhat redundant.
-
- Note: When playing back BT.2020 content with this option enabled, out
- of gamut colors will be numerically clipped, which can potentially
- change the hue and/or luminance. If this is not desired, it is
- recommended to use ``icc-profile`` with an sRGB ICC profile instead,
- when playing back wide-gamut BT.2020 content.
-
``pbo``
Enable use of PBOs. This is slightly faster, but can sometimes lead to
sporadic and temporary image corruption (in theory, because reupload
@@ -460,9 +449,10 @@ Available video output drivers are:
``scale-antiring``.
``linear-scaling``
- Scale in linear light. This is automatically enabled if ``srgb``,
- ``icc-profile`` or ``sigmoid-upscaling`` is set. It should only
- be used with a ``fbo-format`` that has at least 16 bit precision.
+ Scale in linear light. This is automatically enabled if
+ ``target-prim``, ``target-trc``, ``icc-profile`` or
+ ``sigmoid-upscaling`` is set. It should only be used with a
+ ``fbo-format`` that has at least 16 bit precision.
``fancy-downscaling``
When using convolution based filters, extend the filter size
@@ -553,13 +543,44 @@ Available video output drivers are:
NOTE: Only implemented on OS X.
+ ``target-prim=<value>``
+ Specifies the primaries of the display. Video colors will be adapted
+ to this colorspace if necessary. Valid values are:
+
+ auto
+ Disable any adaptation (default)
+ bt470m
+ ITU-R BT.470 M
+ bt601-525
+ ITU-R BT.601 (525-line SD systems, eg. NTSC), SMPTE 170M/240M
+ bt601-625
+ ITU-R BT.601 (625-line SD systems, eg. PAL/SECAM), ITU-R BT.470 B/G
+ bt709
+ ITU-R BT.709 (HD), IEC 61966-2-4 (sRGB), SMPTE RP177 Annex B
+ bt2020
+ ITU-R BT.2020 (UHD)
+
+ ``target-trc=<value>``
+ Specifies the transfer characteristics (gamma) of the display. Video
+ colors will be adjusted to this curve. Valid values are:
+
+ auto
+ Disable any adaptation (default)
+ bt1886
+ ITU-R BT.1886 curve, without the brightness drop (approx. 1.961)
+ srgb
+ IEC 61966-2-4 (sRGB)
+ linear
+ Linear light output
+ gamma22
+ Pure power curve (gamma 2.2)
+
``icc-profile=<file>``
Load an ICC profile and use it to transform linear RGB to screen output.
- Needs LittleCMS 2 support compiled in. This option overrides the ``srgb``
- property, as using both is somewhat redundant. It also enables
+ Needs LittleCMS 2 support compiled in. This option overrides the
+ ``target-prim`` and ``target-trc`` options. It also enables
``linear-scaling``.
-
``icc-profile-auto``
Automatically select the ICC display profile currently specified by
the display settings of the operating system.
@@ -573,9 +594,8 @@ Available video output drivers are:
Its size depends on the ``3dlut-size``, and can be very big.
``icc-intent=<value>``
- Specifies the ICC Intent used for transformations between color spaces.
- This affects the rendering when using ``icc-profile`` or ``srgb`` and
- also affects the way DCP XYZ content gets converted to RGB.
+ Specifies the ICC intent used for the color transformation (when using
+ ``icc-profile``).
0
perceptual
diff --git a/old-makefile b/old-makefile
index b9fbbaaa50..b81b106e60 100644
--- a/old-makefile
+++ b/old-makefile
@@ -358,10 +358,6 @@ demux/ebml.c: demux/ebml_defs.c
demux/ebml_defs.c: TOOLS/matroska.pl $(MKVLIB_DEPS)
./$< --generate-definitions > $@
-video/out/gl_video.c: video/out/gl_video_shaders.h
-video/out/gl_video_shaders.h: TOOLS/file2string.pl video/out/gl_video_shaders.glsl
- ./$^ >$@
-
video/out/x11_common.c: video/out/x11_icon.inc
video/out/x11_icon.inc: TOOLS/file2string.pl video/out/x11_icon.bin
./$^ >$@
@@ -475,7 +471,6 @@ clean:
-$(RM) input/input_conf.h
-$(RM) video/out/vdpau_template.c
-$(RM) demux/ebml_types.h demux/ebml_defs.c
- -$(RM) video/out/gl_video_shaders.h
-$(RM) video/out/x11_icon.inc
-$(RM) sub/osd_font.h
-$(RM) player/lua/defaults.inc
diff --git a/video/csputils.c b/video/csputils.c
index cee33dbba9..06de4bb9e8 100644
--- a/video/csputils.c
+++ b/video/csputils.c
@@ -70,6 +70,7 @@ const char *const mp_csp_trc_names[MP_CSP_TRC_COUNT] = {
"BT.1886 (SD, HD, UHD)",
"sRGB (IEC 61966-2-1)",
"Linear light",
+ "Pure power (gamma 2.2)",
};
const char *const mp_csp_equalizer_names[MP_CSP_EQ_COUNT] = {
@@ -156,6 +157,7 @@ enum mp_csp_trc avcol_trc_to_mp_csp_trc(int avtrc)
case AVCOL_TRC_BT2020_12: return MP_CSP_TRC_BT_1886;
case AVCOL_TRC_IEC61966_2_1: return MP_CSP_TRC_SRGB;
case AVCOL_TRC_LINEAR: return MP_CSP_TRC_LINEAR;
+ case AVCOL_TRC_GAMMA22: return MP_CSP_TRC_GAMMA22;
default: return MP_CSP_TRC_AUTO;
}
}
@@ -202,6 +204,7 @@ int mp_csp_trc_to_avcol_trc(enum mp_csp_trc trc)
case MP_CSP_TRC_BT_1886: return AVCOL_TRC_BT709;
case MP_CSP_TRC_SRGB: return AVCOL_TRC_IEC61966_2_1;
case MP_CSP_TRC_LINEAR: return AVCOL_TRC_LINEAR;
+ case MP_CSP_TRC_GAMMA22: return AVCOL_TRC_GAMMA22;
default: return AVCOL_TRC_UNSPECIFIED;
}
}
diff --git a/video/csputils.h b/video/csputils.h
index a082682e43..a68c106549 100644
--- a/video/csputils.h
+++ b/video/csputils.h
@@ -76,6 +76,7 @@ enum mp_csp_trc {
MP_CSP_TRC_BT_1886,
MP_CSP_TRC_SRGB,
MP_CSP_TRC_LINEAR,
+ MP_CSP_TRC_GAMMA22,
MP_CSP_TRC_COUNT
};
diff --git a/video/out/gl_osd.c b/video/out/gl_osd.c
index 117d7feedb..7a9532d416 100644
--- a/video/out/gl_osd.c
+++ b/video/out/gl_osd.c
@@ -55,20 +55,46 @@ static const struct osd_fmt_entry osd_to_gl2_formats[SUBBITMAP_COUNT] = {
struct vertex {
float position[2];
- uint8_t color[4];
float texcoord[2];
+ uint8_t ass_color[4];
};
static const struct gl_vao_entry vertex_vao[] = {
- {"vertex_position", 2, GL_FLOAT, false, offsetof(struct vertex, position)},
- {"vertex_color", 4, GL_UNSIGNED_BYTE, true, offsetof(struct vertex, color)},
- {"vertex_texcoord", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord)},
+ {"position", 2, GL_FLOAT, false, offsetof(struct vertex, position)},
+ {"texcoord" , 2, GL_FLOAT, false, offsetof(struct vertex, texcoord)},
+ {"ass_color", 4, GL_UNSIGNED_BYTE, true, offsetof(struct vertex, ass_color)},
{0}
};
-// programs: SUBBITMAP_COUNT elements
-struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd,
- GLuint *programs)
+struct mpgl_osd_part {
+ enum sub_bitmap_format format;
+ int bitmap_id, bitmap_pos_id;
+ GLuint texture;
+ int w, h;
+ GLuint buffer;
+ int num_subparts;
+ struct sub_bitmap *subparts;
+ struct vertex *vertices;
+ struct bitmap_packer *packer;
+};
+
+struct mpgl_osd {
+ struct mp_log *log;
+ struct osd_state *osd;
+ GL *gl;
+ bool use_pbo;
+ bool scaled;
+ struct mpgl_osd_part *parts[MAX_OSD_PARTS];
+ const struct osd_fmt_entry *fmt_table;
+ bool formats[SUBBITMAP_COUNT];
+ struct gl_vao vao;
+ // temporary
+ int stereo_mode;
+ int display_size[2];
+ void *scratch;
+};
+
+struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd)
{
GLint max_texture_size;
gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
@@ -79,7 +105,6 @@ struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd
.osd = osd,
.gl = gl,
.fmt_table = osd_to_gl3_formats,
- .programs = programs,
.scratch = talloc_zero_size(ctx, 1),
};
@@ -126,6 +151,11 @@ void mpgl_osd_destroy(struct mpgl_osd *ctx)
talloc_free(ctx);
}
+void mpgl_osd_set_options(struct mpgl_osd *ctx, bool pbo)
+{
+ ctx->use_pbo = pbo;
+}
+
static bool upload_pbo(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
struct sub_bitmaps *imgs)
{
@@ -154,8 +184,7 @@ static bool upload_pbo(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
success = false;
glUploadTex(gl, GL_TEXTURE_2D, fmt.format, fmt.type, NULL, stride,
- bb[0].x, bb[0].y, bb[1].x - bb[0].x, bb[1].y - bb[0].y,
- 0);
+ bb[0].x, bb[0].y, bb[1].x - bb[0].x, bb[1].y - bb[0].y, 0);
}
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
@@ -240,11 +269,12 @@ static bool upload_osd(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
return true;
}
-static struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
- struct sub_bitmaps *imgs)
+static void gen_osd_cb(void *pctx, struct sub_bitmaps *imgs)
{
+ struct mpgl_osd *ctx = pctx;
+
if (imgs->num_parts == 0 || !ctx->formats[imgs->format])
- return NULL;
+ return;
struct mpgl_osd_part *osd = ctx->parts[imgs->render_index];
@@ -256,83 +286,76 @@ static struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
osd->bitmap_id = imgs->bitmap_id;
osd->bitmap_pos_id = imgs->bitmap_pos_id;
- osd->num_vertices = 0;
}
+ osd->num_subparts = osd->packer->count;
- return osd->packer->count ? osd : NULL;
+ MP_TARRAY_GROW(osd, osd->subparts, osd->num_subparts);
+ memcpy(osd->subparts, imgs->parts,
+ osd->num_subparts * sizeof(osd->subparts[0]));
}
-static void write_quad(struct vertex *va,
+static void write_quad(struct vertex *va, float matrix[3][2],
float x0, float y0, float x1, float y1,
float tx0, float ty0, float tx1, float ty1,
float tex_w, float tex_h, const uint8_t color[4])
{
+ gl_matrix_mul_vec(matrix, &x0, &y0);
+ gl_matrix_mul_vec(matrix, &x1, &y1);
+
#define COLOR_INIT {color[0], color[1], color[2], color[3]}
- va[0] = (struct vertex){ {x0, y0}, COLOR_INIT, {tx0 / tex_w, ty0 / tex_h} };
- va[1] = (struct vertex){ {x0, y1}, COLOR_INIT, {tx0 / tex_w, ty1 / tex_h} };
- va[2] = (struct vertex){ {x1, y0}, COLOR_INIT, {tx1 / tex_w, ty0 / tex_h} };
- va[3] = (struct vertex){ {x1, y1}, COLOR_INIT, {tx1 / tex_w, ty1 / tex_h} };
+ va[0] = (struct vertex){ {x0, y0}, {tx0 / tex_w, ty0 / tex_h}, COLOR_INIT };
+ va[1] = (struct vertex){ {x0, y1}, {tx0 / tex_w, ty1 / tex_h}, COLOR_INIT };
+ va[2] = (struct vertex){ {x1, y0}, {tx1 / tex_w, ty0 / tex_h}, COLOR_INIT };
+ va[3] = (struct vertex){ {x1, y1}, {tx1 / tex_w, ty1 / tex_h}, COLOR_INIT };
va[4] = va[2];
va[5] = va[1];
#undef COLOR_INIT
}
-static void draw_osd_cb(void *pctx, struct sub_bitmaps *imgs)
+static int generate_verts(struct mpgl_osd_part *part, float matrix[3][2])
{
- struct mpgl_osd *ctx = pctx;
- GL *gl = ctx->gl;
+ int num_vertices = part->num_subparts * 6;
+ MP_TARRAY_GROW(part, part->vertices, num_vertices);
- struct mpgl_osd_part *part = mpgl_osd_generate(ctx, imgs);
- if (!part)
- return;
+ for (int n = 0; n < part->num_subparts; n++) {
+ struct sub_bitmap *b = &part->subparts[n];
+ struct pos pos = part->packer->result[n];
+ struct vertex *va = part->vertices;
- assert(part->format != SUBBITMAP_EMPTY);
+ // NOTE: the blend color is used with SUBBITMAP_LIBASS only, so it
+ // doesn't matter that we upload garbage for the other formats
+ uint32_t c = b->libass.color;
+ uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
+ (c >> 8) & 0xff, 255 - (c & 0xff) };
- if (!part->num_vertices) {
- part->vertices = talloc_realloc(part, part->vertices, struct vertex,
- part->packer->count * 6);
+ write_quad(&va[n * 6], matrix,
+ b->x, b->y, b->x + b->dw, b->y + b->dh,
+ pos.x, pos.y, pos.x + b->w, pos.y + b->h,
+ part->w, part->h, color);
+ }
- struct vertex *va = part->vertices;
+ return num_vertices;
+}
- for (int n = 0; n < part->packer->count; n++) {
- struct sub_bitmap *b = &imgs->parts[n];
- struct pos pos = part->packer->result[n];
-
- // NOTE: the blend color is used with SUBBITMAP_LIBASS only, so it
- // doesn't matter that we upload garbage for the other formats
- uint32_t c = b->libass.color;
- uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
- (c >> 8) & 0xff, 255 - (c & 0xff) };
-
- write_quad(&va[part->num_vertices],
- b->x, b->y, b->x + b->dw, b->y + b->dh,
- pos.x, pos.y, pos.x + b->w, pos.y + b->h,
- part->w, part->h, color);
- part->num_vertices += 6;
- }
- }
+static void draw_part(struct mpgl_osd *ctx, int index, float matrix[3][2])
+{
+ GL *gl = ctx->gl;
+ struct mpgl_osd_part *part = ctx->parts[index];
+
+ int num_vertices = generate_verts(part, matrix);
+ if (!num_vertices)
+ return;
+ gl->Enable(GL_BLEND);
gl->BindTexture(GL_TEXTURE_2D, part->texture);
const int *factors = &blend_factors[part->format][0];
gl->BlendFuncSeparate(factors[0], factors[1], factors[2], factors[3]);
- int program = ctx->programs[part->format];
-
- gl->UseProgram(program);
- bool set_offset = ctx->offset[0] != 0.0f || ctx->offset[1] != 0.0f;
- if (set_offset) {
- gl->Uniform3f(gl->GetUniformLocation(program, "translation"),
- ctx->offset[0], ctx->offset[1], 0);
- }
-
- gl_vao_draw_data(&ctx->vao, GL_TRIANGLES, part->vertices, part->num_vertices);
+ gl_vao_draw_data(&ctx->vao, GL_TRIANGLES, part->vertices, num_vertices);
- if (set_offset)
- gl->Uniform3f(gl->GetUniformLocation(program, "translation"), 0, 0, 0);
-
- gl->UseProgram(0);
gl->BindTexture(GL_TEXTURE_2D, 0);
+ gl->Disable(GL_BLEND);
}
// number of screen divisions per axis (x=0, y=1) for the current 3D mode
@@ -347,26 +370,51 @@ static void get_3d_side_by_side(int stereo_mode, int div[2])
}
}
-void mpgl_osd_draw(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
- int stereo_mode)
+void mpgl_osd_draw_part(struct mpgl_osd *ctx, int vp_w, int vp_h, int index)
{
- GL *gl = ctx->gl;
-
- gl->Enable(GL_BLEND);
-
int div[2];
- get_3d_side_by_side(stereo_mode, div);
+ get_3d_side_by_side(ctx->stereo_mode, div);
for (int x = 0; x < div[0]; x++) {
for (int y = 0; y < div[1]; y++) {
- struct mp_osd_res s_res = res;
- s_res.w /= div[0];
- s_res.h /= div[1];
- ctx->offset[0] = s_res.w * x;
- ctx->offset[1] = s_res.h * y;
- osd_draw(ctx->osd, s_res, pts, 0, ctx->formats, draw_osd_cb, ctx);
+ float matrix[3][2];
+
+ gl_matrix_ortho2d(matrix, 0, vp_w, 0, vp_h);
+
+ float a_x = ctx->display_size[0] * x;
+ float a_y = ctx->display_size[1] * y;
+ matrix[2][0] += a_x * matrix[0][0] + a_y * matrix[1][0];
+ matrix[2][1] += a_x * matrix[0][1] + a_y * matrix[1][1];
+
+ draw_part(ctx, index, matrix);
}
}
+}
- gl->Disable(GL_BLEND);
+enum sub_bitmap_format mpgl_osd_get_part_format(struct mpgl_osd *ctx, int index)
+{
+ assert(index >= 0 && index < MAX_OSD_PARTS);
+ return ctx->parts[index]->format;
+}
+
+struct gl_vao *mpgl_osd_get_vao(struct mpgl_osd *ctx)
+{
+ return &ctx->vao;
+}
+
+void mpgl_osd_generate(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
+ int stereo_mode)
+{
+ for (int n = 0; n < MAX_OSD_PARTS; n++)
+ ctx->parts[n]->num_subparts = 0;
+
+ int div[2];
+ get_3d_side_by_side(stereo_mode, div);
+
+ struct mp_osd_res s_res = res;
+ ctx->display_size[0] = s_res.w = s_res.w / div[0];
+ ctx->display_size[1] = s_res.h = s_res.h / div[1];
+
+ osd_draw(ctx->osd, s_res, pts, 0, ctx->formats, gen_osd_cb, ctx);
+ ctx->stereo_mode = stereo_mode;
}
diff --git a/video/out/gl_osd.h b/video/out/gl_osd.h
index 5c34911e22..0acd200ab4 100644
--- a/video/out/gl_osd.h
+++ b/video/out/gl_osd.h
@@ -7,38 +7,15 @@
#include "gl_utils.h"
#include "sub/osd.h"
-struct mpgl_osd_part {
- enum sub_bitmap_format format;
- int bitmap_id, bitmap_pos_id;
- GLuint texture;
- int w, h;
- GLuint buffer;
- int num_vertices;
- void *vertices;
- struct bitmap_packer *packer;
-};
-
-struct mpgl_osd {
- struct mp_log *log;
- struct osd_state *osd;
- GL *gl;
- bool use_pbo;
- bool scaled;
- struct mpgl_osd_part *parts[MAX_OSD_PARTS];
- const struct osd_fmt_entry *fmt_table;
- bool formats[SUBBITMAP_COUNT];
- struct gl_vao vao;
- GLuint *programs; // SUBBITMAP_COUNT elements
- // temporary
- float offset[2];
- void *scratch;
-};
-
-struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd,
- GLuint *programs);
+struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd);
void mpgl_osd_destroy(struct mpgl_osd *ctx);
-void mpgl_osd_draw(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
- int stereo_mode);
+void mpgl_osd_set_options(struct mpgl_osd *ctx, bool pbo);
+
+void mpgl_osd_generate(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
+ int stereo_mode);
+enum sub_bitmap_format mpgl_osd_get_part_format(struct mpgl_osd *ctx, int index);
+struct gl_vao *mpgl_osd_get_vao(struct mpgl_osd *ctx);
+void mpgl_osd_draw_part(struct mpgl_osd *ctx, int vp_w, int vp_h, int index);
#endif
diff --git a/video/out/gl_utils.c b/video/out/gl_utils.c
index 80ec840582..7881a6cf1f 100644
--- a/video/out/gl_utils.c
+++ b/video/out/gl_utils.c
@@ -25,6 +25,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
#include <assert.h>
#include "common/common.h"
@@ -290,14 +291,6 @@ void gl_vao_unbind(struct gl_vao *vao)
}
}
-void gl_vao_bind_attribs(struct gl_vao *vao, GLuint program)
-{
- GL *gl = vao->gl;
-
- for (int n = 0; vao->entries[n].name; n++)
- gl->BindAttribLocation(program, n, vao->entries[n].name);
-}
-
// Draw the vertex data (as described by the gl_vao_entry entries) in ptr
// to the screen. num is the number of vertexes. prim is usually GL_TRIANGLES.
// If ptr is NULL, then skip the upload, and use the data uploaded with the
@@ -320,24 +313,47 @@ void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num)
}
// Create a texture and a FBO using the texture as color attachments.
-// gl_target: GL_TEXTURE_2D
-// gl_filter: GL_LINEAR
// iformat: texture internal format
// Returns success.
bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
- GLenum gl_target, GLenum gl_filter, GLenum iformat)
+ GLenum iformat)
{
- bool res = true;
-
assert(!fbo->fbo);
assert(!fbo->texture);
+ return fbotex_change(fbo, gl, log, w, h, iformat, 0);
+}
+
+// Like fbotex_init(), except it can be called on an already initialized FBO;
+// and if the parameters are the same as the previous call, do not touch it.
+// flags can be 0, or a combination of FBOTEX_FUZZY_W and FBOTEX_FUZZY_H.
+// Enabling FUZZY for W or H means the w or h does not need to be exact.
+bool fbotex_change(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
+ GLenum iformat, int flags)
+{
+ bool res = true;
+
+ int cw = w, ch = h;
+
+ if ((flags & FBOTEX_FUZZY_W) && cw < fbo->tex_w)
+ cw = fbo->tex_w;
+ if ((flags & FBOTEX_FUZZY_H) && ch < fbo->tex_h)
+ ch = fbo->tex_h;
+
+ if (fbo->tex_w == cw && fbo->tex_h == ch && fbo->iformat == iformat)
+ return true;
+
+ if (flags & FBOTEX_FUZZY_W)
+ w = MP_ALIGN_UP(w, 256);
+ if (flags & FBOTEX_FUZZY_H)
+ h = MP_ALIGN_UP(h, 256);
+
+ GLenum filter = fbo->tex_filter;
*fbo = (struct fbotex) {
.gl = gl,
- .vp_w = w,
- .vp_h = h,
.tex_w = w,
.tex_h = h,
+ .iformat = iformat,
};
mp_verbose(log, "Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h);
@@ -347,19 +363,20 @@ bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
gl->GenFramebuffers(1, &fbo->fbo);
gl->GenTextures(1, &fbo->texture);
- gl->BindTexture(gl_target, fbo->texture);
- gl->TexImage2D(gl_target, 0, iformat, fbo->tex_w, fbo->tex_h, 0,
+ gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, iformat, fbo->tex_w, fbo->tex_h, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(gl_target, GL_TEXTURE_MIN_FILTER, gl_filter);
- gl->TexParameteri(gl_target, GL_TEXTURE_MAG_FILTER, gl_filter);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+
+ fbotex_set_filter(fbo, filter ? filter : GL_LINEAR);
glCheckError(gl, log, "after creating framebuffer texture");
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- gl_target, fbo->texture, 0);
+ GL_TEXTURE_2D, fbo->texture, 0);
GLenum err = gl->CheckFramebufferStatus(GL_FRAMEBUFFER);
if (err != GL_FRAMEBUFFER_COMPLETE) {
@@ -375,6 +392,19 @@ bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
return res;
}
+void fbotex_set_filter(struct fbotex *fbo, GLenum tex_filter)
+{
+ GL *gl = fbo->gl;
+
+ if (fbo->tex_filter != tex_filter && fbo->texture) {
+ gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex_filter);
+ gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex_filter);
+ gl->BindTexture(GL_TEXTURE_2D, 0);
+ }
+ fbo->tex_filter = tex_filter;
+}
+
void fbotex_uninit(struct fbotex *fbo)
{
GL *gl = fbo->gl;
@@ -386,14 +416,22 @@ void fbotex_uninit(struct fbotex *fbo)
}
}
-void gl_matrix_ortho2d(float m[3][3], float x0, float x1, float y0, float y1)
+// Standard parallel 2D projection, except y1 < y0 means that the coordinate
+// system is flipped, not the projection.
+void gl_matrix_ortho2d(float m[3][2], float x0, float x1, float y0, float y1)
{
- memset(m, 0, 9 * sizeof(float));
+ if (y1 < y0) {
+ float t = y0;
+ y0 = t - y1;
+ y1 = t;
+ }
+
m[0][0] = 2.0f / (x1 - x0);
+ m[0][1] = 0.0f;
+ m[1][0] = 0.0f;
m[1][1] = 2.0f / (y1 - y0);
m[2][0] = -(x1 + x0) / (x1 - x0);
m[2][1] = -(y1 + y0) / (y1 - y0);
- m[2][2] = 1.0f;
}
static void GLAPIENTRY gl_debug_cb(GLenum source, GLenum type, GLuint id,
@@ -422,3 +460,417 @@ void gl_set_debug_logger(GL *gl, struct mp_log *log)
}
}
}
+
+#define SC_ENTRIES 10
+#define SC_UNIFORM_ENTRIES 20
+
+enum uniform_type {
+ UT_invalid,
+ UT_i,
+ UT_f,
+ UT_m,
+};
+
+struct sc_uniform {
+ char *name;
+ enum uniform_type type;
+ const char *glsl_type;
+ int size;
+ GLint loc;
+ union {
+ GLfloat f[9];
+ GLint i[4];
+ } v;
+};
+
+struct sc_entry {
+ GLuint gl_shader;
+ // the following fields define the shader's contents
+ char *key; // vertex+frag shader (mangled)
+ struct gl_vao *vao;
+};
+
+struct gl_shader_cache {
+ GL *gl;
+ struct mp_log *log;
+
+ // this is modified during use (gl_sc_add() etc.)
+ char *text;
+ struct gl_vao *vao;
+
+ struct sc_entry entries[SC_ENTRIES];
+ int num_entries;
+
+ struct sc_uniform uniforms[SC_UNIFORM_ENTRIES];
+ int num_uniforms;
+};
+
+struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log)
+{
+ struct gl_shader_cache *sc = talloc_ptrtype(NULL, sc);
+ *sc = (struct gl_shader_cache){
+ .gl = gl,
+ .log = log,
+ .text = talloc_strdup(sc, ""),
+ };
+ return sc;
+}
+
+void gl_sc_reset(struct gl_shader_cache *sc)
+{
+ sc->text[0] = '\0';
+ for (int n = 0; n < sc->num_uniforms; n++)
+ talloc_free(sc->uniforms[n].name);
+ sc->num_uniforms = 0;
+}
+
+static void sc_flush_cache(struct gl_shader_cache *sc)
+{
+ for (int n = 0; n < sc->num_entries; n++) {
+ struct sc_entry *e = &sc->entries[n];
+ sc->gl->DeleteProgram(e->gl_shader);
+ talloc_free(e->key);
+ }
+ sc->num_entries = 0;
+}
+
+void gl_sc_destroy(struct gl_shader_cache *sc)
+{
+ gl_sc_reset(sc);
+ sc_flush_cache(sc);
+ talloc_free(sc);
+}
+
+void gl_sc_add(struct gl_shader_cache *sc, const char *text)
+{
+ sc->text = talloc_strdup_append(sc->text, text);
+}
+
+void gl_sc_addf(struct gl_shader_cache *sc, const char *textf, ...)
+{
+ va_list ap;
+ va_start(ap, textf);
+ ta_xvasprintf_append(&sc->text, textf, ap);
+ va_end(ap);
+}
+
+static struct sc_uniform *find_uniform(struct gl_shader_cache *sc,
+ const char *name)
+{
+ for (int n = 0; n < sc->num_uniforms; n++) {
+ if (strcmp(sc->uniforms[n].name, name) == 0)
+ return &sc->uniforms[n];
+ }
+ // not found -> add it
+ assert(sc->num_uniforms < SC_UNIFORM_ENTRIES); // just don't have too many
+ struct sc_uniform *new = &sc->uniforms[sc->num_uniforms++];
+ *new = (struct sc_uniform) { .loc = -1, .name = talloc_strdup(NULL, name) };
+ return new;
+}
+
+void gl_sc_uniform_sampler(struct gl_shader_cache *sc, char *name, GLenum target,
+ int unit)
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_i;
+ u->size = 1;
+ switch (target) {
+ case GL_TEXTURE_1D: u->glsl_type = "sampler1D"; break;
+ case GL_TEXTURE_2D: u->glsl_type = "sampler2D"; break;
+ case GL_TEXTURE_RECTANGLE: u->glsl_type = "sampler2DRect"; break;
+ case GL_TEXTURE_3D: u->glsl_type = "sampler3D"; break;
+ default: abort();
+ }
+ u->v.i[0] = unit;
+}
+
+void gl_sc_uniform_f(struct gl_shader_cache *sc, char *name, GLfloat f)
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_f;
+ u->size = 1;
+ u->glsl_type = "float";
+ u->v.f[0] = f;
+}
+
+void gl_sc_uniform_vec2(struct gl_shader_cache *sc, char *name, GLfloat f[2])
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_f;
+ u->size = 2;
+ u->glsl_type = "vec2";
+ u->v.f[0] = f[0];
+ u->v.f[1] = f[1];
+}
+
+void gl_sc_uniform_vec3(struct gl_shader_cache *sc, char *name, GLfloat f[3])
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_f;
+ u->size = 3;
+ u->glsl_type = "vec3";
+ u->v.f[0] = f[0];
+ u->v.f[1] = f[1];
+ u->v.f[2] = f[2];
+}
+
+static void transpose2x2(float r[2 * 2])
+{
+ MPSWAP(float, r[0+2*1], r[1+2*0]);
+}
+
+void gl_sc_uniform_mat2(struct gl_shader_cache *sc, char *name,
+ bool transpose, GLfloat *v)
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_m;
+ u->size = 2;
+ u->glsl_type = "mat2";
+ for (int n = 0; n < 4; n++)
+ u->v.f[n] = v[n];
+ if (transpose)
+ transpose2x2(&u->v.f[0]);
+}
+
+static void transpose3x3(float r[3 * 3])
+{
+ MPSWAP(float, r[0+3*1], r[1+3*0]);
+ MPSWAP(float, r[0+3*2], r[2+3*0]);
+ MPSWAP(float, r[1+3*2], r[2+3*1]);
+}
+
+void gl_sc_uniform_mat3(struct gl_shader_cache *sc, char *name,
+ bool transpose, GLfloat *v)
+{
+ struct sc_uniform *u = find_uniform(sc, name);
+ u->type = UT_m;
+ u->size = 3;
+ u->glsl_type = "mat3";
+ for (int n = 0; n < 9; n++)
+ u->v.f[n] = v[n];
+ if (transpose)
+ transpose3x3(&u->v.f[0]);
+}
+
+// This will call glBindAttribLocation() on the shader before it's linked
+// (OpenGL requires this to happen before linking). Basically, it associates
+// the input variable names with the fields in the vao.
+// The vertex shader is setup such that the elements are available as fragment
+// shader variables using the names in the vao entries, which "position" being
+// set to gl_Position.