diff options
author | wm4 <wm4@nowhere> | 2012-11-05 17:02:04 +0100 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2012-11-12 20:06:14 +0100 |
commit | d4bdd0473d6f43132257c9fb3848d829755167a3 (patch) | |
tree | 8021c2f7da1841393c8c832105e20cd527826d6c /video/out/vo_opengl_old.c | |
parent | bd48deba77bd5582c5829d6fe73a7d2571088aba (diff) | |
download | mpv-d4bdd0473d6f43132257c9fb3848d829755167a3.tar.bz2 mpv-d4bdd0473d6f43132257c9fb3848d829755167a3.tar.xz |
Rename directories, move files (step 1 of 2) (does not compile)
Tis drops the silly lib prefixes, and attempts to organize the tree in
a more logical way. Make the top-level directory less cluttered as
well.
Renames the following directories:
libaf -> audio/filter
libao2 -> audio/out
libvo -> video/out
libmpdemux -> demux
Split libmpcodecs:
vf* -> video/filter
vd*, dec_video.* -> video/decode
mp_image*, img_format*, ... -> video/
ad*, dec_audio.* -> audio/decode
libaf/format.* is moved to audio/ - this is similar to how mp_image.*
is located in video/.
Move most top-level .c/.h files to core. (talloc.c/.h is left on top-
level, because it's external.) Park some of the more annoying files
in compat/. Some of these are relicts from the time mplayer used
ffmpeg internals.
sub/ is not split, because it's too much of a mess (subtitle code is
mixed with OSD display and rendering).
Maybe the organization of core is not ideal: it mixes playback core
(like mplayer.c) and utility helpers (like bstr.c/h). Should the need
arise, the playback core will be moved somewhere else, while core
contains all helper and common code.
Diffstat (limited to 'video/out/vo_opengl_old.c')
-rw-r--r-- | video/out/vo_opengl_old.c | 1166 |
1 files changed, 1166 insertions, 0 deletions
diff --git a/video/out/vo_opengl_old.c b/video/out/vo_opengl_old.c new file mode 100644 index 0000000000..b8b1fd4813 --- /dev/null +++ b/video/out/vo_opengl_old.c @@ -0,0 +1,1166 @@ +/* + * 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 "talloc.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 "sub/sub.h" + +#include "gl_common.h" +#include "gl_osd.h" +#include "aspect.h" +#include "fastmemcpy.h" + +//for gl_priv.use_yuv +#define MASK_ALL_YUV (~(1 << YUV_CONVERSION_NONE)) +#define MASK_NOT_COMBINERS (~((1 << YUV_CONVERSION_NONE) | (1 << YUV_CONVERSION_COMBINERS))) +#define MASK_GAMMA_SUPPORT (MASK_NOT_COMBINERS & ~(1 << YUV_CONVERSION_FRAGMENT)) + +struct gl_priv { + MPGLContext *glctx; + GL *gl; + + int allow_sw; + + int use_osd; + int scaled_osd; + struct mpgl_osd *osd; + int osd_color; + + int use_ycbcr; + int use_yuv; + struct mp_csp_details colorspace; + int is_yuv; + int lscale; + int cscale; + float filter_strength; + float noise_strength; + int yuvconvtype; + int use_rectangle; + int err_shown; + uint32_t image_width; + uint32_t image_height; + uint32_t image_format; + int many_fmts; + int have_texture_rg; + int ati_hack; + int force_pbo; + int use_glFinish; + int swap_interval; + GLenum target; + GLint texfmt; + GLenum gl_format; + GLenum gl_type; + GLuint buffer; + GLuint buffer_uv[2]; + int buffersize; + int buffersize_uv; + void *bufferptr; + void *bufferptr_uv[2]; + GLuint fragprog; + GLuint default_texs[22]; + char *custom_prog; + char *custom_tex; + int custom_tlin; + int custom_trect; + int mipmap_gen; + int stereo_mode; + + struct mp_csp_equalizer video_eq; + + int texture_width; + int texture_height; + int mpi_flipped; + int vo_flipped; + int ass_border_x, ass_border_y; + + unsigned int slice_height; +}; + +static void resize(struct vo *vo, int x, int y) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n", x, y); + gl->Viewport(0, 0, x, y); + + gl->MatrixMode(GL_PROJECTION); + gl->LoadIdentity(); + p->ass_border_x = p->ass_border_y = 0; + if (aspect_scaling()) { + int new_w, new_h; + GLdouble scale_x, scale_y; + aspect(vo, &new_w, &new_h, A_WINZOOM); + panscan_calc_windowed(vo); + new_w += vo->panscan_x; + new_h += vo->panscan_y; + scale_x = (GLdouble)new_w / (GLdouble)x; + scale_y = (GLdouble)new_h / (GLdouble)y; + gl->Scaled(scale_x, scale_y, 1); + p->ass_border_x = (vo->dwidth - new_w) / 2; + p->ass_border_y = (vo->dheight - new_h) / 2; + } + gl->Ortho(0, p->image_width, p->image_height, 0, -1, 1); + + gl->MatrixMode(GL_MODELVIEW); + gl->LoadIdentity(); + + gl->Clear(GL_COLOR_BUFFER_BIT); + vo->want_redraw = true; +} + +static void texSize(struct vo *vo, int w, int h, int *texw, int *texh) +{ + struct gl_priv *p = vo->priv; + + if (p->use_rectangle) { + *texw = w; + *texh = h; + } else { + *texw = 32; + while (*texw < w) + *texw *= 2; + *texh = 32; + while (*texh < h) + *texh *= 2; + } + if (p->ati_hack) + *texw = (*texw + 511) & ~511; +} + +//! maximum size of custom fragment program +#define MAX_CUSTOM_PROG_SIZE (1024 * 1024) +static void update_yuvconv(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int xs, ys, depth; + struct mp_csp_params cparams = { .colorspace = p->colorspace }; + mp_csp_copy_equalizer_values(&cparams, &p->video_eq); + gl_conversion_params_t params = { + p->target, p->yuvconvtype, cparams, + p->texture_width, p->texture_height, 0, 0, p->filter_strength, + p->noise_strength + }; + mp_get_chroma_shift(p->image_format, &xs, &ys, &depth); + params.chrom_texw = params.texw >> xs; + params.chrom_texh = params.texh >> ys; + params.csp_params.input_bits = depth; + params.csp_params.texture_bits = depth+7 & ~7; + glSetupYUVConversion(gl, ¶ms); + if (p->custom_prog) { + FILE *f = fopen(p->custom_prog, "rb"); + if (!f) { + mp_msg(MSGT_VO, MSGL_WARN, + "[gl] Could not read customprog %s\n", p->custom_prog); + } else { + char *prog = calloc(1, MAX_CUSTOM_PROG_SIZE + 1); + fread(prog, 1, MAX_CUSTOM_PROG_SIZE, f); + fclose(f); + loadGPUProgram(gl, GL_FRAGMENT_PROGRAM, prog); + free(prog); + } + gl->ProgramEnvParameter4f(GL_FRAGMENT_PROGRAM, 0, + 1.0 / p->texture_width, + 1.0 / p->texture_height, + p->texture_width, p->texture_height); + } + if (p->custom_tex) { + FILE *f = fopen(p->custom_tex, "rb"); + if (!f) { + mp_msg(MSGT_VO, MSGL_WARN, + "[gl] Could not read customtex %s\n", p->custom_tex); + } else { + int width, height, maxval; + gl->ActiveTexture(GL_TEXTURE3); + if (glCreatePPMTex(gl, p->custom_trect ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D, + 0, p->custom_tlin ? GL_LINEAR : GL_NEAREST, + f, &width, &height, &maxval)) { + gl->ProgramEnvParameter4f(GL_FRAGMENT_PROGRAM, 1, + 1.0 / width, 1.0 / height, + width, height); + } else + mp_msg(MSGT_VO, MSGL_WARN, + "[gl] Error parsing customtex %s\n", p->custom_tex); + fclose(f); + gl->ActiveTexture(GL_TEXTURE0); + } + } +} + +static void draw_osd(struct vo *vo, struct osd_state *osd) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + assert(p->osd); + + if (!p->use_osd) + return; + + if (!p->scaled_osd) { + gl->MatrixMode(GL_PROJECTION); + gl->PushMatrix(); + gl->LoadIdentity(); + gl->Ortho(0, vo->dwidth, vo->dheight, 0, -1, 1); + } + + gl->Color4ub((p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff, + p->osd_color & 0xff, 0xff - (p->osd_color >> 24)); + + struct mp_osd_res res = { + .w = vo->dwidth, + .h = vo->dheight, + .display_par = vo->monitor_par, + .video_par = vo->aspdat.par, + }; + if (p->scaled_osd) { + res.w = p->image_width; + res.h = p->image_height; + } else if (aspect_scaling()) { + res.ml = res.mr = p->ass_border_x; + res.mt = res.mb = p->ass_border_y; + } + + mpgl_osd_draw_legacy(p->osd, osd, res); + + if (!p->scaled_osd) + gl->PopMatrix(); +} + +/** + * \brief uninitialize OpenGL context, freeing textures, buffers etc. + */ +static void uninitGl(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + if (!gl) + return; + + int i = 0; + if (gl->DeletePrograms && p->fragprog) + gl->DeletePrograms(1, &p->fragprog); + p->fragprog = 0; + while (p->default_texs[i] != 0) + i++; + if (i) + gl->DeleteTextures(i, p->default_texs); + p->default_texs[0] = 0; + if (p->osd) + mpgl_osd_destroy(p->osd); + p->osd = NULL; + p->buffer = 0; + p->buffersize = 0; + p->bufferptr = NULL; + if (gl->DeleteBuffers && p->buffer_uv[0]) + gl->DeleteBuffers(2, p->buffer_uv); + p->buffer_uv[0] = p->buffer_uv[1] = 0; + p->buffersize_uv = 0; + p->bufferptr_uv[0] = p->bufferptr_uv[1] = 0; + p->err_shown = 0; +} + +static void autodetectGlExtensions(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + const char *extensions = gl->GetString(GL_EXTENSIONS); + const char *vendor = gl->GetString(GL_VENDOR); + const char *version = gl->GetString(GL_VERSION); + const char *renderer = gl->GetString(GL_RENDERER); + int is_ati = vendor && strstr(vendor, "ATI") != NULL; + int ati_broken_pbo = 0; + mp_msg(MSGT_VO, MSGL_V, "[gl] Running on OpenGL '%s' by '%s', version '%s'\n", + renderer, vendor, version); + if (is_ati && strncmp(version, "2.1.", 4) == 0) { + int ver = atoi(version + 4); + mp_msg(MSGT_VO, MSGL_V, "[gl] Detected ATI driver version: %i\n", ver); + ati_broken_pbo = ver && ver < 8395; + } + if (p->ati_hack == -1) + p->ati_hack = ati_broken_pbo; + if (p->force_pbo == -1) { + p->force_pbo = 0; + if (extensions && strstr(extensions, "_pixel_buffer_object")) + p->force_pbo = is_ati; + } + p->have_texture_rg = extensions && strstr(extensions, "GL_ARB_texture_rg"); + if (p->use_rectangle == -1) { + p->use_rectangle = 0; + if (extensions) { +// if (strstr(extensions, "_texture_non_power_of_two")) + if (strstr(extensions, "_texture_rectangle")) + p->use_rectangle = renderer + && strstr(renderer, "Mesa DRI R200") ? 1 : 0; + } + } + if (p->use_osd == -1) + p->use_osd = gl->BindTexture != NULL; + if (p->use_yuv == -1) + p->use_yuv = glAutodetectYUVConversion(gl); + + int eq_caps = 0; + int yuv_mask = (1 << p->use_yuv); + if (!(yuv_mask & MASK_NOT_COMBINERS)) { + // combiners + eq_caps = (1 << MP_CSP_EQ_HUE) | (1 << MP_CSP_EQ_SATURATION); + } else if (yuv_mask & MASK_ALL_YUV) { + eq_caps = MP_CSP_EQ_CAPS_COLORMATRIX; + if (yuv_mask & MASK_GAMMA_SUPPORT) + eq_caps |= MP_CSP_EQ_CAPS_GAMMA; + } + p->video_eq.capabilities = eq_caps; + + if (is_ati && (p->lscale == 1 || p->lscale == 2 || p->cscale == 1 || p->cscale == 2)) + mp_msg(MSGT_VO, MSGL_WARN, "[gl] Selected scaling mode may be broken on" + " ATI cards.\n" + "Tell _them_ to fix GL_REPEAT if you have issues.\n"); + mp_msg(MSGT_VO, MSGL_V, "[gl] Settings after autodetection: ati-hack = %i, " + "force-pbo = %i, rectangle = %i, yuv = %i\n", + p->ati_hack, p->force_pbo, p->use_rectangle, p->use_yuv); +} + +static GLint get_scale_type(struct vo *vo, int chroma) +{ + struct gl_priv *p = vo->priv; + + int nearest = (chroma ? p->cscale : p->lscale) & 64; + if (nearest) + return p->mipmap_gen ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST; + return p->mipmap_gen ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR; +} + +// Return the high byte of the value that represents white in chroma (U/V) +static int get_chroma_clear_val(int bit_depth) +{ + return 1 << (bit_depth - 1 & 7); +} + +/** + * \brief Initialize a (new or reused) OpenGL context. + * set global gl-related variables to their default values + */ +static int initGl(struct vo *vo, uint32_t d_width, uint32_t d_height) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + GLint scale_type = get_scale_type(vo, 0); + autodetectGlExtensions(vo); + p->target = p->use_rectangle == 1 ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D; + p->yuvconvtype = SET_YUV_CONVERSION(p->use_yuv) | + SET_YUV_LUM_SCALER(p->lscale) | + SET_YUV_CHROM_SCALER(p->cscale); + + texSize(vo, p->image_width, p->image_height, + &p->texture_width, &p->texture_height); + + gl->Disable(GL_BLEND); + gl->Disable(GL_DEPTH_TEST); + gl->DepthMask(GL_FALSE); + gl->Disable(GL_CULL_FACE); + gl->Enable(p->target); + gl->DrawBuffer(GL_BACK); + gl->TexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + mp_msg(MSGT_VO, MSGL_V, "[gl] Creating %dx%d texture...\n", + p->texture_width, p->texture_height); + + glCreateClearTex(gl, p->target, p->texfmt, p->gl_format, + p->gl_type, scale_type, + p->texture_width, p->texture_height, 0); + + if (p->mipmap_gen) + gl->TexParameteri(p->target, GL_GENERATE_MIPMAP, GL_TRUE); + + if (p->is_yuv) { + int i; + int xs, ys, depth; + scale_type = get_scale_type(vo, 1); + mp_get_chroma_shift(p->image_format, &xs, &ys, &depth); + int clear = get_chroma_clear_val(depth); + gl->GenTextures(21, p->default_texs); + p->default_texs[21] = 0; + for (i = 0; i < 7; i++) { + gl->ActiveTexture(GL_TEXTURE1 + i); + gl->BindTexture(GL_TEXTURE_2D, p->default_texs[i]); + gl->BindTexture(GL_TEXTURE_RECTANGLE, p->default_texs[i + 7]); + gl->BindTexture(GL_TEXTURE_3D, p->default_texs[i + 14]); + } + gl->ActiveTexture(GL_TEXTURE1); + glCreateClearTex(gl, p->target, p->texfmt, p->gl_format, + p->gl_type, scale_type, + p->texture_width >> xs, p->texture_height >> ys, + clear); + if (p->mipmap_gen) + gl->TexParameteri(p->target, GL_GENERATE_MIPMAP, GL_TRUE); + gl->ActiveTexture(GL_TEXTURE2); + glCreateClearTex(gl, p->target, p->texfmt, p->gl_format, + p->gl_type, scale_type, + p->texture_width >> xs, p->texture_height >> ys, + clear); + if (p->mipmap_gen) + gl->TexParameteri(p->target, GL_GENERATE_MIPMAP, GL_TRUE); + gl->ActiveTexture(GL_TEXTURE0); + gl->BindTexture(p->target, 0); + } + if (p->is_yuv || p->custom_prog) { + if ((MASK_NOT_COMBINERS & (1 << p->use_yuv)) || p->custom_prog) { + if (!gl->GenPrograms || !gl->BindProgram) + mp_msg(MSGT_VO, MSGL_ERR, + "[gl] fragment program functions missing!\n"); + else { + gl->GenPrograms(1, &p->fragprog); + gl->BindProgram(GL_FRAGMENT_PROGRAM, p->fragprog); + } + } + update_yuvconv(vo); + } + + p->osd = mpgl_osd_init(gl, true); + p->osd->scaled = p->scaled_osd; + + resize(vo, d_width, d_height); + + gl->ClearColor(0.0f, 0.0f, 0.0f, 0.0f); + gl->Clear(GL_COLOR_BUFFER_BIT); + if (gl->SwapInterval && p->swap_interval >= 0) + gl->SwapInterval(p->swap_interval); + return 1; +} + +static bool create_window(struct vo *vo, uint32_t d_width, uint32_t d_height, + uint32_t flags) +{ + struct gl_priv *p = vo->priv; + + if (p->stereo_mode == GL_3D_QUADBUFFER) + flags |= VOFLAG_STEREO; + + int mpgl_caps = MPGL_CAP_GL_LEGACY; + if (!p->allow_sw) + mpgl_caps |= MPGL_CAP_NO_SW; + return mpgl_create_window(p->glctx, mpgl_caps, d_width, d_height, flags); +} + +static int config(struct vo *vo, uint32_t width, uint32_t height, + uint32_t d_width, uint32_t d_height, uint32_t flags, + uint32_t format) +{ + struct gl_priv *p = vo->priv; + + int xs, ys; + p->image_height = height; + p->image_width = width; + p->image_format = format; + p->is_yuv = mp_get_chroma_shift(p->image_format, &xs, &ys, NULL) > 0; + p->is_yuv |= (xs << 8) | (ys << 16); + glFindFormat(format, p->have_texture_rg, NULL, &p->texfmt, &p->gl_format, + &p->gl_type); + + p->vo_flipped = !!(flags & VOFLAG_FLIPPING); + + if (vo->config_count) + uninitGl(vo); + + if (!create_window(vo, d_width, d_height, flags)) + return -1; + + initGl(vo, vo->dwidth, vo->dheight); + + return 0; +} + +static void check_events(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + + int e = p->glctx->check_events(vo); + if (e & VO_EVENT_REINIT) { + uninitGl(vo); + initGl(vo, vo->dwidth, vo->dheight); + } + if (e & VO_EVENT_RESIZE) + resize(vo, vo->dwidth, vo->dheight); + if (e & VO_EVENT_EXPOSE) + vo->want_redraw = true; +} + +static void do_render(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + +// Enable(GL_TEXTURE_2D); +// BindTexture(GL_TEXTURE_2D, texture_id); + + gl->Color4f(1, 1, 1, 1); + if (p->is_yuv || p->custom_prog) + glEnableYUVConversion(gl, p->target, p->yuvconvtype); + if (p->stereo_mode) { + glEnable3DLeft(gl, p->stereo_mode); + glDrawTex(gl, 0, 0, p->image_width, p->image_height, + 0, 0, p->image_width >> 1, p->image_height, + p->texture_width, p->texture_height, + p->use_rectangle == 1, p->is_yuv, + p->mpi_flipped ^ p->vo_flipped); + glEnable3DRight(gl, p->stereo_mode); + glDrawTex(gl, 0, 0, p->image_width, p->image_height, + p->image_width >> 1, 0, p->image_width >> 1, + p->image_height, p->texture_width, p->texture_height, + p->use_rectangle == 1, p->is_yuv, + p->mpi_flipped ^ p->vo_flipped); + glDisable3D(gl, p->stereo_mode); + } else { + glDrawTex(gl, 0, 0, p->image_width, p->image_height, + 0, 0, p->image_width, p->image_height, + p->texture_width, p->texture_height, + p->use_rectangle == 1, p->is_yuv, + p->mpi_flipped ^ p->vo_flipped); + } + if (p->is_yuv || p->custom_prog) + glDisableYUVConversion(gl, p->target, p->yuvconvtype); +} + +static void flip_page(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + if (p->use_glFinish) + gl->Finish(); + p->glctx->swapGlBuffers(p->glctx); + if (aspect_scaling()) + gl->Clear(GL_COLOR_BUFFER_BIT); +} + +static int draw_slice(struct vo *vo, uint8_t *src[], int stride[], int w, int h, + int x, int y) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + p->mpi_flipped = stride[0] < 0; + glUploadTex(gl, p->target, p->gl_format, p->gl_type, src[0], stride[0], + x, y, w, h, p->slice_height); + if (p->is_yuv) { + int xs, ys; + mp_get_chroma_shift(p->image_format, &xs, &ys, NULL); + gl->ActiveTexture(GL_TEXTURE1); + glUploadTex(gl, p->target, p->gl_format, p->gl_type, src[1], stride[1], + x >> xs, y >> ys, w >> xs, h >> ys, p->slice_height); + gl->ActiveTexture(GL_TEXTURE2); + glUploadTex(gl, p->target, p->gl_format, p->gl_type, src[2], stride[2], + x >> xs, y >> ys, w >> xs, h >> ys, p->slice_height); + gl->ActiveTexture(GL_TEXTURE0); + } + return 0; +} + +static uint32_t get_image(struct vo *vo, mp_image_t *mpi) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int needed_size; + if (!gl->GenBuffers || !gl->BindBuffer || !gl->BufferData || !gl->MapBuffer) { + if (!p->err_shown) + mp_msg(MSGT_VO, MSGL_ERR, "[gl] extensions missing for dr\n" + "Expect a _major_ speed penalty\n"); + p->err_shown = 1; + return VO_FALSE; + } + if (mpi->flags & MP_IMGFLAG_READABLE) + return VO_FALSE; + if (mpi->type != MP_IMGTYPE_STATIC && mpi->type != MP_IMGTYPE_TEMP && + (mpi->type != MP_IMGTYPE_NUMBERED || mpi->number)) + return VO_FALSE; + if (p->ati_hack) { + mpi->width = p->texture_width; + mpi->height = p->texture_height; + } + mpi->stride[0] = mpi->width * mpi->bpp / 8; + needed_size = mpi->stride[0] * mpi->height; + if (!p->buffer) + gl->GenBuffers(1, &p->buffer); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer); + if (needed_size > p->buffersize) { + p->buffersize = needed_size; + gl->BufferData(GL_PIXEL_UNPACK_BUFFER, p->buffersize, + NULL, GL_DYNAMIC_DRAW); + } + if (!p->bufferptr) + p->bufferptr = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); + mpi->planes[0] = p->bufferptr; + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + if (!mpi->planes[0]) { + if (!p->err_shown) + mp_msg(MSGT_VO, MSGL_ERR, "[gl] could not acquire buffer for dr\n" + "Expect a _major_ speed penalty\n"); + p->err_shown = 1; + return VO_FALSE; + } + if (p->is_yuv) { + // planar YUV + int xs, ys, component_bits; + mp_get_chroma_shift(p->image_format, &xs, &ys, &component_bits); + int bp = (component_bits + 7) / 8; + mpi->flags |= MP_IMGFLAG_COMMON_STRIDE | MP_IMGFLAG_COMMON_PLANE; + mpi->stride[0] = mpi->width * bp; + mpi->planes[1] = mpi->planes[0] + mpi->stride[0] * mpi->height; + mpi->stride[1] = (mpi->width >> xs) * bp; + mpi->planes[2] = mpi->planes[1] + mpi->stride[1] * (mpi->height >> ys); + mpi->stride[2] = (mpi->width >> xs) * bp; + if (p->ati_hack) { + mpi->flags &= ~MP_IMGFLAG_COMMON_PLANE; + if (!p->buffer_uv[0]) + gl->GenBuffers(2, p->buffer_uv); + int buffer_size = mpi->stride[1] * mpi->height; + if (buffer_size > p->buffersize_uv) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[0]); + gl->BufferData(GL_PIXEL_UNPACK_BUFFER, buffer_size, NULL, + GL_DYNAMIC_DRAW); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]); + gl->BufferData(GL_PIXEL_UNPACK_BUFFER, buffer_size, NULL, + GL_DYNAMIC_DRAW); + p->buffersize_uv = buffer_size; + } + if (!p->bufferptr_uv[0]) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[0]); + p->bufferptr_uv[0] = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, + GL_WRITE_ONLY); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]); + p->bufferptr_uv[1] = gl->MapBuffer(GL_PIXEL_UNPACK_BUFFER, + GL_WRITE_ONLY); + } + mpi->planes[1] = p->bufferptr_uv[0]; + mpi->planes[2] = p->bufferptr_uv[1]; + } + } + mpi->flags |= MP_IMGFLAG_DIRECT; + return VO_TRUE; +} + +static void clear_border(struct vo *vo, uint8_t *dst, int start, int stride, + int height, int full_height, int value) +{ + int right_border = stride - start; + int bottom_border = full_height - height; + while (height > 0) { + if (right_border > 0) + memset(dst + start, value, right_border); + dst += stride; + height--; + } + if (bottom_border > 0) + memset(dst, value, stride * bottom_border); +} + +static uint32_t draw_image(struct vo *vo, mp_image_t *mpi) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int slice = p->slice_height; + int stride[3]; + unsigned char *planes[3]; + mp_image_t mpi2 = *mpi; + int w = mpi->w, h = mpi->h; + if (mpi->flags & MP_IMGFLAG_DRAW_CALLBACK) + goto skip_upload; + mpi2.flags = 0; + mpi2.type = MP_IMGTYPE_TEMP; + mpi2.width = mpi2.w; + mpi2.height = mpi2.h; + if (p->force_pbo && !(mpi->flags & MP_IMGFLAG_DIRECT) && !p->bufferptr + && get_image(vo, &mpi2) == VO_TRUE) + { + int bp = mpi->bpp / 8; + int xs, ys, component_bits; + mp_get_chroma_shift(p->image_format, &xs, &ys, &component_bits); + if (p->is_yuv) + bp = (component_bits + 7) / 8; + memcpy_pic(mpi2.planes[0], mpi->planes[0], mpi->w * bp, mpi->h, + mpi2.stride[0], mpi->stride[0]); + int uv_bytes = (mpi->w >> xs) * bp; + if (p->is_yuv) { + memcpy_pic(mpi2.planes[1], mpi->planes[1], uv_bytes, mpi->h >> ys, + mpi2.stride[1], mpi->stride[1]); + memcpy_pic(mpi2.planes[2], mpi->planes[2], uv_bytes, mpi->h >> ys, + mpi2.stride[2], mpi->stride[2]); + } + if (p->ati_hack) { + // since we have to do a full upload we need to clear the borders + clear_border(vo, mpi2.planes[0], mpi->w * bp, mpi2.stride[0], + mpi->h, mpi2.height, 0); + if (p->is_yuv) { + int clear = get_chroma_clear_val(component_bits); + clear_border(vo, mpi2.planes[1], uv_bytes, mpi2.stride[1], + mpi->h >> ys, mpi2.height >> ys, clear); + clear_border(vo, mpi2.planes[2], uv_bytes, mpi2.stride[2], + mpi->h >> ys, mpi2.height >> ys, clear); + } + } + mpi = &mpi2; + } + stride[0] = mpi->stride[0]; + stride[1] = mpi->stride[1]; + stride[2] = mpi->stride[2]; + planes[0] = mpi->planes[0]; + planes[1] = mpi->planes[1]; + planes[2] = mpi->planes[2]; + p->mpi_flipped = stride[0] < 0; + if (mpi->flags & MP_IMGFLAG_DIRECT) { + intptr_t base = (intptr_t)planes[0]; + if (p->ati_hack) { + w = p->texture_width; + h = p->texture_height; + } + if (p->mpi_flipped) + base += (mpi->h - 1) * stride[0]; + planes[0] -= base; + planes[1] -= base; + planes[2] -= base; + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer); + gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + p->bufferptr = NULL; + if (!(mpi->flags & MP_IMGFLAG_COMMON_PLANE)) + planes[0] = planes[1] = planes[2] = NULL; + slice = 0; // always "upload" full texture + } + glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[0], + stride[0], 0, 0, w, h, slice); + if (p->is_yuv) { + int xs, ys; + mp_get_chroma_shift(p->image_format, &xs, &ys, NULL); + if ((mpi->flags & MP_IMGFLAG_DIRECT) && !(mpi->flags & MP_IMGFLAG_COMMON_PLANE)) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[0]); + gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + p->bufferptr_uv[0] = NULL; + } + gl->ActiveTexture(GL_TEXTURE1); + glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[1], + stride[1], 0, 0, w >> xs, h >> ys, slice); + if ((mpi->flags & MP_IMGFLAG_DIRECT) && !(mpi->flags & MP_IMGFLAG_COMMON_PLANE)) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]); + gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + p->bufferptr_uv[1] = NULL; + } + gl->ActiveTexture(GL_TEXTURE2); + glUploadTex(gl, p->target, p->gl_format, p->gl_type, planes[2], + stride[2], 0, 0, w >> xs, h >> ys, slice); + gl->ActiveTexture(GL_TEXTURE0); + } + if (mpi->flags & MP_IMGFLAG_DIRECT) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } +skip_upload: + do_render(vo); + return VO_TRUE; +} + +static mp_image_t *get_screenshot(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + mp_image_t *image = alloc_mpi(p->texture_width, p->texture_height, + p->image_format); + + glDownloadTex(gl, p->target, p->gl_format, p->gl_type, image->planes[0], + image->stride[0]); + + if (p->is_yuv) { + gl->ActiveTexture(GL_TEXTURE1); + glDownloadTex(gl, p->target, p->gl_format, p->gl_type, image->planes[1], + image->stride[1]); + gl->ActiveTexture(GL_TEXTURE2); + glDownloadTex(gl, p->target, p->gl_format, p->gl_type, image->planes[2], + image->stride[2]); + gl->ActiveTexture(GL_TEXTURE0); + } + + image->w = p->image_width; + image->h = p->image_height; + image->display_w = vo->aspdat.prew; + image->display_h = vo->aspdat.preh; + + mp_image_set_colorspace_details(image, &p->colorspace); + + return image; +} + +static int query_format(struct vo *vo, uint32_t format) +{ + struct gl_priv *p = vo->priv; + + int depth; + int caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_FLIP | + VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE; + if (p->use_osd) + caps |= VFCAP_OSD; + if (format == IMGFMT_RGB24 || format == IMGFMT_RGBA) + return caps; + if (p->use_yuv && mp_get_chroma_shift(format, NULL, NULL, &depth) && + (depth == 8 || depth == 16 || glYUVLargeRange(p->use_yuv)) && + (IMGFMT_IS_YUVP16_NE(format) || !IMGFMT_IS_YUVP16(format))) + return caps; + // HACK, otherwise we get only b&w with some filters (e.g. -vf eq) + // ideally MPlayer should be fixed instead not to use Y800 when it has the choice + if (!p->use_yuv && (format == IMGFMT_Y8 || format == IMGFMT_Y800)) + return 0; + if (!p->use_ycbcr && (format == IMGFMT_UYVY || format == IMGFMT_YVYU)) + return 0; + if (p->many_fmts && + glFindFormat(format, p->have_texture_rg, NULL, NULL, NULL, NULL)) + return caps; + return 0; +} + +static void uninit(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + + uninitGl(vo); + free(p->custom_prog); + p->custom_prog = NULL; + free(p->custom_tex); + p->custom_tex = NULL; + mpgl_uninit(p->glctx); + p->glctx = NULL; + p->gl = NULL; +} + +static int backend_valid(void *arg) +{ + return mpgl_find_backend(*(const char **)arg) >= 0; +} + +static int preinit(struct vo *vo, const char *arg) +{ + struct gl_priv *p = talloc_zero(vo, struct gl_priv); + vo->priv = p; + + *p = (struct gl_priv) { + .many_fmts = 1, + .use_osd = -1, + .use_yuv = -1, + .colorspace = MP_CSP_DETAILS_DEFAULTS, + .filter_strength = 0.5, + .use_rectangle = -1, + .ati_hack = -1, + .force_pbo = -1, + .swap_interval = vo_vsync, + .custom_prog = NULL, + .custom_tex = NULL, + .custom_tlin = 1, + .osd_color = 0xffffff, + }; + + char *backend_arg = NULL; + + //essentially unused; for legacy warnings only + int user_colorspace = 0; + int levelconv = -1; + int aspect = -1; + + const opt_t subopts[] = { + {"manyfmts", OPT_ARG_BOOL, &p->many_fmts, NULL}, + {"osd", OPT_ARG_BOOL, &p->use_osd, NULL}, + {"scaled-osd", OPT_ARG_BOOL, &p->scaled_osd, NULL}, + {"ycbcr", OPT_ARG_BOOL, &p->use_ycbcr, NULL}, + {"slice-height", OPT_ARG_INT, &p->slice_height, int_non_neg}, + {"rectangle", OPT_ARG_INT, &p->use_rectangle,int_non_neg}, + {"yuv", OPT_ARG_INT, &p->use_yuv, int_non_neg}, + {"lscale", OPT_ARG_INT, &p->lscale, int_non_neg}, + {"cscale", OPT_ARG_INT, &p->cscale, int_non_neg}, + {"filter-strength", OPT_ARG_FLOAT, &p->filter_strength, NULL}, + {"noise-strength", OPT_ARG_FLOAT, &p->noise_strength, NULL}, + {"ati-hack", OPT_ARG_BOOL, &p->ati_hack, NULL}, + {"force-pbo", OPT_ARG_BOOL, &p->force_pbo, NULL}, + {"glfinish", OPT_ARG_BOOL, &p->use_glFinish, NULL}, + {"swapinterval", OPT_ARG_INT, &p->swap_interval,NULL}, + {"customprog", OPT_ARG_MSTRZ,&p->custom_prog, NULL}, + {"customtex", OPT_ARG_MSTRZ,&p->custom_tex, NULL}, + {"customtlin", OPT_AR |