summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile8
-rwxr-xr-xconfigure21
-rw-r--r--libvo/filter_kernels.c279
-rw-r--r--libvo/filter_kernels.h45
-rw-r--r--libvo/gl_common.c16
-rw-r--r--libvo/gl_common.h4
-rw-r--r--libvo/video_out.c4
-rw-r--r--libvo/vo_gl3.c2418
-rw-r--r--libvo/vo_gl3_shaders.glsl316
10 files changed, 3112 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index e90239351c..8c4cd6181a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,3 +17,5 @@
/TAGS
/locale
/po
+
+libvo/vo_gl3_shaders.h
diff --git a/Makefile b/Makefile
index f6b869e8a6..ae2267f5ec 100644
--- a/Makefile
+++ b/Makefile
@@ -451,7 +451,7 @@ SRCS_MPLAYER-$(ESD) += libao2/ao_esd.c
SRCS_MPLAYER-$(FBDEV) += libvo/vo_fbdev.c libvo/vo_fbdev2.c
SRCS_MPLAYER-$(GGI) += libvo/vo_ggi.c
SRCS_MPLAYER-$(GIF) += libvo/vo_gif89a.c
-SRCS_MPLAYER-$(GL) += libvo/gl_common.c libvo/vo_gl.c \
+SRCS_MPLAYER-$(GL) += libvo/gl_common.c libvo/vo_gl.c libvo/vo_gl3.c \
pnm_loader.c
SRCS_MPLAYER-$(GL_COCOA) += libvo/cocoa_common.m
SRCS_MPLAYER-$(GL_SDL) += libvo/sdl_common.c
@@ -510,6 +510,7 @@ SRCS_MPLAYER = command.c \
libao2/audio_out.c \
libvo/aspect.c \
libvo/csputils.c \
+ libvo/filter_kernels.c \
libvo/geometry.c \
libvo/old_vo_wrapper.c \
libvo/spuenc.c \
@@ -605,6 +606,11 @@ codec-cfg$(EXESUF): codec-cfg.c codec-cfg.h
codecs.conf.h: codec-cfg$(EXESUF) etc/codecs.conf
./$^ > $@
+libvo/vo_gl3_shaders.h: libvo/vo_gl3_shaders.glsl
+ python ./bin_to_header.py $^ $@
+
+libvo/vo_gl3.c: libvo/vo_gl3_shaders.h
+
# ./configure must be rerun if it changed
config.mak: configure
@echo "############################################################"
diff --git a/configure b/configure
index 53a9ead726..3731ba9458 100755
--- a/configure
+++ b/configure
@@ -338,6 +338,7 @@ Optional features:
--enable-smb enable Samba (SMB) input [autodetect]
--enable-live enable LIVE555 Streaming Media [disable]
--enable-nemesi enable Nemesi Streaming Media [autodetect]
+ --enable-lcms2 enable LCMS2 support [autodetect]
--disable-vcd disable VCD support [autodetect]
--disable-bluray disable Blu-ray support [autodetect]
--disable-dvdnav disable libdvdnav [autodetect]
@@ -637,6 +638,7 @@ _xanim=auto
_real=auto
_live=no
_nemesi=auto
+_lcms2=auto
_native_rtsp=yes
_xinerama=auto
_mga=auto
@@ -990,6 +992,8 @@ for ac_option do
--disable-live) _live=no ;;
--enable-nemesi) _nemesi=yes ;;
--disable-nemesi) _nemesi=no ;;
+ --enable-lcms2) _lcms2=yes ;;
+ --disable-lcms2) _lcms2=no ;;
--enable-xinerama) _xinerama=yes ;;
--disable-xinerama) _xinerama=no ;;
--enable-mga) _mga=yes ;;
@@ -5726,6 +5730,20 @@ else
fi
echores "$_qtx"
+echocheck "LCMS2 support"
+if test "$_lcms2" = auto ; then
+ _lcms2=no
+ if pkg_config_add lcms2 ; then
+ _lcms2=yes
+ fi
+fi
+if test "$_lcms2" = yes; then
+ def_lcms2="#define CONFIG_LCMS2 1"
+else
+ def_lcms2="#undef CONFIG_LCMS2"
+fi
+echores "$_lcms2"
+
echocheck "Nemesi Streaming Media libraries"
if test "$_nemesi" = auto && test "$networking" = yes ; then
_nemesi=no
@@ -6518,6 +6536,7 @@ LIBDV = $_libdv
LIBDVDCSS_INTERNAL = $_libdvdcss_internal
LIBMAD = $_mad
LIBNEMESI = $_nemesi
+LCMS2 = $_lcms2
LIBNUT = $_libnut
LIBPOSTPROC = $libpostproc
LIBSMBCLIENT = $_smb
@@ -6874,6 +6893,8 @@ $def_smb
$def_socklen_t
$def_vstream
+$def_lcms2
+
/* libvo options */
$def_3dfx
diff --git a/libvo/filter_kernels.c b/libvo/filter_kernels.c
new file mode 100644
index 0000000000..2c2f56ee51
--- /dev/null
+++ b/libvo/filter_kernels.c
@@ -0,0 +1,279 @@
+/*
+ * This file is part of mplayer2.
+ *
+ * Most code for computing the weights is taken from Anti-Grain Geometry (AGG)
+ * (licensed under GPL 2 or later), with modifications.
+ * Copyright (C) 2002-2006 Maxim Shemanarev
+ * http://vector-agg.cvs.sourceforge.net/viewvc/vector-agg/agg-2.5/include/agg_image_filters.h?view=markup
+ *
+ * Also see glumpy (BSD licensed), contains the same code in Python:
+ * http://code.google.com/p/glumpy/source/browse/glumpy/image/filter.py
+ *
+ * Also see: Paul Heckbert's "zoom"
+ *
+ * Also see XBMC: ConvolutionKernels.cpp etc.
+ *
+ * mplayer2 is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mplayer2; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stddef.h>
+#include <string.h>
+#include <math.h>
+#include <assert.h>
+
+#include "filter_kernels.h"
+
+// NOTE: all filters are separable, symmetric, and are intended for use with
+// a lookup table/texture.
+
+const struct filter_kernel *mp_find_filter_kernel(const char *name)
+{
+ for (const struct filter_kernel *k = mp_filter_kernels; k->name; k++) {
+ if (strcmp(k->name, name) == 0)
+ return k;
+ }
+ return NULL;
+}
+
+// sizes = sorted list of available filter sizes, terminated with size 0
+// inv_scale = source_size / dest_size
+bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
+ double inv_scale)
+{
+ // only downscaling requires widening the filter
+ filter->inv_scale = inv_scale >= 1.0 ? inv_scale : 1.0;
+ double support = filter->radius * filter->inv_scale;
+ int size = ceil(2.0 * support);
+ // round up to smallest available size that's still large enough
+ if (size < sizes[0])
+ size = sizes[0];
+ const int *cursize = sizes;
+ while (size > *cursize && *cursize)
+ cursize++;
+ if (*cursize) {
+ filter->size = *cursize;
+ return true;
+ } else {
+ // The filter doesn't fit - instead of failing completely, use the
+ // largest filter available. This is incorrect, but better than refusing
+ // to do anything.
+ filter->size = cursize[-1];
+ filter->inv_scale = filter->size / 2.0 / filter->radius;
+ return false;
+ }
+}
+
+// Calculate the 1D filtering kernel for N sample points.
+// N = number of samples, which is filter->size
+// The weights will be stored in out_w[0] to out_w[N - 1]
+// f = x0 - abs(x0), subpixel position in the range [0,1) or [0,1].
+void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w)
+{
+ assert(filter->size > 0);
+ double sum = 0;
+ for (int n = 0; n < filter->size; n++) {
+ double x = f - (n - filter->size / 2 + 1);
+ double w = filter->weight(filter, fabs(x) / filter->inv_scale);
+ out_w[n] = w;
+ sum += w;
+ }
+ //normalize
+ for (int n = 0; n < filter->size; n++)
+ out_w[n] /= sum;
+}
+
+// Fill the given array with weights for the range [0.0, 1.0]. The array is
+// interpreted as rectangular array of count * filter->size items.
+void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array)
+{
+ for (int n = 0; n < count; n++) {
+ mp_compute_weights(filter, n / (double)(count - 1),
+ out_array + filter->size * n);
+ }
+}
+
+typedef struct filter_kernel kernel;
+
+static double bilinear(kernel *k, double x)
+{
+ return 1.0 - x;
+}
+
+static double hanning(kernel *k, double x)
+{
+ return 0.5 + 0.5 * cos(M_PI * x);
+}
+
+static double hamming(kernel *k, double x)
+{
+ return 0.54 + 0.46 * cos(M_PI * x);
+}
+
+static double hermite(kernel *k, double x)
+{
+ return (2.0 * x - 3.0) * x * x + 1.0;
+}
+
+static double quadric(kernel *k, double x)
+{
+ // NOTE: glumpy uses 0.75, AGG uses 0.5
+ if (x < 0.5)
+ return 0.75 - x * x;
+ if (x < 1.5)
+ return 0.5 * (x - 1.5) * (x - 1.5);
+ return 0;
+}
+
+static double bc_pow3(double x)
+{
+ return (x <= 0) ? 0 : x * x * x;
+}
+
+static double bicubic(kernel *k, double x)
+{
+ return (1.0/6.0) * ( bc_pow3(x + 2)
+ - 4 * bc_pow3(x + 1)
+ + 6 * bc_pow3(x)
+ - 4 * bc_pow3(x - 1));
+}
+
+static double bessel_i0(double epsilon, double x)
+{
+ double sum = 1;
+ double y = x * x / 4;
+ double t = y;
+ for (int i = 2; t > epsilon; i++) {
+ sum += t;
+ t *= y / (i * i);
+ }
+ return sum;
+}
+
+static double kaiser(kernel *k, double x)
+{
+ double a = k->params[0];
+ double b = k->params[1];
+ double epsilon = 1e-12;
+ double i0a = 1 / bessel_i0(epsilon, b);
+ return bessel_i0(epsilon, a * sqrt(1 - x * x)) * i0a;
+}
+
+static double catmull_rom(kernel *k, double x)
+{
+ if (x < 1.0)
+ return 0.5 * (2.0 + x * x * (-5.0 + x * 3.0));
+ if (x < 2.0)
+ return 0.5 * (4.0 + x * (-8.0 + x * (5.0 - x)));
+ return 0;
+}
+
+// Mitchell-Netravali
+static double mitchell(kernel *k, double x)
+{
+ double b = k->params[0];
+ double c = k->params[1];
+ double
+ p0 = (6.0 - 2.0 * b) / 6.0,
+ p2 = (-18.0 + 12.0 * b + 6.0 * c) / 6.0,
+ p3 = (12.0 - 9.0 * b - 6.0 * c) / 6.0,
+ q0 = (8.0 * b + 24.0 * c) / 6.0,
+ q1 = (-12.0 * b - 48.0 * c) / 6.0,
+ q2 = (6.0 * b + 30.0 * c) / 6.0,
+ q3 = (-b - 6.0 * c) / 6.0;
+ if (x < 1.0)
+ return p0 + x * x * (p2 + x * p3);
+ if (x < 2.0)
+ return q0 + x * (q1 + x * (q2 + x * q3));
+ return 0;
+}
+
+static double spline16(kernel *k, double x)
+{
+ if (x < 1.0)
+ return ((x - 9.0/5.0 ) * x - 1.0/5.0 ) * x + 1.0;
+ return ((-1.0/3.0 * (x-1) + 4.0/5.0) * (x-1) - 7.0/15.0 ) * (x-1);
+}
+
+static double spline36(kernel *k, double x)
+{
+ if(x < 1.0)
+ return ((13.0/11.0 * x - 453.0/209.0) * x - 3.0/209.0) * x + 1.0;
+ if(x < 2.0)
+ return ((-6.0/11.0 * (x - 1) + 270.0/209.0) * (x - 1) - 156.0/209.0)
+ * (x - 1);
+ return ((1.0/11.0 * (x - 2) - 45.0/209.0) * (x - 2) + 26.0/209.0)
+ * (x - 2);
+}
+
+static double gaussian(kernel *k, double x)
+{
+ return exp(-2.0 * x * x) * sqrt(2.0 / M_PI);
+}
+
+static double sinc(kernel *k, double x)
+{
+ if (x == 0.0)
+ return 1.0;
+ double pix = M_PI * x;
+ return sin(pix) / pix;
+}
+
+static double lanczos(kernel *k, double x)
+{
+ double radius = k->size / 2;
+ if (x < -radius || x > radius)
+ return 0;
+ if (x == 0)
+ return 1;
+ double pix = M_PI * x;
+ return radius * sin(pix) * sin(pix / radius) / (pix * pix);
+}
+
+static double blackman(kernel *k, double x)
+{
+ double radius = k->size / 2;
+ if (x == 0.0)
+ return 1.0;
+ if (x > radius)
+ return 0.0;
+ x *= M_PI;
+ double xr = x / radius;
+ return (sin(x) / x) * (0.42 + 0.5 * cos(xr) + 0.08 * cos(2 * xr));
+}
+
+const struct filter_kernel mp_filter_kernels[] = {
+ {"bilinear_slow", 1, bilinear},
+ {"hanning", 1, hanning},
+ {"hamming", 1, hamming},
+ {"hermite", 1, hermite},
+ {"quadric", 1.5, quadric},
+ {"bicubic", 2, bicubic},
+ {"kaiser", 1, kaiser, .params = {6.33, 6.33} },
+ {"catmull_rom", 2, catmull_rom},
+ {"mitchell", 2, mitchell, .params = {1.0/3.0, 1.0/3.0} },
+ {"spline16", 2, spline16},
+ {"spline36", 3, spline36},
+ {"gaussian", 2, gaussian},
+ {"sinc2", 2, sinc},
+ {"sinc3", 3, sinc},
+ {"sinc4", 4, sinc},
+ {"lanczos2", 2, lanczos},
+ {"lanczos3", 3, lanczos},
+ {"lanczos4", 4, lanczos},
+ {"blackman2", 2, blackman},
+ {"blackman3", 3, blackman},
+ {"blackman4", 4, blackman},
+ {0}
+};
diff --git a/libvo/filter_kernels.h b/libvo/filter_kernels.h
new file mode 100644
index 0000000000..46a392c40a
--- /dev/null
+++ b/libvo/filter_kernels.h
@@ -0,0 +1,45 @@
+/*
+ * This file is part of mplayer2.
+ *
+ * mplayer2 is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mplayer2 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mplayer2; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPLAYER_FILTER_KERNELS_H
+#define MPLAYER_FILTER_KERNELS_H
+
+#include <stdbool.h>
+
+struct filter_kernel {
+ const char *name;
+ double radius;
+ double (*weight)(struct filter_kernel *kernel, double x);
+
+ // The filter params can be changed at runtime. Only used by some filters.
+ float params[2];
+ // The following values are set by mp_init_filter() at runtime.
+ // Number of coefficients; equals the rounded up radius multiplied with 2.
+ int size;
+ double inv_scale;
+};
+
+extern const struct filter_kernel mp_filter_kernels[];
+
+const struct filter_kernel *mp_find_filter_kernel(const char *name);
+bool mp_init_filter(struct filter_kernel *filter, const int *sizes,
+ double scale);
+void mp_compute_weights(struct filter_kernel *filter, double f, float *out_w);
+void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array);
+
+#endif /* MPLAYER_FILTER_KERNELS_H */
diff --git a/libvo/gl_common.c b/libvo/gl_common.c
index 230e5dc2bd..b867af81b0 100644
--- a/libvo/gl_common.c
+++ b/libvo/gl_common.c
@@ -2587,3 +2587,19 @@ void uninit_mpglcontext(MPGLContext *ctx)
}
talloc_free(ctx);
}
+
+void mp_log_source(int mod, 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(mod, lev, "[%3d] %.*s\n", line, (int)(end - src), src);
+ line++;
+ src = next;
+ }
+}
diff --git a/libvo/gl_common.h b/libvo/gl_common.h
index 2f6b4b5083..08e21f28b4 100644
--- a/libvo/gl_common.h
+++ b/libvo/gl_common.h
@@ -431,6 +431,10 @@ void uninit_mpglcontext(MPGLContext *ctx);
int create_mpglcontext(struct MPGLContext *ctx, int gl_flags, int gl_version,
uint32_t d_width, uint32_t d_height, uint32_t flags);
+// print a multi line string with line numbers (e.g. for shader sources)
+// mod, lev: module and log level, as in mp_msg()
+void mp_log_source(int mod, int lev, const char *src);
+
//function pointers loaded from the OpenGL library
struct GL {
void (GLAPIENTRY *Begin)(GLenum);
diff --git a/libvo/video_out.c b/libvo/video_out.c
index bbab3f0907..094d5b1a12 100644
--- a/libvo/video_out.c
+++ b/libvo/video_out.c
@@ -84,6 +84,7 @@ extern struct vo_driver video_out_vdpau;
extern struct vo_driver video_out_xv;
extern struct vo_driver video_out_gl_nosw;
extern struct vo_driver video_out_gl;
+extern struct vo_driver video_out_gl3;
extern struct vo_driver video_out_dga;
extern struct vo_driver video_out_sdl;
extern struct vo_driver video_out_3dfx;
@@ -169,6 +170,9 @@ const struct vo_driver *video_out_drivers[] =
#ifdef CONFIG_XV
&video_out_xv,
#endif
+#ifdef CONFIG_GL
+ &video_out_gl3,
+#endif
#ifdef CONFIG_X11
#ifdef CONFIG_GL
&video_out_gl_nosw,
diff --git a/libvo/vo_gl3.c b/libvo/vo_gl3.c
new file mode 100644
index 0000000000..1947839816
--- /dev/null
+++ b/libvo/vo_gl3.c
@@ -0,0 +1,2418 @@
+/*
+ * This file is part of MPlayer.
+ *
+ * MPlayer is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * MPlayer is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with MPlayer; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * You can alternatively redistribute this file and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <stdbool.h>
+#include <assert.h>
+#include "config.h"
+
+#include <libavutil/avutil.h>
+
+#ifdef CONFIG_LCMS2
+#include <lcms2.h>
+#include "stream/stream.h"
+#endif
+
+#include "talloc.h"
+#include "bstr.h"
+#include "mp_msg.h"
+#include "subopt-helper.h"
+#include "video_out.h"
+#include "libmpcodecs/vfcap.h"
+#include "libmpcodecs/mp_image.h"
+#include "geometry.h"
+#include "osd.h"
+#include "sub/font_load.h"
+#include "sub/sub.h"
+#include "eosd_packer.h"
+
+#include "gl_common.h"
+#include "filter_kernels.h"
+#include "aspect.h"
+#include "fastmemcpy.h"
+#include "sub/ass_mp.h"
+
+// Generated from libvo/vo_gl3_shaders.glsl
+#include "libvo/vo_gl3_shaders.h"
+
+// How many parts the OSD may consist of at most.
+#define MAX_OSD_PARTS 20
+
+// Pixel width of 1D lookup textures.
+#define LOOKUP_TEXTURE_SIZE 256
+
+// Texture units 0-2 are used by the video, with unit 0 for free use.
+// Units 3-4 are used for scaler LUTs.
+#define TEXUNIT_SCALERS 3
+#define TEXUNIT_3DLUT 5
+#define TEXUNIT_DITHER 6
+
+// lscale/cscale arguments that map directly to shader filter routines.
+// Note that the convolution filters are not included in this list.
+static const char *fixed_scale_filters[] = {
+ "bilinear",
+ "bicubic_fast",
+ "sharpen3",
+ "sharpen5",
+ NULL
+};
+
+struct lut_tex_format {
+ int pixels;
+ GLint internal_format;
+ GLenum format;
+};
+
+// Indexed with filter_kernel->size.
+// This must match the weightsN functions in the shader.
+// Each entry uses (size+3)/4 pixels per LUT entry, and size/pixels components
+// per pixel.
+struct lut_tex_format lut_tex_formats[] = {
+ [2] = {1, GL_RG16F, GL_RG},
+ [4] = {1, GL_RGBA16F, GL_RGBA},
+ [6] = {2, GL_RGB16F, GL_RGB},
+ [8] = {2, GL_RGBA16F, GL_RGBA},
+ [12] = {3, GL_RGBA16F, GL_RGBA},
+ [16] = {4, GL_RGBA16F, GL_RGBA},
+};
+
+// must be sorted, and terminated with 0
+static const int filter_sizes[] = {2, 4, 6, 8, 12, 16, 0};
+
+struct vertex {
+ float position[2];
+ uint8_t color[4];
+ float texcoord[2];
+};
+
+#define VERTEX_ATTRIB_POSITION 0
+#define VERTEX_ATTRIB_COLOR 1
+#define VERTEX_ATTRIB_TEXCOORD 2
+
+// 2 triangles primitives per quad = 6 vertices per quad
+// (GL_QUAD is deprecated, strips can't be used with EOSD image lists)
+#define VERTICES_PER_QUAD 6
+
+struct texplane {
+ int shift_x, shift_y;
+ GLuint gl_texture;
+ int gl_buffer;
+ int buffer_size;
+ void *buffer_ptr;
+};
+
+struct scaler {
+ int index;
+ const char *name;
+ float params[2];
+ struct filter_kernel *kernel;
+ GLuint gl_lut;
+ const char *lut_name;
+
+ // kernel points here
+ struct filter_kernel kernel_storage;
+};
+
+struct fbotex {
+ GLuint fbo;
+ GLuint texture;
+ int tex_w, tex_h; // size of .texture
+ int vp_w, vp_h; // viewport of fbo / used part of the texture
+};
+
+struct gl_priv {
+ struct vo *vo;
+ MPGLContext *glctx;
+ GL *gl;
+ const char *shader_version;
+
+ int use_indirect;
+ int use_gamma;
+ int use_srgb;
+ int use_scale_sep;
+ int use_fancy_downscaling;
+ int use_lut_3d;
+ int use_npot;
+ int use_pbo;
+ int use_glFinish;
+ int use_gl_debug;
+ int use_gl2;
+
+ int dither_depth;
+ int swap_interval;
+ GLint fbo_format;
+ int stereo_mode;
+ int osd_color;
+
+ GLuint vertex_buffer;
+ GLuint vao;
+
+ GLuint osd_program, eosd_program;
+ GLuint indirect_program, scale_sep_program, final_program;
+
+ GLuint osd_textures[MAX_OSD_PARTS];
+ int osd_textures_count;
+ struct vertex osd_va[MAX_OSD_PARTS * VERTICES_PER_QUAD];
+
+ GLuint eosd_texture;
+ int eosd_texture_width, eosd_texture_height;
+ GLuint eosd_buffer;
+ struct vertex *eosd_va;
+ struct eosd_packer *eosd;
+
+ GLuint lut_3d_texture;
+ int lut_3d_w, lut_3d_h, lut_3d_d;
+ void *lut_3d_data;
+
+ GLuint dither_texture;
+ float dither_quantization;
+ float dither_multiply;
+
+ uint32_t image_width;
+ uint32_t image_height;
+ uint32_t image_format;
+ int texture_width;
+ int texture_height;
+
+ bool is_yuv;
+ bool is_linear_rgb;
+
+ // per pixel (full pixel when packed, each component when planar)
+ int plane_bytes;
+ int plane_bits;
+ int component_bits;
+
+ GLint gl_internal_format;
+ GLenum gl_format;
+ GLenum gl_type;
+
+ int plane_count;
+ struct texplane planes[3];
+
+ struct fbotex indirect_fbo; // RGB target
+ struct fbotex scale_sep_fbo; // first pass when doing 2 pass scaling
+
+ // state for luma (0) and chroma (1) scalers
+ struct scaler scalers[2];
+ // luma scaler parameters (the same are used for chroma)
+ float scaler_params[2];
+
+ struct mp_csp_details colorspace;
+ struct mp_csp_equalizer video_eq;
+
+ int mpi_flipped;
+ int vo_flipped;
+
+ struct vo_rect src_rect; // displayed part of the source video
+ struct vo_rect dst_rect; // video rectangle on output window
+ int border_x, border_y; // OSD borders
+ int vp_x, vp_y, vp_w, vp_h; // GL viewport
+};
+
+struct fmt_entry {
+ int mp_format;
+ GLint internal_format;
+ GLenum format;
+ int component_bits;
+ GLenum type;
+};
+
+static const struct fmt_entry mp_to_gl_formats[] = {
+ {IMGFMT_RGB48NE, GL_RGB16, GL_RGB, 16, GL_UNSIGNED_SHORT},
+ {IMGFMT_RGB24, GL_RGB, GL_RGB, 8, GL_UNSIGNED_BYTE},
+ {IMGFMT_RGBA, GL_RGBA, GL_RGBA, 8, GL_UNSIGNED_BYTE},
+ {IMGFMT_RGB15, GL_RGBA, GL_RGBA, 5, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ {IMGFMT_RGB16, GL_RGB, GL_RGB, 6, GL_UNSIGNED_SHORT_5_6_5_REV},
+ {IMGFMT_BGR15, GL_RGBA, GL_BGRA, 5, GL_UNSIGNED_SHORT_1_5_5_5_REV},
+ {IMGFMT_BGR16, GL_RGB, GL_RGB, 6, GL_UNSIGNED_SHORT_5_6_5},
+ {IMGFMT_BGR24, GL_RGB, GL_BGR, 8, GL_UNSIGNED_BYTE},
+ {IMGFMT_BGRA, GL_RGBA, GL_BGRA, 8, GL_UNSIGNED_BYTE},
+ {0},
+};
+
+
+static const char help_text[];
+
+static void uninit_rendering(struct gl_priv *p);
+static void delete_shaders(struct gl_priv *p);
+
+
+static void default_tex_params(struct GL *gl, GLenum target, GLint filter)
+{
+ gl->TexParameteri(target, GL_TEXTURE_MIN_FILTER, filter);
+ gl->TexParameteri(target, GL_TEXTURE_MAG_FILTER, filter);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ gl->TexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+static void debug_check_gl(struct gl_priv *p, const char *msg)
+{
+ if (p->use_gl_debug)
+ glCheckError(p->gl, msg);
+}
+
+static void tex_size(struct gl_priv *p, int w, int h, int *texw, int *texh)
+{
+ if (p->use_npot) {
+ *texw = w;
+ *texh = h;
+ } else {
+ *texw = 32;
+ while (*texw < w)
+ *texw *= 2;
+ *texh = 32;
+ while (*texh < h)
+ *texh *= 2;
+ }
+}
+
+static void draw_triangles(struct gl_priv *p, struct vertex *vb, int vert_count)
+{
+ GL *gl = p->gl;
+
+ assert(vert_count % 3 == 0);
+
+ gl->BindBuffer(GL_ARRAY_BUFFER, p->vertex_buffer);
+ gl->BufferData(GL_ARRAY_BUFFER, vert_count * sizeof(struct vertex), vb,
+ GL_DYNAMIC_DRAW);
+ gl->BindBuffer(GL_ARRAY_BUFFER, 0);
+
+ gl->BindVertexArray(p->vao);
+ gl->DrawArrays(GL_TRIANGLES, 0, vert_count);
+ gl->BindVertexArray(0);
+
+ debug_check_gl(p, "after rendering");
+}
+
+// Write a textured quad to a vertex array.
+// va = destination vertex array, VERTICES_PER_QUAD entries will be overwritten
+// x0, y0, x1, y1 = destination coordinates of the quad
+// tx0, ty0, tx1, ty1 = source texture coordinates (usually in pixels)
+// texture_w, texture_h = size of the texture, or an inverse factor
+// color = optional color for all vertices, NULL for opaque white
+// flip = flip vertically
+static void write_quad(struct vertex *va,
+ float x0, float y0, float x1, float y1,
+ float tx0, float ty0, float tx1, float ty1,
+ float texture_w, float texture_h,
+ const uint8_t color[4], bool flip)
+{
+ static const uint8_t white[4] = { 255, 255, 255, 255 };
+
+ if (!color)
+ color = white;
+
+ tx0 /= texture_w;
+ ty0 /= texture_h;
+ tx1 /= texture_w;
+ ty1 /= texture_h;
+
+ if (flip) {
+ float tmp = ty0;
+ ty0 = ty1;
+ ty1 = tmp;
+ }
+
+#define COLOR_INIT {color[0], color[1], color[2], color[3]}
+ va[0] = (struct vertex) { {x0, y0}, COLOR_INIT, {tx0, ty0} };
+ va[1] = (struct vertex) { {x0, y1}, COLOR_INIT, {tx0, ty1} };
+ va[2] = (struct vertex) { {x1, y0}, COLOR_INIT, {tx1, ty0} };
+ va[3] = (struct vertex) { {x1, y1}, COLOR_INIT, {tx1, ty1} };
+ va[4] = va[2];
+ va[5] = va[1];
+#undef COLOR_INIT
+}
+
+static void fbotex_init(struct gl_priv *p, struct fbotex *fbo, int w, int h)
+{
+ GL *gl = p->gl;
+
+ assert(!fbo->fbo);
+ assert(!fbo->texture);
+
+ tex_size(p, w, h, &fbo->tex_w, &fbo->tex_h);
+
+ fbo->vp_w = w;
+ fbo->vp_h = h;
+
+ mp_msg(MSGT_VO, MSGL_V, "[gl] Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h);
+
+ gl->GenFramebuffers(1, &fbo->fbo);
+ gl->GenTextures(1, &fbo->texture);
+ gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, p->fbo_format, fbo->tex_w, fbo->tex_h, 0,
+ GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ default_tex_params(gl, GL_TEXTURE_2D, GL_LINEAR);
+ gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
+ gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+ GL_TEXTURE_2D, fbo->texture, 0);
+
+ if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER)
+ != GL_FRAMEBUFFER_COMPLETE)
+ {
+ mp_msg(MSGT_VO, MSGL_ERR, "[gl] Error: framebuffer completeness "
+ "check failed!\n");
+ }
+
+ gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ debug_check_gl(p, "after creating framebuffer & associated texture");
+}
+
+static void fbotex_uninit(struct gl_priv *p, struct fbotex *fbo)
+{
+ GL *gl = p->gl;
+
+ gl->DeleteFramebuffers(1, &fbo->fbo);
+ gl->DeleteTextures(1, &fbo->texture);
+ *fbo = (struct fbotex) {0};
+}
+
+static void matrix_ortho2d(float m[3][3], float x0, float x1,
+ float y0, float y1)
+{
+ memset(m, 0, 9 * sizeof(float));
+ m[0][0] = 2.0f / (x1 - x0);
+ 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 update_uniforms(struct gl_priv *p, GLuint program)
+{
+ GL *gl = p->gl;
+ GLint loc;
+
+ if (program == 0)
+ return;
+
+ gl->UseProgram(program);
+
+ struct mp_csp_params cparams = {
+ .colorspace = p->colorspace,
+ .input_bits = p->plane_bits,
+ .texture_bits = (p->plane_bits + 7) & ~7,
+ };
+ mp_csp_copy_equalizer_values(&cparams, &p->video_eq);
+
+ loc = gl->GetUniformLocation(program, "transform");
+ if (loc >= 0) {
+ float matrix[3][3];
+ matrix_ortho2d(matrix, 0, p->vp_w, p->vp_h, 0);
+ gl->UniformMatrix3fv(loc, 1, GL_FALSE, &matrix[0][0]);
+ }
+
+ loc = gl->GetUniformLocation(program, "colormatrix");
+ if (loc >= 0) {
+ float yuv2rgb[3][4] = {{0}};
+ if (p->is_yuv)
+ mp_get_yuv2rgb_coeffs(&cparams, yuv2rgb);
+ gl->UniformMatrix4x3fv(loc, 1, GL_TRUE, &yuv2rgb[0][0]);
+ }
+
+ gl->Uniform3f(gl->GetUniformLocation(program, "inv_gamma"),
+ 1.0 / cparams.rgamma,
+ 1.0 / cparams.ggamma,
+ 1.0 / cparams.bgamma);
+
+ gl->Uniform1i(gl->GetUniformLocation(program, "texture1"), 0);
+ gl->Uniform1i(gl->GetUniformLocation(program, "texture2"), 1);
+ gl->Uniform1i(gl->GetUniformLocation(program, "texture3"), 2);
+
+ gl->Uniform1i(gl->GetUniformLocation(program, "lut_3d"), TEXUNIT_3DLUT);
+
+ for (int n = 0; n < 2; n++) {
+ const char *lut = p->scalers[n].lut_name;
+ if (lut)
+ gl->Uniform1i(gl->GetUniformLocation(program, lut),
+ TEXUNIT_SCALERS + n);
+ }
+
+ gl->Uniform1i(gl->GetUniformLocation(program, "dither"), TEXUNIT_DITHER);
+ gl->Uniform1f(gl->GetUniformLocation(program, "dither_quantization"),
+ p->dither_quantization);
+ gl->Uniform1f(gl->GetUniformLocation(program, "dither_multiply"),
+ p->dither_multiply);
+
+ float sparam1 = p->scaler_params[0];
+ gl->Uniform1f(gl->GetUniformLocation(program, "filter_param1"),
+ isnan(sparam1) ? 0.5f : sparam1);
+
+ gl->UseProgram(0);
+
+ debug_check_gl(p, "update_uniforms()");
+}
+
+static void update_all_uniforms(struct gl_priv *p)
+{
+ update_uniforms(p, p->osd_program);
+ update_uniforms(p, p->eosd_program);
+ update_uniforms(p, p->indirect_program);
+ update_uniforms(p, p->scale_sep_program);
+ update_uniforms(p, p->final_program);
+}
+
+#define SECTION_HEADER "#!section "
+
+static char *get_section(void *talloc_ctx, struct bstr source,
+ const char *section)
+{
+ char *res = talloc_strdup(talloc_ctx, "");
+ bool copy = false;
+ while (source.len) {
+ struct bstr line = bstr_getline(source, &source);
+ if (bstr_eatstart(&line, bstr(SECTION_HEADER))) {
+ copy = bstrcmp0(line, section) == 0;
+ } else if (copy) {
+ res = talloc_asprintf_append_buffer(res, "%.*s\n", BSTR_P(line));
+ }
+ }
+ return res;
+}
+
+static char *t_concat(void *talloc_ctx, const char *s1, const char *s2)
+{
+ return talloc_asprintf(talloc_ctx, "%s%s", s1, s2);
+}
+
+static GLuint create_shader(GL *gl, GLenum type, const char *header,
+ const char *source)
+{
+ void *tmp = talloc_new(NULL);
+ const char *full_source = t_concat(tmp, header, source);
+
+ GLuint shader = gl->CreateShader(type);
+ gl->ShaderSource(shader, 1, &full_source, NULL);
+ gl->CompileShader(shader);
+ GLint status;
+ gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ GLint log_length;
+ gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
+
+ int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DBG2) : MSGL_ERR;
+ const char *typestr = type == GL_VERTEX_SHADER ? "vertex" : "fragment";
+ if (mp_msg_test(MSGT_VO, pri)) {
+ mp_msg(MSGT_VO, pri, "[gl] %s shader source:\n", typestr);