diff options
author | wm4 <wm4@mplayer2.org> | 2012-03-31 01:13:38 +0200 |
---|---|---|
committer | wm4 <wm4@mplayer2.org> | 2012-03-31 02:58:52 +0200 |
commit | 98052873dadf619536c3ab379d45a998f2cf0999 (patch) | |
tree | 33b1dfde0716a2e94b5c21d446aa227c5e502f1c | |
parent | b00c1335c83be933b96de9464779a0e74c34331d (diff) | |
download | mpv-98052873dadf619536c3ab379d45a998f2cf0999.tar.bz2 mpv-98052873dadf619536c3ab379d45a998f2cf0999.tar.xz |
libvo: add vo_gl3
This new vo is heavily based on vo_gl.c. It provides better scale
filters, dithering, and optional color management with LittleCMS2.
It requires OpenGL 3.
Many features are enabled by default, so it will be slower than vo_gl.
However, it can be tuned to behave almost as vo_gl.
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 8 | ||||
-rwxr-xr-x | configure | 21 | ||||
-rw-r--r-- | libvo/filter_kernels.c | 279 | ||||
-rw-r--r-- | libvo/filter_kernels.h | 45 | ||||
-rw-r--r-- | libvo/gl_common.c | 16 | ||||
-rw-r--r-- | libvo/gl_common.h | 4 | ||||
-rw-r--r-- | libvo/video_out.c | 4 | ||||
-rw-r--r-- | libvo/vo_gl3.c | 2418 | ||||
-rw-r--r-- | libvo/vo_gl3_shaders.glsl | 316 |
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 @@ -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 "############################################################" @@ -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, + |