From c852246cdf3e2604d1a3bff1fccd981d7b3b58c9 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 25 Sep 2011 11:18:07 +0200 Subject: [PATCH] vo_gl: convert to new API, clean up code Reformat vo_gl.c, gl_common.c, gl_common.h. Remove all global variables and move them into a context struct (the Windows and SDL backends still refer to global_vo though). Change vo_gl.c to use the "new" VO API. --- libvo/gl_common.c | 4146 +++++++++++++++++++++++++++-------------------------- libvo/gl_common.h | 312 ++-- libvo/vo_gl.c | 2981 ++++++++++++++++++++------------------ 3 files changed, 3837 insertions(+), 3602 deletions(-) rewrite libvo/gl_common.c (76%) rewrite libvo/vo_gl.c (89%) diff --git a/libvo/gl_common.c b/libvo/gl_common.c dissimilarity index 76% index eb12d79a74..16e05fb688 100644 --- a/libvo/gl_common.c +++ b/libvo/gl_common.c @@ -1,2059 +1,2087 @@ -/* - * common OpenGL routines - * - * copyleft (C) 2005-2010 Reimar Döffinger - * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c - * gave me lots of good ideas. - * - * 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. - */ - -/** - * \file gl_common.c - * \brief OpenGL helper functions used by vo_gl.c and vo_gl2.c - */ - -#include -#include -#include -#include -#include -#include "old_vo_defines.h" -#include "gl_common.h" -#include "csputils.h" -#include "aspect.h" -#include "pnm_loader.h" - -void (GLAPIENTRY *mpglBegin)(GLenum); -void (GLAPIENTRY *mpglEnd)(void); -void (GLAPIENTRY *mpglViewport)(GLint, GLint, GLsizei, GLsizei); -void (GLAPIENTRY *mpglMatrixMode)(GLenum); -void (GLAPIENTRY *mpglLoadIdentity)(void); -void (GLAPIENTRY *mpglTranslated)(double, double, double); -void (GLAPIENTRY *mpglScaled)(double, double, double); -void (GLAPIENTRY *mpglOrtho)(double, double, double, double, double, double); -void (GLAPIENTRY *mpglFrustum)(double, double, double, double, double, double); -void (GLAPIENTRY *mpglPushMatrix)(void); -void (GLAPIENTRY *mpglPopMatrix)(void); -void (GLAPIENTRY *mpglClear)(GLbitfield); -GLuint (GLAPIENTRY *mpglGenLists)(GLsizei); -void (GLAPIENTRY *mpglDeleteLists)(GLuint, GLsizei); -void (GLAPIENTRY *mpglNewList)(GLuint, GLenum); -void (GLAPIENTRY *mpglEndList)(void); -void (GLAPIENTRY *mpglCallList)(GLuint); -void (GLAPIENTRY *mpglCallLists)(GLsizei, GLenum, const GLvoid *); -void (GLAPIENTRY *mpglGenTextures)(GLsizei, GLuint *); -void (GLAPIENTRY *mpglDeleteTextures)(GLsizei, const GLuint *); -void (GLAPIENTRY *mpglTexEnvf)(GLenum, GLenum, GLfloat); -void (GLAPIENTRY *mpglTexEnvi)(GLenum, GLenum, GLint); -void (GLAPIENTRY *mpglColor4ub)(GLubyte, GLubyte, GLubyte, GLubyte); -void (GLAPIENTRY *mpglColor3f)(GLfloat, GLfloat, GLfloat); -void (GLAPIENTRY *mpglColor4f)(GLfloat, GLfloat, GLfloat, GLfloat); -void (GLAPIENTRY *mpglClearColor)(GLclampf, GLclampf, GLclampf, GLclampf); -void (GLAPIENTRY *mpglClearDepth)(GLclampd); -void (GLAPIENTRY *mpglDepthFunc)(GLenum); -void (GLAPIENTRY *mpglEnable)(GLenum); -void (GLAPIENTRY *mpglDisable)(GLenum); -const GLubyte *(GLAPIENTRY *mpglGetString)(GLenum); -void (GLAPIENTRY *mpglDrawBuffer)(GLenum); -void (GLAPIENTRY *mpglDepthMask)(GLboolean); -void (GLAPIENTRY *mpglBlendFunc)(GLenum, GLenum); -void (GLAPIENTRY *mpglFlush)(void); -void (GLAPIENTRY *mpglFinish)(void); -void (GLAPIENTRY *mpglPixelStorei)(GLenum, GLint); -void (GLAPIENTRY *mpglTexImage1D)(GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const GLvoid *); -void (GLAPIENTRY *mpglTexImage2D)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *); -void (GLAPIENTRY *mpglTexSubImage2D)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); -void (GLAPIENTRY *mpglTexParameteri)(GLenum, GLenum, GLint); -void (GLAPIENTRY *mpglTexParameterf)(GLenum, GLenum, GLfloat); -void (GLAPIENTRY *mpglTexParameterfv)(GLenum, GLenum, const GLfloat *); -void (GLAPIENTRY *mpglTexCoord2f)(GLfloat, GLfloat); -void (GLAPIENTRY *mpglVertex2f)(GLfloat, GLfloat); -void (GLAPIENTRY *mpglVertex3f)(GLfloat, GLfloat, GLfloat); -void (GLAPIENTRY *mpglNormal3f)(GLfloat, GLfloat, GLfloat); -void (GLAPIENTRY *mpglLightfv)(GLenum, GLenum, const GLfloat *); -void (GLAPIENTRY *mpglColorMaterial)(GLenum, GLenum); -void (GLAPIENTRY *mpglShadeModel)(GLenum); -void (GLAPIENTRY *mpglGetIntegerv)(GLenum, GLint *); -void (GLAPIENTRY *mpglColorMask)(GLboolean, GLboolean, GLboolean, GLboolean); - -/** - * \defgroup glextfunctions OpenGL extension functions - * - * the pointers to these functions are acquired when the OpenGL - * context is created - * \{ - */ -void (GLAPIENTRY *mpglGenBuffers)(GLsizei, GLuint *); -void (GLAPIENTRY *mpglDeleteBuffers)(GLsizei, const GLuint *); -void (GLAPIENTRY *mpglBindBuffer)(GLenum, GLuint); -GLvoid* (GLAPIENTRY *mpglMapBuffer)(GLenum, GLenum); -GLboolean (GLAPIENTRY *mpglUnmapBuffer)(GLenum); -void (GLAPIENTRY *mpglBufferData)(GLenum, intptr_t, const GLvoid *, GLenum); -void (GLAPIENTRY *mpglCombinerParameterfv)(GLenum, const GLfloat *); -void (GLAPIENTRY *mpglCombinerParameteri)(GLenum, GLint); -void (GLAPIENTRY *mpglCombinerInput)(GLenum, GLenum, GLenum, GLenum, GLenum, - GLenum); -void (GLAPIENTRY *mpglCombinerOutput)(GLenum, GLenum, GLenum, GLenum, GLenum, - GLenum, GLenum, GLboolean, GLboolean, - GLboolean); -void (GLAPIENTRY *mpglBeginFragmentShader)(void); -void (GLAPIENTRY *mpglEndFragmentShader)(void); -void (GLAPIENTRY *mpglSampleMap)(GLuint, GLuint, GLenum); -void (GLAPIENTRY *mpglColorFragmentOp2)(GLenum, GLuint, GLuint, GLuint, GLuint, - GLuint, GLuint, GLuint, GLuint, GLuint); -void (GLAPIENTRY *mpglColorFragmentOp3)(GLenum, GLuint, GLuint, GLuint, GLuint, - GLuint, GLuint, GLuint, GLuint, GLuint, - GLuint, GLuint, GLuint); -void (GLAPIENTRY *mpglSetFragmentShaderConstant)(GLuint, const GLfloat *); -void (GLAPIENTRY *mpglActiveTexture)(GLenum); -void (GLAPIENTRY *mpglBindTexture)(GLenum, GLuint); -void (GLAPIENTRY *mpglMultiTexCoord2f)(GLenum, GLfloat, GLfloat); -void (GLAPIENTRY *mpglGenPrograms)(GLsizei, GLuint *); -void (GLAPIENTRY *mpglDeletePrograms)(GLsizei, const GLuint *); -void (GLAPIENTRY *mpglBindProgram)(GLenum, GLuint); -void (GLAPIENTRY *mpglProgramString)(GLenum, GLenum, GLsizei, const GLvoid *); -void (GLAPIENTRY *mpglGetProgramiv)(GLenum, GLenum, GLint *); -void (GLAPIENTRY *mpglProgramEnvParameter4f)(GLenum, GLuint, GLfloat, GLfloat, - GLfloat, GLfloat); -int (GLAPIENTRY *mpglSwapInterval)(int); -void (GLAPIENTRY *mpglTexImage3D)(GLenum, GLint, GLenum, GLsizei, GLsizei, GLsizei, - GLint, GLenum, GLenum, const GLvoid *); -void* (GLAPIENTRY *mpglAllocateMemoryMESA)(void *, int, size_t, float, float, float); -void (GLAPIENTRY *mpglFreeMemoryMESA)(void *, int, void *); -/** \} */ // end of glextfunctions group - -//! \defgroup glgeneral OpenGL general helper functions - -//! \defgroup glcontext OpenGL context management helper functions - -//! \defgroup gltexture OpenGL texture handling helper functions - -//! \defgroup glconversion OpenGL conversion helper functions - -static GLint hqtexfmt; - -/** - * \brief adjusts the GL_UNPACK_ALIGNMENT to fit the stride. - * \param stride number of bytes per line for which alignment should fit. - * \ingroup glgeneral - */ -void glAdjustAlignment(int stride) { - GLint gl_alignment; - if (stride % 8 == 0) - gl_alignment=8; - else if (stride % 4 == 0) - gl_alignment=4; - else if (stride % 2 == 0) - gl_alignment=2; - else - gl_alignment=1; - mpglPixelStorei(GL_UNPACK_ALIGNMENT, gl_alignment); -} - -struct gl_name_map_struct { - GLint value; - const char *name; -}; - -#undef MAP -#define MAP(a) {a, #a} -//! mapping table for the glValName function -static const struct gl_name_map_struct gl_name_map[] = { - // internal format - MAP(GL_R3_G3_B2), MAP(GL_RGB4), MAP(GL_RGB5), MAP(GL_RGB8), - MAP(GL_RGB10), MAP(GL_RGB12), MAP(GL_RGB16), MAP(GL_RGBA2), - MAP(GL_RGBA4), MAP(GL_RGB5_A1), MAP(GL_RGBA8), MAP(GL_RGB10_A2), - MAP(GL_RGBA12), MAP(GL_RGBA16), MAP(GL_LUMINANCE8), MAP(GL_LUMINANCE16), - - // format - MAP(GL_RGB), MAP(GL_RGBA), MAP(GL_RED), MAP(GL_GREEN), MAP(GL_BLUE), - MAP(GL_ALPHA), MAP(GL_LUMINANCE), MAP(GL_LUMINANCE_ALPHA), - MAP(GL_COLOR_INDEX), - // rest 1.2 only - MAP(GL_BGR), MAP(GL_BGRA), - - //type - MAP(GL_BYTE), MAP(GL_UNSIGNED_BYTE), MAP(GL_SHORT), MAP(GL_UNSIGNED_SHORT), - MAP(GL_INT), MAP(GL_UNSIGNED_INT), MAP(GL_FLOAT), MAP(GL_DOUBLE), - MAP(GL_2_BYTES), MAP(GL_3_BYTES), MAP(GL_4_BYTES), - // rest 1.2 only - MAP(GL_UNSIGNED_BYTE_3_3_2), MAP(GL_UNSIGNED_BYTE_2_3_3_REV), - MAP(GL_UNSIGNED_SHORT_5_6_5), MAP(GL_UNSIGNED_SHORT_5_6_5_REV), - MAP(GL_UNSIGNED_SHORT_4_4_4_4), MAP(GL_UNSIGNED_SHORT_4_4_4_4_REV), - MAP(GL_UNSIGNED_SHORT_5_5_5_1), MAP(GL_UNSIGNED_SHORT_1_5_5_5_REV), - MAP(GL_UNSIGNED_INT_8_8_8_8), MAP(GL_UNSIGNED_INT_8_8_8_8_REV), - MAP(GL_UNSIGNED_INT_10_10_10_2), MAP(GL_UNSIGNED_INT_2_10_10_10_REV), - {0, 0} -}; -#undef MAP - -/** - * \brief return the name of an OpenGL constant - * \param value the constant - * \return name of the constant or "Unknown format!" - * \ingroup glgeneral - */ -const char *glValName(GLint value) -{ - int i = 0; - - while (gl_name_map[i].name) { - if (gl_name_map[i].value == value) - return gl_name_map[i].name; - i++; - } - return "Unknown format!"; -} - -//! always return this format as internal texture format in glFindFormat -#define TEXTUREFORMAT_ALWAYS GL_RGB8 -#undef TEXTUREFORMAT_ALWAYS - -/** - * \brief find the OpenGL settings coresponding to format. - * - * All parameters may be NULL. - * \param fmt MPlayer format to analyze. - * \param bpp [OUT] bits per pixel of that format. - * \param gl_texfmt [OUT] internal texture format that fits the - * image format, not necessarily the best for performance. - * \param gl_format [OUT] OpenGL format for this image format. - * \param gl_type [OUT] OpenGL type for this image format. - * \return 1 if format is supported by OpenGL, 0 if not. - * \ingroup gltexture - */ -int glFindFormat(uint32_t fmt, int *bpp, GLint *gl_texfmt, - GLenum *gl_format, GLenum *gl_type) -{ - int supported = 1; - int dummy1; - GLenum dummy2; - GLint dummy3; - if (!bpp) bpp = &dummy1; - if (!gl_texfmt) gl_texfmt = &dummy3; - if (!gl_format) gl_format = &dummy2; - if (!gl_type) gl_type = &dummy2; - - if (mp_get_chroma_shift(fmt, NULL, NULL, NULL)) { - // reduce the possible cases a bit - if (IMGFMT_IS_YUVP16_LE(fmt)) - fmt = IMGFMT_420P16_LE; - else if (IMGFMT_IS_YUVP16_BE(fmt)) - fmt = IMGFMT_420P16_BE; - else - fmt = IMGFMT_YV12; - } - - *bpp = IMGFMT_IS_BGR(fmt)?IMGFMT_BGR_DEPTH(fmt):IMGFMT_RGB_DEPTH(fmt); - *gl_texfmt = 3; - switch (fmt) { - case IMGFMT_RGB48NE: - *gl_format = GL_RGB; - *gl_type = GL_UNSIGNED_SHORT; - break; - case IMGFMT_RGB24: - *gl_format = GL_RGB; - *gl_type = GL_UNSIGNED_BYTE; - break; - case IMGFMT_RGBA: - *gl_texfmt = 4; - *gl_format = GL_RGBA; - *gl_type = GL_UNSIGNED_BYTE; - break; - case IMGFMT_420P16: - supported = 0; // no native YUV support - *gl_texfmt = GL_LUMINANCE16; - *bpp = 16; - *gl_format = GL_LUMINANCE; - *gl_type = GL_UNSIGNED_SHORT; - break; - case IMGFMT_YV12: - supported = 0; // no native YV12 support - case IMGFMT_Y800: - case IMGFMT_Y8: - *gl_texfmt = 1; - *bpp = 8; - *gl_format = GL_LUMINANCE; - *gl_type = GL_UNSIGNED_BYTE; - break; - case IMGFMT_UYVY: - // IMGFMT_YUY2 would be more logical for the _REV format, - // but gives clearly swapped colors. - case IMGFMT_YVYU: - *gl_texfmt = GL_YCBCR_MESA; - *bpp = 16; - *gl_format = GL_YCBCR_MESA; - *gl_type = fmt == IMGFMT_UYVY ? GL_UNSIGNED_SHORT_8_8 : GL_UNSIGNED_SHORT_8_8_REV; - break; -#if 0 - // we do not support palettized formats, although the format the - // swscale produces works - case IMGFMT_RGB8: - gl_format = GL_RGB; - gl_type = GL_UNSIGNED_BYTE_2_3_3_REV; - break; -#endif - case IMGFMT_RGB15: - *gl_format = GL_RGBA; - *gl_type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - break; - case IMGFMT_RGB16: - *gl_format = GL_RGB; - *gl_type = GL_UNSIGNED_SHORT_5_6_5_REV; - break; -#if 0 - case IMGFMT_BGR8: - // special case as red and blue have a differen number of bits. - // GL_BGR and GL_UNSIGNED_BYTE_3_3_2 isn't supported at least - // by nVidia drivers, and in addition would give more bits to - // blue than to red, which isn't wanted - gl_format = GL_RGB; - gl_type = GL_UNSIGNED_BYTE_3_3_2; - break; -#endif - case IMGFMT_BGR15: - *gl_format = GL_BGRA; - *gl_type = GL_UNSIGNED_SHORT_1_5_5_5_REV; - break; - case IMGFMT_BGR16: - *gl_format = GL_RGB; - *gl_type = GL_UNSIGNED_SHORT_5_6_5; - break; - case IMGFMT_BGR24: - *gl_format = GL_BGR; - *gl_type = GL_UNSIGNED_BYTE; - break; - case IMGFMT_BGRA: - *gl_texfmt = 4; - *gl_format = GL_BGRA; - *gl_type = GL_UNSIGNED_BYTE; - break; - default: - *gl_texfmt = 4; - *gl_format = GL_RGBA; - *gl_type = GL_UNSIGNED_BYTE; - supported = 0; - } -#ifdef TEXTUREFORMAT_ALWAYS - *gl_texfmt = TEXTUREFORMAT_ALWAYS; -#endif - return supported; -} - -#ifdef HAVE_LIBDL -#include -#endif -/** - * \brief find address of a linked function - * \param s name of function to find - * \return address of function or NULL if not found - */ -static void *getdladdr(const char *s) { - void *ret = NULL; -#ifdef HAVE_LIBDL - void *handle = dlopen(NULL, RTLD_LAZY); - if (!handle) - return NULL; - ret = dlsym(handle, s); - dlclose(handle); -#endif - return ret; -} - -typedef struct { - void *funcptr; - const char *extstr; - const char *funcnames[7]; - void *fallback; -} extfunc_desc_t; - -#define DEF_FUNC_DESC(name) {&mpgl##name, NULL, {"gl"#name, NULL}, gl ##name} -static const extfunc_desc_t extfuncs[] = { - // these aren't extension functions but we query them anyway to allow - // different "backends" with one binary - DEF_FUNC_DESC(Begin), - DEF_FUNC_DESC(End), - DEF_FUNC_DESC(Viewport), - DEF_FUNC_DESC(MatrixMode), - DEF_FUNC_DESC(LoadIdentity), - DEF_FUNC_DESC(Translated), - DEF_FUNC_DESC(Scaled), - DEF_FUNC_DESC(Ortho), - DEF_FUNC_DESC(Frustum), - DEF_FUNC_DESC(PushMatrix), - DEF_FUNC_DESC(PopMatrix), - DEF_FUNC_DESC(Clear), - DEF_FUNC_DESC(GenLists), - DEF_FUNC_DESC(DeleteLists), - DEF_FUNC_DESC(NewList), - DEF_FUNC_DESC(EndList), - DEF_FUNC_DESC(CallList), - DEF_FUNC_DESC(CallLists), - DEF_FUNC_DESC(GenTextures), - DEF_FUNC_DESC(DeleteTextures), - DEF_FUNC_DESC(TexEnvf), - DEF_FUNC_DESC(TexEnvi), - DEF_FUNC_DESC(Color4ub), - DEF_FUNC_DESC(Color3f), - DEF_FUNC_DESC(Color4f), - DEF_FUNC_DESC(ClearColor), - DEF_FUNC_DESC(ClearDepth), - DEF_FUNC_DESC(DepthFunc), - DEF_FUNC_DESC(Enable), - DEF_FUNC_DESC(Disable), - DEF_FUNC_DESC(DrawBuffer), - DEF_FUNC_DESC(DepthMask), - DEF_FUNC_DESC(BlendFunc), - DEF_FUNC_DESC(Flush), - DEF_FUNC_DESC(Finish), - DEF_FUNC_DESC(PixelStorei), - DEF_FUNC_DESC(TexImage1D), - DEF_FUNC_DESC(TexImage2D), - DEF_FUNC_DESC(TexSubImage2D), - DEF_FUNC_DESC(TexParameteri), - DEF_FUNC_DESC(TexParameterf), - DEF_FUNC_DESC(TexParameterfv), - DEF_FUNC_DESC(TexCoord2f), - DEF_FUNC_DESC(Vertex2f), - DEF_FUNC_DESC(Vertex3f), - DEF_FUNC_DESC(Normal3f), - DEF_FUNC_DESC(Lightfv), - DEF_FUNC_DESC(ColorMaterial), - DEF_FUNC_DESC(ShadeModel), - DEF_FUNC_DESC(GetIntegerv), - DEF_FUNC_DESC(ColorMask), - - // here start the real extensions - {&mpglGenBuffers, NULL, {"glGenBuffers", "glGenBuffersARB", NULL}}, - {&mpglDeleteBuffers, NULL, {"glDeleteBuffers", "glDeleteBuffersARB", NULL}}, - {&mpglBindBuffer, NULL, {"glBindBuffer", "glBindBufferARB", NULL}}, - {&mpglMapBuffer, NULL, {"glMapBuffer", "glMapBufferARB", NULL}}, - {&mpglUnmapBuffer, NULL, {"glUnmapBuffer", "glUnmapBufferARB", NULL}}, - {&mpglBufferData, NULL, {"glBufferData", "glBufferDataARB", NULL}}, - {&mpglCombinerParameterfv, "NV_register_combiners", {"glCombinerParameterfv", "glCombinerParameterfvNV", NULL}}, - {&mpglCombinerParameteri, "NV_register_combiners", {"glCombinerParameteri", "glCombinerParameteriNV", NULL}}, - {&mpglCombinerInput, "NV_register_combiners", {"glCombinerInput", "glCombinerInputNV", NULL}}, - {&mpglCombinerOutput, "NV_register_combiners", {"glCombinerOutput", "glCombinerOutputNV", NULL}}, - {&mpglBeginFragmentShader, "ATI_fragment_shader", {"glBeginFragmentShaderATI", NULL}}, - {&mpglEndFragmentShader, "ATI_fragment_shader", {"glEndFragmentShaderATI", NULL}}, - {&mpglSampleMap, "ATI_fragment_shader", {"glSampleMapATI", NULL}}, - {&mpglColorFragmentOp2, "ATI_fragment_shader", {"glColorFragmentOp2ATI", NULL}}, - {&mpglColorFragmentOp3, "ATI_fragment_shader", {"glColorFragmentOp3ATI", NULL}}, - {&mpglSetFragmentShaderConstant, "ATI_fragment_shader", {"glSetFragmentShaderConstantATI", NULL}}, - {&mpglActiveTexture, NULL, {"glActiveTexture", "glActiveTextureARB", NULL}}, - {&mpglBindTexture, NULL, {"glBindTexture", "glBindTextureARB", "glBindTextureEXT", NULL}}, - {&mpglMultiTexCoord2f, NULL, {"glMultiTexCoord2f", "glMultiTexCoord2fARB", NULL}}, - {&mpglGenPrograms, "_program", {"glGenProgramsARB", NULL}}, - {&mpglDeletePrograms, "_program", {"glDeleteProgramsARB", NULL}}, - {&mpglBindProgram, "_program", {"glBindProgramARB", NULL}}, - {&mpglProgramString, "_program", {"glProgramStringARB", NULL}}, - {&mpglGetProgramiv, "_program", {"glGetProgramivARB", NULL}}, - {&mpglProgramEnvParameter4f, "_program", {"glProgramEnvParameter4fARB", NULL}}, - {&mpglSwapInterval, "_swap_control", {"glXSwapIntervalSGI", "glXSwapInterval", "wglSwapIntervalSGI", "wglSwapInterval", "wglSwapIntervalEXT", NULL}}, - {&mpglTexImage3D, NULL, {"glTexImage3D", NULL}}, - {&mpglAllocateMemoryMESA, "GLX_MESA_allocate_memory", {"glXAllocateMemoryMESA", NULL}}, - {&mpglFreeMemoryMESA, "GLX_MESA_allocate_memory", {"glXFreeMemoryMESA", NULL}}, - {NULL} -}; - -/** - * \brief find the function pointers of some useful OpenGL extensions - * \param getProcAddress function to resolve function names, may be NULL - * \param ext2 an extra extension string - */ -static void getFunctions(void *(*getProcAddress)(const GLubyte *), - const char *ext2) { - const extfunc_desc_t *dsc; - const char *extensions; - char *allexts; - - if (!getProcAddress) - getProcAddress = (void *)getdladdr; - - // special case, we need glGetString before starting to find the other functions - mpglGetString = getProcAddress("glGetString"); - if (!mpglGetString) - mpglGetString = glGetString; - - extensions = (const char *)mpglGetString(GL_EXTENSIONS); - if (!extensions) extensions = ""; - if (!ext2) ext2 = ""; - allexts = malloc(strlen(extensions) + strlen(ext2) + 2); - strcpy(allexts, extensions); - strcat(allexts, " "); - strcat(allexts, ext2); - mp_msg(MSGT_VO, MSGL_DBG2, "OpenGL extensions string:\n%s\n", allexts); - for (dsc = extfuncs; dsc->funcptr; dsc++) { - void *ptr = NULL; - int i; - if (!dsc->extstr || strstr(allexts, dsc->extstr)) { - for (i = 0; !ptr && dsc->funcnames[i]; i++) - ptr = getProcAddress((const GLubyte *)dsc->funcnames[i]); - } - if (!ptr) - ptr = dsc->fallback; - *(void **)dsc->funcptr = ptr; - } - if (strstr(allexts, "_texture_float")) - hqtexfmt = GL_RGB32F; - else if (strstr(allexts, "NV_float_buffer")) - hqtexfmt = GL_FLOAT_RGB32_NV; - else - hqtexfmt = GL_RGB16; - free(allexts); -} - -/** - * \brief create a texture and set some defaults - * \param target texture taget, usually GL_TEXTURE_2D - * \param fmt internal texture format - * \param format texture host data format - * \param type texture host data type - * \param filter filter used for scaling, e.g. GL_LINEAR - * \param w texture width - * \param h texture height - * \param val luminance value to fill texture with - * \ingroup gltexture - */ -void glCreateClearTex(GLenum target, GLenum fmt, GLenum format, GLenum type, GLint filter, - int w, int h, unsigned char val) { - GLfloat fval = (GLfloat)val / 255.0; - GLfloat border[4] = {fval, fval, fval, fval}; - int stride; - char *init; - if (w == 0) w = 1; - if (h == 0) h = 1; - stride = w * glFmt2bpp(format, type); - if (!stride) return; - init = malloc(stride * h); - memset(init, val, stride * h); - glAdjustAlignment(stride); - mpglPixelStorei(GL_UNPACK_ROW_LENGTH, w); - mpglTexImage2D(target, 0, fmt, w, h, 0, format, type, init); - mpglTexParameterf(target, GL_TEXTURE_PRIORITY, 1.0); - mpglTexParameteri(target, GL_TEXTURE_MIN_FILTER, filter); - mpglTexParameteri(target, GL_TEXTURE_MAG_FILTER, filter); - mpglTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - mpglTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - // Border texels should not be used with CLAMP_TO_EDGE - // We set a sane default anyway. - mpglTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, border); - free(init); -} - -/** - * \brief creates a texture from a PPM file - * \param target texture taget, usually GL_TEXTURE_2D - * \param fmt internal texture format, 0 for default - * \param filter filter used for scaling, e.g. GL_LINEAR - * \param f file to read PPM from - * \param width [out] width of texture - * \param height [out] height of texture - * \param maxval [out] maxval value from PPM file - * \return 0 on error, 1 otherwise - * \ingroup gltexture - */ -int glCreatePPMTex(GLenum target, GLenum fmt, GLint filter, - FILE *f, int *width, int *height, int *maxval) { - int w, h, m, bpp; - GLenum type; - uint8_t *data = read_pnm(f, &w, &h, &bpp, &m); - if (!data || (bpp != 3 && bpp != 6)) { - free(data); - return 0; - } - if (!fmt) { - fmt = bpp == 6 ? hqtexfmt : 3; - if (fmt == GL_FLOAT_RGB32_NV && target != GL_TEXTURE_RECTANGLE) - fmt = GL_RGB16; - } - type = bpp == 6 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE; - glCreateClearTex(target, fmt, GL_RGB, type, filter, w, h, 0); - glUploadTex(target, GL_RGB, type, - data, w * bpp, 0, 0, w, h, 0); - free(data); - if (width) *width = w; - if (height) *height = h; - if (maxval) *maxval = m; - return 1; -} - -/** - * \brief return the number of bytes per pixel for the given format - * \param format OpenGL format - * \param type OpenGL type - * \return bytes per pixel - * \ingroup glgeneral - * - * Does not handle all possible variants, just those used by MPlayer - */ -int glFmt2bpp(GLenum format, GLenum type) { - int component_size = 0; - switch (type) { - case GL_UNSIGNED_BYTE_3_3_2: - case GL_UNSIGNED_BYTE_2_3_3_REV: - return 1; - case GL_UNSIGNED_SHORT_5_5_5_1: - case GL_UNSIGNED_SHORT_1_5_5_5_REV: - case GL_UNSIGNED_SHORT_5_6_5: - case GL_UNSIGNED_SHORT_5_6_5_REV: - return 2; - case GL_UNSIGNED_BYTE: - component_size = 1; - break; - case GL_UNSIGNED_SHORT: - component_size = 2; - break; - } - switch (format) { - case GL_LUMINANCE: - case GL_ALPHA: - return component_size; - case GL_YCBCR_MESA: - return 2; - case GL_RGB: - case GL_BGR: - return 3 * component_size; - case GL_RGBA: - case GL_BGRA: - return 4 * component_size; - } - return 0; // unknown -} - -/** - * \brief upload a texture, handling things like stride and slices - * \param target texture target, usually GL_TEXTURE_2D - * \param format OpenGL format of data - * \param type OpenGL type of data - * \param dataptr data to upload - * \param stride data stride - * \param x x offset in texture - * \param y y offset in texture - * \param w width of the texture part to upload - * \param h height of the texture part to upload - * \param slice height of an upload slice, 0 for all at once - * \ingroup gltexture - */ -void glUploadTex(GLenum target, GLenum format, GLenum type, - const void *dataptr, int stride, - int x, int y, int w, int h, int slice) { - const uint8_t *data = dataptr; - int y_max = y + h; - if (w <= 0 || h <= 0) return; - if (slice <= 0) - slice = h; - if (stride < 0) { - data += (h - 1) * stride; - stride = -stride; - } - // this is not always correct, but should work for MPlayer - glAdjustAlignment(stride); - mpglPixelStorei(GL_UNPACK_ROW_LENGTH, stride / glFmt2bpp(format, type)); - for (; y + slice <= y_max; y += slice) { - mpglTexSubImage2D(target, 0, x, y, w, slice, format, type, data); - data += stride * slice; - } - if (y < y_max) - mpglTexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data); -} - -static void fillUVcoeff(GLfloat *ucoef, GLfloat *vcoef, - float uvcos, float uvsin) { - int i; - ucoef[0] = 0 * uvcos + 1.403 * uvsin; - vcoef[0] = 0 * uvsin + 1.403 * uvcos; - ucoef[1] = -0.344 * uvcos + -0.714 * uvsin; - vcoef[1] = -0.344 * uvsin + -0.714 * uvcos; - ucoef[2] = 1.770 * uvcos + 0 * uvsin; - vcoef[2] = 1.770 * uvsin + 0 * uvcos; - ucoef[3] = 0; - vcoef[3] = 0; - // Coefficients (probably) must be in [0, 1] range, whereas they originally - // are in [-2, 2] range, so here comes the trick: - // First put them in the [-0.5, 0.5] range, then add 0.5. - // This can be undone with the HALF_BIAS and SCALE_BY_FOUR arguments - // for CombinerInput and CombinerOutput (or the respective ATI variants) - for (i = 0; i < 4; i++) { - ucoef[i] = ucoef[i] * 0.25 + 0.5; - vcoef[i] = vcoef[i] * 0.25 + 0.5; - } -} - -/** - * \brief Setup register combiners for YUV to RGB conversion. - * \param uvcos used for saturation and hue adjustment - * \param uvsin used for saturation and hue adjustment - */ -static void glSetupYUVCombiners(float uvcos, float uvsin) { - GLfloat ucoef[4]; - GLfloat vcoef[4]; - GLint i; - if (!mpglCombinerInput || !mpglCombinerOutput || - !mpglCombinerParameterfv || !mpglCombinerParameteri) { - mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Combiner functions missing!\n"); - return; - } - mpglGetIntegerv(GL_MAX_GENERAL_COMBINERS_NV, &i); - if (i < 2) - mp_msg(MSGT_VO, MSGL_ERR, - "[gl] 2 general combiners needed for YUV combiner support (found %i)\n", i); - mpglGetIntegerv(GL_MAX_TEXTURE_UNITS, &i); - if (i < 3) - mp_msg(MSGT_VO, MSGL_ERR, - "[gl] 3 texture units needed for YUV combiner support (found %i)\n", i); - fillUVcoeff(ucoef, vcoef, uvcos, uvsin); - mpglCombinerParameterfv(GL_CONSTANT_COLOR0_NV, ucoef); - mpglCombinerParameterfv(GL_CONSTANT_COLOR1_NV, vcoef); - - // UV first, like this green component cannot overflow - mpglCombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, - GL_TEXTURE1, GL_HALF_BIAS_NORMAL_NV, GL_RGB); - mpglCombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, - GL_CONSTANT_COLOR0_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB); - mpglCombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, - GL_TEXTURE2, GL_HALF_BIAS_NORMAL_NV, GL_RGB); - mpglCombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, - GL_CONSTANT_COLOR1_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB); - mpglCombinerOutput(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, - GL_SPARE0_NV, GL_SCALE_BY_FOUR_NV, GL_NONE, GL_FALSE, - GL_FALSE, GL_FALSE); - - // stage 2 - mpglCombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE0_NV, - GL_SIGNED_IDENTITY_NV, GL_RGB); - mpglCombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO, - GL_UNSIGNED_INVERT_NV, GL_RGB); - mpglCombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV, - GL_TEXTURE0, GL_SIGNED_IDENTITY_NV, GL_RGB); - mpglCombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, - GL_UNSIGNED_INVERT_NV, GL_RGB); - mpglCombinerOutput(GL_COMBINER1_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, - GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, - GL_FALSE, GL_FALSE); - - // leave final combiner stage in default mode - mpglCombinerParameteri(GL_NUM_GENERAL_COMBINERS_NV, 2); -} - -/** - * \brief Setup ATI version of register combiners for YUV to RGB conversion. - * \param csp_params parameters used for colorspace conversion - * \param text if set use the GL_ATI_text_fragment_shader API as - * used on OS X. - */ -static void glSetupYUVFragmentATI(struct mp_csp_params *csp_params, - int text) { - GLint i; - float yuv2rgb[3][4]; - - mpglGetIntegerv (GL_MAX_TEXTURE_UNITS, &i); - if (i < 3) - mp_msg(MSGT_VO, MSGL_ERR, - "[gl] 3 texture units needed for YUV combiner (ATI) support (found %i)\n", i); - - mp_get_yuv2rgb_coeffs(csp_params, yuv2rgb); - for (i = 0; i < 3; i++) { - int j; - yuv2rgb[i][3] -= -0.5 * (yuv2rgb[i][1] + yuv2rgb[i][2]); - for (j = 0; j < 4; j++) { - yuv2rgb[i][j] *= 0.125; - yuv2rgb[i][j] += 0.5; - if (yuv2rgb[i][j] > 1) - yuv2rgb[i][j] = 1; - if (yuv2rgb[i][j] < 0) - yuv2rgb[i][j] = 0; - } - } - if (text == 0) { - GLfloat c0[4] = {yuv2rgb[0][0], yuv2rgb[1][0], yuv2rgb[2][0]}; - GLfloat c1[4] = {yuv2rgb[0][1], yuv2rgb[1][1], yuv2rgb[2][1]}; - GLfloat c2[4] = {yuv2rgb[0][2], yuv2rgb[1][2], yuv2rgb[2][2]}; - GLfloat c3[4] = {yuv2rgb[0][3], yuv2rgb[1][3], yuv2rgb[2][3]}; - if (!mpglBeginFragmentShader || !mpglEndFragmentShader || - !mpglSetFragmentShaderConstant || !mpglSampleMap || - !mpglColorFragmentOp2 || !mpglColorFragmentOp3) { - mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Combiner (ATI) functions missing!\n"); - return; - } - mpglGetIntegerv(GL_NUM_FRAGMENT_REGISTERS_ATI, &i); - if (i < 3) - mp_msg(MSGT_VO, MSGL_ERR, - "[gl] 3 registers needed for YUV combiner (ATI) support (found %i)\n", i); - mpglBeginFragmentShader(); - mpglSetFragmentShaderConstant(GL_CON_0_ATI, c0); - mpglSetFragmentShaderConstant(GL_CON_1_ATI, c1); - mpglSetFragmentShaderConstant(GL_CON_2_ATI, c2); - mpglSetFragmentShaderConstant(GL_CON_3_ATI, c3); - mpglSampleMap(GL_REG_0_ATI, GL_TEXTURE0, GL_SWIZZLE_STR_ATI); - mpglSampleMap(GL_REG_1_ATI, GL_TEXTURE1, GL_SWIZZLE_STR_ATI); - mpglSampleMap(GL_REG_2_ATI, GL_TEXTURE2, GL_SWIZZLE_STR_ATI); - mpglColorFragmentOp2(GL_MUL_ATI, GL_REG_1_ATI, GL_NONE, GL_NONE, - GL_REG_1_ATI, GL_NONE, GL_BIAS_BIT_ATI, - GL_CON_1_ATI, GL_NONE, GL_BIAS_BIT_ATI); - mpglColorFragmentOp3(GL_MAD_ATI, GL_REG_2_ATI, GL_NONE, GL_NONE, - GL_REG_2_ATI, GL_NONE, GL_BIAS_BIT_ATI, - GL_CON_2_ATI, GL_NONE, GL_BIAS_BIT_ATI, - GL_REG_1_ATI, GL_NONE, GL_NONE); - mpglColorFragmentOp3(GL_MAD_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE, - GL_REG_0_ATI, GL_NONE, GL_NONE, - GL_CON_0_ATI, GL_NONE, GL_BIAS_BIT_ATI, - GL_REG_2_ATI, GL_NONE, GL_NONE); - mpglColorFragmentOp2(GL_ADD_ATI, GL_REG_0_ATI, GL_NONE, GL_8X_BIT_ATI, - GL_REG_0_ATI, GL_NONE, GL_NONE, - GL_CON_3_ATI, GL_NONE, GL_BIAS_BIT_ATI); - mpglEndFragmentShader(); - } else { - static const char template[] = - "!!ATIfs1.0\n" - "StartConstants;\n" - " CONSTANT c0 = {%e, %e, %e};\n" - " CONSTANT c1 = {%e, %e, %e};\n" - " CONSTANT c2 = {%e, %e, %e};\n" - " CONSTANT c3 = {%e, %e, %e};\n" - "EndConstants;\n" - "StartOutputPass;\n" - " SampleMap r0, t0.str;\n" - " SampleMap r1, t1.str;\n" - " SampleMap r2, t2.str;\n" - " MUL r1.rgb, r1.bias, c1.bias;\n" - " MAD r2.rgb, r2.bias, c2.bias, r1;\n" - " MAD r0.rgb, r0, c0.bias, r2;\n" - " ADD r0.rgb.8x, r0, c3.bias;\n" - "EndPass;\n"; - char buffer[512]; - snprintf(buffer, sizeof(buffer), template, - yuv2rgb[0][0], yuv2rgb[1][0], yuv2rgb[2][0], - yuv2rgb[0][1], yuv2rgb[1][1], yuv2rgb[2][1], - yuv2rgb[0][2], yuv2rgb[1][2], yuv2rgb[2][2], - yuv2rgb[0][3], yuv2rgb[1][3], yuv2rgb[2][3]); - mp_msg(MSGT_VO, MSGL_DBG2, "[gl] generated fragment program:\n%s\n", buffer); - loadGPUProgram(GL_TEXT_FRAGMENT_SHADER_ATI, buffer); - } -} - -/** - * \brief helper function for gen_spline_lookup_tex - * \param x subpixel-position ((0,1) range) to calculate weights for - * \param dst where to store transformed weights, must provide space for 4 GLfloats - * - * calculates the weights and stores them after appropriate transformation - * for the scaler fragment program. - */ -static void store_weights(float x, GLfloat *dst) { - float w0 = (((-1 * x + 3) * x - 3) * x + 1) / 6; - float w1 = ((( 3 * x - 6) * x + 0) * x + 4) / 6; - float w2 = (((-3 * x + 3) * x + 3) * x + 1) / 6; - float w3 = ((( 1 * x + 0) * x + 0) * x + 0) / 6; - *dst++ = 1 + x - w1 / (w0 + w1); - *dst++ = 1 - x + w3 / (w2 + w3); - *dst++ = w0 + w1; - *dst++ = 0; -} - -//! to avoid artefacts this should be rather large -#define LOOKUP_BSPLINE_RES (2 * 1024) -/** - * \brief creates the 1D lookup texture needed for fast higher-order filtering - * \param unit texture unit to attach texture to - */ -static void gen_spline_lookup_tex(GLenum unit) { - GLfloat *tex = calloc(4 * LOOKUP_BSPLINE_RES, sizeof(*tex)); - GLfloat *tp = tex; - int i; - for (i = 0; i < LOOKUP_BSPLINE_RES; i++) { - float x = (float)(i + 0.5) / LOOKUP_BSPLINE_RES; - store_weights(x, tp); - tp += 4; - } - store_weights(0, tex); - store_weights(1, &tex[4 * (LOOKUP_BSPLINE_RES - 1)]); - mpglActiveTexture(unit); - mpglTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16, LOOKUP_BSPLINE_RES, 0, GL_RGBA, GL_FLOAT, tex); - mpglTexParameterf(GL_TEXTURE_1D, GL_TEXTURE_PRIORITY, 1.0); - mpglTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - mpglTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - mpglTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); - mpglActiveTexture(GL_TEXTURE0); - free(tex); -} - -static const char *bilin_filt_template = - "TEX yuv.%c, fragment.texcoord[%c], texture[%c], %s;\n"; - -#define BICUB_FILT_MAIN(textype) \ - /* first y-interpolation */ \ - "ADD coord, fragment.texcoord[%c].xyxy, cdelta.xyxw;\n" \ - "ADD coord2, fragment.texcoord[%c].xyxy, cdelta.zyzw;\n" \ - "TEX a.r, coord.xyxy, texture[%c], "textype";\n" \ - "TEX a.g, coord.zwzw, texture[%c], "textype";\n" \ - /* second y-interpolation */ \ - "TEX b.r, coord2.xyxy, texture[%c], "textype";\n" \ - "TEX b.g, coord2.zwzw, texture[%c], "textype";\n" \ - "LRP a.b, parmy.b, a.rrrr, a.gggg;\n" \ - "LRP a.a, parmy.b, b.rrrr, b.gggg;\n" \ - /* x-interpolation */ \ - "LRP yuv.%c, parmx.b, a.bbbb, a.aaaa;\n" - -static const char *bicub_filt_template_2D = - "MAD coord.xy, fragment.texcoord[%c], {%e, %e}, {0.5, 0.5};\n" - "TEX parmx, coord.x, texture[%c], 1D;\n" - "MUL cdelta.xz, parmx.rrgg, {-%e, 0, %e, 0};\n" - "TEX parmy, coord.y, texture[%c], 1D;\n" - "MUL cdelta.yw, parmy.rrgg, {0, -%e, 0, %e};\n" - BICUB_FILT_MAIN("2D"); - -static const char *bicub_filt_template_RECT = - "ADD coord, fragment.texcoord[%c], {0.5, 0.5};\n" - "TEX parmx, coord.x, texture[%c], 1D;\n" - "MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};\n" - "TEX parmy, coord.y, texture[%c], 1D;\n" - "MUL cdelta.yw, parmy.rrgg, {0, -1, 0, 1};\n" - BICUB_FILT_MAIN("RECT"); - -#define CALCWEIGHTS(t, s) \ - "MAD "t", {-0.5, 0.1666, 0.3333, -0.3333}, "s", {1, 0, -0.5, 0.5};\n" \ - "MAD "t", "t", "s", {0, 0, -0.5, 0.5};\n" \ - "MAD "t", "t", "s", {-0.6666, 0, 0.8333, 0.1666};\n" \ - "RCP a.x, "t".z;\n" \ - "RCP a.y, "t".w;\n" \ - "MAD "t".xy, "t".xyxy, a.xyxy, {1, 1, 0, 0};\n" \ - "ADD "t".x, "t".xxxx, "s";\n" \ - "SUB "t".y, "t".yyyy, "s";\n" - -static const char *bicub_notex_filt_template_2D = - "MAD coord.xy, fragment.texcoord[%c], {%e, %e}, {0.5, 0.5};\n" - "FRC coord.xy, coord.xyxy;\n" - CALCWEIGHTS("parmx", "coord.xxxx") - "MUL cdelta.xz, parmx.rrgg, {-%e, 0, %e, 0};\n" - CALCWEIGHTS("parmy", "coord.yyyy") - "MUL cdelta.yw, parmy.rrgg, {0, -%e, 0, %e};\n" - BICUB_FILT_MAIN("2D"); - -static const char *bicub_notex_filt_template_RECT = - "ADD coord, fragment.texcoord[%c], {0.5, 0.5};\n" - "FRC coord.xy, coord.xyxy;\n" - CALCWEIGHTS("parmx", "coord.xxxx") - "MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};\n" - CALCWEIGHTS("parmy", "coord.yyyy") - "MUL cdelta.yw, parmy.rrgg, {0, -1, 0, 1};\n" - BICUB_FILT_MAIN("RECT"); - -#define BICUB_X_FILT_MAIN(textype) \ - "ADD coord.xy, fragment.texcoord[%c].xyxy, cdelta.xyxy;\n" \ - "ADD coord2.xy, fragment.texcoord[%c].xyxy, cdelta.zyzy;\n" \ - "TEX a.r, coord, texture[%c], "textype";\n" \ - "TEX b.r, coord2, texture[%c], "textype";\n" \ - /* x-interpolation */ \ - "LRP yuv.%c, parmx.b, a.rrrr, b.rrrr;\n" - -static const char *bicub_x_filt_template_2D = - "MAD coord.x, fragment.texcoord[%c], {%e}, {0.5};\n" - "TEX parmx, coord, texture[%c], 1D;\n" - "MUL cdelta.xyz, parmx.rrgg, {-%e, 0, %e};\n" - BICUB_X_FILT_MAIN("2D"); - -static const char *bicub_x_filt_template_RECT = - "ADD coord.x, fragment.texcoord[%c], {0.5};\n" - "TEX parmx, coord, texture[%c], 1D;\n" - "MUL cdelta.xyz, parmx.rrgg, {-1, 0, 1};\n" - BICUB_X_FILT_MAIN("RECT"); - -static const char *unsharp_filt_template = - "PARAM dcoord%c = {%e, %e, %e, %e};\n" - "ADD coord, fragment.texcoord[%c].xyxy, dcoord%c;\n" - "SUB coord2, fragment.texcoord[%c].xyxy, dcoord%c;\n" - "TEX a.r, fragment.texcoord[%c], texture[%c], %s;\n" - "TEX b.r, coord.xyxy, texture[%c], %s;\n" - "TEX b.g, coord.zwzw, texture[%c], %s;\n" - "ADD b.r, b.r, b.g;\n" - "TEX b.b, coord2.xyxy, texture[%c], %s;\n" - "TEX b.g, coord2.zwzw, texture[%c], %s;\n" - "DP3 b, b, {0.25, 0.25, 0.25};\n" - "SUB b.r, a.r, b.r;\n" - "MAD yuv.%c, b.r, {%e}, a.r;\n"; - -static const char *unsharp_filt_template2 = - "PARAM dcoord%c = {%e, %e, %e, %e};\n" - "PARAM dcoord2%c = {%e, 0, 0, %e};\n" - "ADD coord, fragment.texcoord[%c].xyxy, dcoord%c;\n" - "SUB coord2, fragment.texcoord[%c].xyxy, dcoord%c;\n" - "TEX a.r, fragment.texcoord[%c], texture[%c], %s;\n" - "TEX b.r, coord.xyxy, texture[%c], %s;\n" - "TEX b.g, coord.zwzw, texture[%c], %s;\n" - "ADD b.r, b.r, b.g;\n" - "TEX b.b, coord2.xyxy, texture[%c], %s;\n" - "TEX b.g, coord2.zwzw, texture[%c], %s;\n" - "ADD b.r, b.r, b.b;\n" - "ADD b.a, b.r, b.g;\n" - "ADD coord, fragment.texcoord[%c].xyxy, dcoord2%c;\n" - "SUB coord2, fragment.texcoord[%c].xyxy, dcoord2%c;\n" - "TEX b.r, coord.xyxy, texture[%c], %s;\n" - "TEX b.g, coord.zwzw, texture[%c], %s;\n" - "ADD b.r, b.r, b.g;\n" - "TEX b.b, coord2.xyxy, texture[%c], %s;\n" - "TEX b.g, coord2.zwzw, texture[%c], %s;\n" - "DP4 b.r, b, {-0.1171875, -0.1171875, -0.1171875, -0.09765625};\n" - "MAD b.r, a.r, {0.859375}, b.r;\n" - "MAD yuv.%c, b.r, {%e}, a.r;\n"; - -static const char *yuv_prog_template = - "PARAM ycoef = {%e, %e, %e};\n" - "PARAM ucoef = {%e, %e, %e};\n" - "PARAM vcoef = {%e, %e, %e};\n" - "PARAM offsets = {%e, %e, %e};\n" - "TEMP res;\n" - "MAD res.rgb, yuv.rrrr, ycoef, offsets;\n" - "MAD res.rgb, yuv.gggg, ucoef, res;\n" - "MAD result.color.rgb, yuv.bbbb, vcoef, res;\n" - "END"; - -static const char *yuv_pow_prog_template = - "PARAM ycoef = {%e, %e, %e};\n" - "PARAM ucoef = {%e, %e, %e};\n" - "PARAM vcoef = {%e, %e, %e};\n" - "PARAM offsets = {%e, %e, %e};\n" - "PARAM gamma = {%e, %e, %e};\n" - "TEMP res;\n" - "MAD res.rgb, yuv.rrrr, ycoef, offsets;\n" - "MAD res.rgb, yuv.gggg, ucoef, res;\n" - "MAD_SAT res.rgb, yuv.bbbb, vcoef, res;\n" - "POW result.color.r, res.r, gamma.r;\n" - "POW result.color.g, res.g, gamma.g;\n" - "POW result.color.b, res.b, gamma.b;\n" - "END"; - -static const char *yuv_lookup_prog_template = - "PARAM ycoef = {%e, %e, %e, 0};\n" - "PARAM ucoef = {%e, %e, %e, 0};\n" - "PARAM vcoef = {%e, %e, %e, 0};\n" - "PARAM offsets = {%e, %e, %e, 0.125};\n" - "TEMP res;\n" - "MAD res, yuv.rrrr, ycoef, offsets;\n" - "MAD res.rgb, yuv.gggg, ucoef, res;\n" - "MAD res.rgb, yuv.bbbb, vcoef, res;\n" - "TEX result.color.r, res.raaa, texture[%c], 2D;\n" - "ADD res.a, res.a, 0.25;\n" - "TEX result.color.g, res.gaaa, texture[%c], 2D;\n" - "ADD res.a, res.a, 0.25;\n" - "TEX result.color.b, res.baaa, texture[%c], 2D;\n" - "END"; - -static const char *yuv_lookup3d_prog_template = - "TEX result.color, yuv, texture[%c], 3D;\n" - "END"; - -/** - * \brief creates and initializes helper textures needed for scaling texture read - * \param scaler scaler type to create texture for - * \param texu contains next free texture unit number - * \param texs texture unit ids for the scaler are stored in this array - */ -static void create_scaler_textures(int scaler, int *texu, char *texs) { - switch (scaler) { - case YUV_SCALER_BILIN: - case YUV_SCALER_BICUB_NOTEX: - case YUV_SCALER_UNSHARP: - case YUV_SCALER_UNSHARP2: - break; - case YUV_SCALER_BICUB: - case YUV_SCALER_BICUB_X: - texs[0] = (*texu)++; - gen_spline_lookup_tex(GL_TEXTURE0 + texs[0]); - texs[0] += '0'; - break; - default: - mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown scaler type %i\n", scaler); - } -} - -//! resolution of texture for gamma lookup table -#define LOOKUP_RES 512 -//! resolution for 3D yuv->rgb conversion lookup table -#define LOOKUP_3DRES 32 -/** - * \brief creates and initializes helper textures needed for yuv conversion - * \param params struct containing parameters like brightness, gamma, ... - * \param texu contains next free texture unit number - * \param texs texture unit ids for the conversion are stored in this array - */ -static void create_conv_textures(gl_conversion_params_t *params, int *texu, char *texs) { - unsigned char *lookup_data = NULL; - int conv = YUV_CONVERSION(params->type); - switch (conv) { - case YUV_CONVERSION_FRAGMENT: - case YUV_CONVERSION_FRAGMENT_POW: - break; - case YUV_CONVERSION_FRAGMENT_LOOKUP: - texs[0] = (*texu)++; - mpglActiveTexture(GL_TEXTURE0 + texs[0]); - lookup_data = malloc(4 * LOOKUP_RES); - mp_gen_gamma_map(lookup_data, LOOKUP_RES, params->csp_params.rgamma); - mp_gen_gamma_map(&lookup_data[LOOKUP_RES], LOOKUP_RES, params->csp_params.ggamma); - mp_gen_gamma_map(&lookup_data[2 * LOOKUP_RES], LOOKUP_RES, params->csp_params.bgamma); - glCreateClearTex(GL_TEXTURE_2D, GL_LUMINANCE8, GL_LUMINANCE, GL_UNSIGNED_BYTE, GL_LINEAR, - LOOKUP_RES, 4, 0); - glUploadTex(GL_TEXTURE_2D, GL_LUMINANCE, GL_UNSIGNED_BYTE, lookup_data, - LOOKUP_RES, 0, 0, LOOKUP_RES, 4, 0); - mpglActiveTexture(GL_TEXTURE0); - texs[0] += '0'; - break; - case YUV_CONVERSION_FRAGMENT_LOOKUP3D: - { - int sz = LOOKUP_3DRES + 2; // texture size including borders - if (!mpglTexImage3D) { - mp_msg(MSGT_VO, MSGL_ERR, "[gl] Missing 3D texture function!\n"); - break; - } - texs[0] = (*texu)++; - mpglActiveTexture(GL_TEXTURE0 + texs[0]); - lookup_data = malloc(3 * sz * sz * sz); - mp_gen_yuv2rgb_map(¶ms->csp_params, lookup_data, LOOKUP_3DRES); - glAdjustAlignment(sz); - mpglPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - mpglTexImage3D(GL_TEXTURE_3D, 0, 3, sz, sz, sz, 1, - GL_RGB, GL_UNSIGNED_BYTE, lookup_data); - mpglTexParameterf(GL_TEXTURE_3D, GL_TEXTURE_PRIORITY, 1.0); - mpglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - mpglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - mpglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP); - mpglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP); - mpglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP); - mpglActiveTexture(GL_TEXTURE0); - texs[0] += '0'; - } - break; - default: - mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown conversion type %i\n", conv); - } - free(lookup_data); -} - -/** - * \brief adds a scaling texture read at the current fragment program position - * \param scaler type of scaler to insert - * \param prog_pos current position in fragment program - * \param remain how many bytes remain in the buffer given by prog_pos - * \param texs array containing the texture unit identifiers for this scaler - * \param in_tex texture unit the scaler should read from - * \param out_comp component of the yuv variable the scaler stores the result in - * \param rect if rectangular (pixel) adressing should be used for in_tex - * \param texw width of the in_tex texture - * \param texh height of the in_tex texture - * \param strength strength of filter effect if the scaler does some kind of filtering - */ -static void add_scaler(int scaler, char **prog_pos, int *remain, char *texs, - char in_tex, char out_comp, int rect, int texw, int texh, - double strength) { - const char *ttype = rect ? "RECT" : "2D"; - const float ptw = rect ? 1.0 : 1.0 / texw; - const float pth = rect ? 1.0 : 1.0 / texh; - switch (scaler) { - case YUV_SCALER_BILIN: - snprintf(*prog_pos, *remain, bilin_filt_template, out_comp, in_tex, - in_tex, ttype); - break; - case YUV_SCALER_BICUB: - if (rect) - snprintf(*prog_pos, *remain, bicub_filt_template_RECT, - in_tex, texs[0], texs[0], - in_tex, in_tex, in_tex, in_tex, in_tex, in_tex, out_comp); - else - snprintf(*prog_pos, *remain, bicub_filt_template_2D, - in_tex, (float)texw, (float)texh, - texs[0], ptw, ptw, texs[0], pth, pth, - in_tex, in_tex, in_tex, in_tex, in_tex, in_tex, out_comp); - break; - case YUV_SCALER_BICUB_X: - if (rect) - snprintf(*prog_pos, *remain, bicub_x_filt_template_RECT, - in_tex, texs[0], - in_tex, in_tex, in_tex, in_tex, out_comp); - else - snprintf(*prog_pos, *remain, bicub_x_filt_template_2D, - in_tex, (float)texw, - texs[0], ptw, ptw, - in_tex, in_tex, in_tex, in_tex, out_comp); - break; - case YUV_SCALER_BICUB_NOTEX: - if (rect) - snprintf(*prog_pos, *remain, bicub_notex_filt_template_RECT, - in_tex, - in_tex, in_tex, in_tex, in_tex, in_tex, in_tex, out_comp); - else - snprintf(*prog_pos, *remain, bicub_notex_filt_template_2D, - in_tex, (float)texw, (float)texh, ptw, ptw, pth, pth, - in_tex, in_tex, in_tex, in_tex, in_tex, in_tex, out_comp); - break; - case YUV_SCALER_UNSHARP: - snprintf(*prog_pos, *remain, unsharp_filt_template, - out_comp, 0.5 * ptw, 0.5 * pth, 0.5 * ptw, -0.5 * pth, - in_tex, out_comp, in_tex, out_comp, in_tex, - in_tex, ttype, in_tex, ttype, in_tex, ttype, in_tex, ttype, - in_tex, ttype, out_comp, strength); - break; - case YUV_SCALER_UNSHARP2: - snprintf(*prog_pos, *remain, unsharp_filt_template2, - out_comp, 1.2 * ptw, 1.2 * pth, 1.2 * ptw, -1.2 * pth, - out_comp, 1.5 * ptw, 1.5 * pth, - in_tex, out_comp, in_tex, out_comp, in_tex, - in_tex, ttype, in_tex, ttype, in_tex, ttype, in_tex, ttype, - in_tex, ttype, in_tex, out_comp, in_tex, out_comp, - in_tex, ttype, in_tex, ttype, in_tex, ttype, - in_tex, ttype, out_comp, strength); - break; - } - *remain -= strlen(*prog_pos); - *prog_pos += strlen(*prog_pos); -} - -static const struct { - const char *name; - GLenum cur; - GLenum max; -} progstats[] = { - {"instructions", 0x88A0, 0x88A1}, - {"native instructions", 0x88A2, 0x88A3}, - {"temporaries", 0x88A4, 0x88A5}, - {"native temporaries", 0x88A6, 0x88A7}, - {"parameters", 0x88A8, 0x88A9}, - {"native parameters", 0x88AA, 0x88AB}, - {"attribs", 0x88AC, 0x88AD}, - {"native attribs", 0x88AE, 0x88AF}, - {"ALU instructions", 0x8805, 0x880B}, - {"TEX instructions", 0x8806, 0x880C}, - {"TEX indirections", 0x8807, 0x880D}, - {"native ALU instructions", 0x8808, 0x880E}, - {"native TEX instructions", 0x8809, 0x880F}, - {"native TEX indirections", 0x880A, 0x8810}, - {NULL, 0, 0} -}; - -/** - * \brief load the specified GPU Program - * \param target program target to load into, only GL_FRAGMENT_PROGRAM is tested - * \param prog program string - * \return 1 on success, 0 otherwise - */ -int loadGPUProgram(GLenum target, char *prog) { - int i; - GLint cur = 0, max = 0, err = 0; - if (!mpglProgramString) { - mp_msg(MSGT_VO, MSGL_ERR, "[gl] Missing GPU program function\n"); - return 0; - } - mpglProgramString(target, GL_PROGRAM_FORMAT_ASCII, strlen(prog), prog); - mpglGetIntegerv(GL_PROGRAM_ERROR_POSITION, &err); - if (err != -1) { - mp_msg(MSGT_VO, MSGL_ERR, - "[gl] Error compiling fragment program, make sure your card supports\n" - "[gl] GL_ARB_fragment_program (use glxinfo to check).\n" - "[gl] Error message:\n %s at %.10s\n", - mpglGetString(GL_PROGRAM_ERROR_STRING), &prog[err]); - return 0; - } - if (!mpglGetProgramiv || !mp_msg_test(MSGT_VO, MSGL_DBG2)) - return 1; - mp_msg(MSGT_VO, MSGL_V, "[gl] Program statistics:\n"); - for (i = 0; progstats[i].name; i++) { - mpglGetProgramiv(target, progstats[i].cur, &cur); - mpglGetProgramiv(target, progstats[i].max, &max); - mp_msg(MSGT_VO, MSGL_V, "[gl] %s: %i/%i\n", progstats[i].name, cur, max); - } - return 1; -} - -#define MAX_PROGSZ (1024*1024) - -/** - * \brief setup a fragment program that will do YUV->RGB conversion - * \param parms struct containing parameters like conversion and scaler type, - * brightness, ... - */ -static void glSetupYUVFragprog(gl_conversion_params_t *params) { - int type = params->type; - int texw = params->texw; - int texh = params->texh; - int rect = params->target == GL_TEXTURE_RECTANGLE; - static const char prog_hdr[] = - "!!ARBfp1.0\n" - "OPTION ARB_precision_hint_fastest;\n" - // all scaler variables must go here so they aren't defined - // multiple times when the same scaler is used more than once - "TEMP coord, coord2, cdelta, parmx, parmy, a, b, yuv;\n"; - int prog_remain; - char *yuv_prog, *prog_pos; - int cur_texu = 3; - char lum_scale_texs[1]; - char chrom_scale_texs[1]; - char conv_texs[1]; - GLint i; - // this is the conversion matrix, with y, u, v factors - // for red, green, blue and the constant offsets - float yuv2rgb[3][4]; - create_conv_textures(params, &cur_texu, conv_texs); - create_scaler_textures(YUV_LUM_SCALER(type), &cur_texu, lum_scale_texs); - if (YUV_CHROM_SCALER(type) == YUV_LUM_SCALER(type)) - memcpy(chrom_scale_texs, lum_scale_texs, sizeof(chrom_scale_texs)); - else - create_scaler_textures(YUV_CHROM_SCALER(type), &cur_texu, chrom_scale_texs); - mpglGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &i); - if (i < cur_texu) - mp_msg(MSGT_VO, MSGL_ERR, - "[gl] %i texture units needed for this type of YUV fragment support (found %i)\n", - cur_texu, i); - if (!mpglProgramString) { - mp_msg(MSGT_VO, MSGL_FATAL, "[gl] ProgramString function missing!\n"); - return; - } - yuv_prog = malloc(MAX_PROGSZ); - strcpy(yuv_prog, prog_hdr); - prog_pos = yuv_prog + sizeof(prog_hdr) - 1; - prog_remain = MAX_PROGSZ - sizeof(prog_hdr); - add_scaler(YUV_LUM_SCALER(type), &prog_pos, &prog_remain, lum_scale_texs, - '0', 'r', rect, texw, texh, params->filter_strength); - add_scaler(YUV_CHROM_SCALER(type), &prog_pos, &prog_remain, chrom_scale_texs, - '1', 'g', rect, params->chrom_texw, params->chrom_texh, params->filter_strength); - add_scaler(YUV_CHROM_SCALER(type), &prog_pos, &prog_remain, chrom_scale_texs, - '2', 'b', rect, params->chrom_texw, params->chrom_texh, params->filter_strength); - mp_get_yuv2rgb_coeffs(¶ms->csp_params, yuv2rgb); - switch (YUV_CONVERSION(type)) { - case YUV_CONVERSION_FRAGMENT: - snprintf(prog_pos, prog_remain, yuv_prog_template, - yuv2rgb[ROW_R][COL_Y], yuv2rgb[ROW_G][COL_Y], yuv2rgb[ROW_B][COL_Y], - yuv2rgb[ROW_R][COL_U], yuv2rgb[ROW_G][COL_U], yuv2rgb[ROW_B][COL_U], - yuv2rgb[ROW_R][COL_V], yuv2rgb[ROW_G][COL_V], yuv2rgb[ROW_B][COL_V], - yuv2rgb[ROW_R][COL_C], yuv2rgb[ROW_G][COL_C], yuv2rgb[ROW_B][COL_C]); - break; - case YUV_CONVERSION_FRAGMENT_POW: - snprintf(prog_pos, prog_remain, yuv_pow_prog_template, - yuv2rgb[ROW_R][COL_Y], yuv2rgb[ROW_G][COL_Y], yuv2rgb[ROW_B][COL_Y], - yuv2rgb[ROW_R][COL_U], yuv2rgb[ROW_G][COL_U], yuv2rgb[ROW_B][COL_U], - yuv2rgb[ROW_R][COL_V], yuv2rgb[ROW_G][COL_V], yuv2rgb[ROW_B][COL_V], - yuv2rgb[ROW_R][COL_C], yuv2rgb[ROW_G][COL_C], yuv2rgb[ROW_B][COL_C], - (float)1.0 / params->csp_params.rgamma, (float)1.0 / params->csp_params.bgamma, (float)1.0 / params->csp_params.bgamma); - break; - case YUV_CONVERSION_FRAGMENT_LOOKUP: - snprintf(prog_pos, prog_remain, yuv_lookup_prog_template, - yuv2rgb[ROW_R][COL_Y], yuv2rgb[ROW_G][COL_Y], yuv2rgb[ROW_B][COL_Y], - yuv2rgb[ROW_R][COL_U], yuv2rgb[ROW_G][COL_U], yuv2rgb[ROW_B][COL_U], - yuv2rgb[ROW_R][COL_V], yuv2rgb[ROW_G][COL_V], yuv2rgb[ROW_B][COL_V], - yuv2rgb[ROW_R][COL_C], yuv2rgb[ROW_G][COL_C], yuv2rgb[ROW_B][COL_C], - conv_texs[0], conv_texs[0], conv_texs[0]); - break; - case YUV_CONVERSION_FRAGMENT_LOOKUP3D: - snprintf(prog_pos, prog_remain, yuv_lookup3d_prog_template, conv_texs[0]); - break; - default: - mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown conversion type %i\n", YUV_CONVERSION(type)); - break; - } - mp_msg(MSGT_VO, MSGL_DBG2, "[gl] generated fragment program:\n%s\n", yuv_prog); - loadGPUProgram(GL_FRAGMENT_PROGRAM, yuv_prog); - free(yuv_prog); -} - -/** - * \brief detect the best YUV->RGB conversion method available - */ -int glAutodetectYUVConversion(void) { - const char *extensions = mpglGetString(GL_EXTENSIONS); - if (!extensions || !mpglMultiTexCoord2f) - return YUV_CONVERSION_NONE; - if (strstr(extensions, "GL_ARB_fragment_program")) - return YUV_CONVERSION_FRAGMENT; - if (strstr(extensions, "GL_ATI_text_fragment_shader")) - return YUV_CONVERSION_TEXT_FRAGMENT; - if (strstr(extensions, "GL_ATI_fragment_shader")) - return YUV_CONVERSION_COMBINERS_ATI; - return YUV_CONVERSION_NONE; -} - -/** - * \brief setup YUV->RGB conversion - * \param parms struct containing parameters like conversion and scaler type, - * brightness, ... - * \ingroup glconversion - */ -void glSetupYUVConversion(gl_conversion_params_t *params) { - float uvcos = params->csp_params.saturation * cos(params->csp_params.hue); - float uvsin = params->csp_params.saturation * sin(params->csp_params.hue); - if (params->chrom_texw == 0) params->chrom_texw = 1; - if (params->chrom_texh == 0) params->chrom_texh = 1; - switch (YUV_CONVERSION(params->type)) { - case YUV_CONVERSION_COMBINERS: - glSetupYUVCombiners(uvcos, uvsin); - break; - case YUV_CONVERSION_COMBINERS_ATI: - glSetupYUVFragmentATI(¶ms->csp_params, 0); - break; - case YUV_CONVERSION_TEXT_FRAGMENT: - glSetupYUVFragmentATI(¶ms->csp_params, 1); - break; - case YUV_CONVERSION_FRAGMENT_LOOKUP: - case YUV_CONVERSION_FRAGMENT_LOOKUP3D: - case YUV_CONVERSION_FRAGMENT: - case YUV_CONVERSION_FRAGMENT_POW: - glSetupYUVFragprog(params); - break; - case YUV_CONVERSION_NONE: - break; - default: - mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown conversion type %i\n", YUV_CONVERSION(params->type)); - } -} - -/** - * \brief enable the specified YUV conversion - * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D) - * \param type type of YUV conversion - * \ingroup glconversion - */ -void glEnableYUVConversion(GLenum target, int type) { - switch (YUV_CONVERSION(type)) { - case YUV_CONVERSION_COMBINERS: - mpglActiveTexture(GL_TEXTURE1); - mpglEnable(target); - mpglActiveTexture(GL_TEXTURE2); - mpglEnable(target); - mpglActiveTexture(GL_TEXTURE0); - mpglEnable(GL_REGISTER_COMBINERS_NV); - break; - case YUV_CONVERSION_COMBINERS_ATI: - mpglActiveTexture(GL_TEXTURE1); - mpglEnable(target); - mpglActiveTexture(GL_TEXTURE2); - mpglEnable(target); - mpglActiveTexture(GL_TEXTURE0); - mpglEnable(GL_FRAGMENT_SHADER_ATI); - break; - case YUV_CONVERSION_TEXT_FRAGMENT: - mpglActiveTexture(GL_TEXTURE1); - mpglEnable(target); - mpglActiveTexture(GL_TEXTURE2); - mpglEnable(target); - mpglActiveTexture(GL_TEXTURE0); - mpglEnable(GL_TEXT_FRAGMENT_SHADER_ATI); - break; - case YUV_CONVERSION_FRAGMENT_LOOKUP3D: - case YUV_CONVERSION_FRAGMENT_LOOKUP: - case YUV_CONVERSION_FRAGMENT_POW: - case YUV_CONVERSION_FRAGMENT: - case YUV_CONVERSION_NONE: - mpglEnable(GL_FRAGMENT_PROGRAM); - break; - } -} - -/** - * \brief disable the specified YUV conversion - * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D) - * \param type type of YUV conversion - * \ingroup glconversion - */ -void glDisableYUVConversion(GLenum target, int type) { - switch (YUV_CONVERSION(type)) { - case YUV_CONVERSION_COMBINERS: - mpglActiveTexture(GL_TEXTURE1); - mpglDisable(target); - mpglActiveTexture(GL_TEXTURE2); - mpglDisable(target); - mpglActiveTexture(GL_TEXTURE0); - mpglDisable(GL_REGISTER_COMBINERS_NV); - break; - case YUV_CONVERSION_COMBINERS_ATI: - mpglActiveTexture(GL_TEXTURE1); - mpglDisable(target); - mpglActiveTexture(GL_TEXTURE2); - mpglDisable(target); - mpglActiveTexture(GL_TEXTURE0); - mpglDisable(GL_FRAGMENT_SHADER_ATI); - break; - case YUV_CONVERSION_TEXT_FRAGMENT: - mpglDisable(GL_TEXT_FRAGMENT_SHADER_ATI); - // HACK: at least the Mac OS X 10.5 PPC Radeon drivers are broken and - // without this disable the texture units while the program is still - // running (10.4 PPC seems to work without this though). - mpglFlush(); - mpglActiveTexture(GL_TEXTURE1); - mpglDisable(target); - mpglActiveTexture(GL_TEXTURE2); - mpglDisable(target); - mpglActiveTexture(GL_TEXTURE0); - break; - case YUV_CONVERSION_FRAGMENT_LOOKUP3D: - case YUV_CONVERSION_FRAGMENT_LOOKUP: - case YUV_CONVERSION_FRAGMENT_POW: - case YUV_CONVERSION_FRAGMENT: - case YUV_CONVERSION_NONE: - mpglDisable(GL_FRAGMENT_PROGRAM); - break; - } -} - -void glEnable3DLeft(int type) { - GLint buffer; - switch (type) { - case GL_3D_RED_CYAN: - mpglColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE); - break; - case GL_3D_GREEN_MAGENTA: - mpglColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE); - break; - case GL_3D_QUADBUFFER: - mpglGetIntegerv(GL_DRAW_BUFFER, &buffer); - switch (buffer) { - case GL_FRONT: - case GL_FRONT_LEFT: - case GL_FRONT_RIGHT: - buffer = GL_FRONT_LEFT; - break; - case GL_BACK: - case GL_BACK_LEFT: - case GL_BACK_RIGHT: - buffer = GL_BACK_LEFT; - break; - } - mpglDrawBuffer(buffer); - break; - } -} - -void glEnable3DRight(int type) { - GLint buffer; - switch (type) { - case GL_3D_RED_CYAN: - mpglColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_FALSE); - break; - case GL_3D_GREEN_MAGENTA: - mpglColorMask(GL_TRUE, GL_FALSE, GL_TRUE, GL_FALSE); - break; - case GL_3D_QUADBUFFER: - mpglGetIntegerv(GL_DRAW_BUFFER, &buffer); - switch (buffer) { - case GL_FRONT: - case GL_FRONT_LEFT: - case GL_FRONT_RIGHT: - buffer = GL_FRONT_RIGHT; - break; - case GL_BACK: - case GL_BACK_LEFT: - case GL_BACK_RIGHT: - buffer = GL_BACK_RIGHT; - break; - } - mpglDrawBuffer(buffer); - break; - } -} - -void glDisable3D(int type) { - GLint buffer; - switch (type) { - case GL_3D_RED_CYAN: - case GL_3D_GREEN_MAGENTA: - mpglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - break; - case GL_3D_QUADBUFFER: - mpglDrawBuffer(vo_doublebuffering ? GL_BACK : GL_FRONT); - mpglGetIntegerv(GL_DRAW_BUFFER, &buffer); - switch (buffer) { - case GL_FRONT: - case GL_FRONT_LEFT: - case GL_FRONT_RIGHT: - buffer = GL_FRONT; - break; - case GL_BACK: - case GL_BACK_LEFT: - case GL_BACK_RIGHT: - buffer = GL_BACK; - break; - } - mpglDrawBuffer(buffer); - break; - } -} - -/** - * \brief draw a texture part at given 2D coordinates - * \param x screen top coordinate - * \param y screen left coordinate - * \param w screen width coordinate - * \param h screen height coordinate - * \param tx texture top coordinate in pixels - * \param ty texture left coordinate in pixels - * \param tw texture part width in pixels - * \param th texture part height in pixels - * \param sx width of texture in pixels - * \param sy height of texture in pixels - * \param rect_tex whether this texture uses texture_rectangle extension - * \param is_yv12 if != 0, also draw the textures from units 1 and 2, - * bits 8 - 15 and 16 - 23 specify the x and y scaling of those textures - * \param flip flip the texture upside down - * \ingroup gltexture - */ -void glDrawTex(GLfloat x, GLfloat y, GLfloat w, GLfloat h, - GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th, - int sx, int sy, int rect_tex, int is_yv12, int flip) { - int chroma_x_shift = (is_yv12 >> 8) & 31; - int chroma_y_shift = (is_yv12 >> 16) & 31; - GLfloat xscale = 1 << chroma_x_shift; - GLfloat yscale = 1 << chroma_y_shift; - GLfloat tx2 = tx / xscale, ty2 = ty / yscale, tw2 = tw / xscale, th2 = th / yscale; - if (!rect_tex) { - tx /= sx; ty /= sy; tw /= sx; th /= sy; - tx2 = tx, ty2 = ty, tw2 = tw, th2 = th; - } - if (flip) { - y += h; - h = -h; - } - mpglBegin(GL_QUADS); - mpglTexCoord2f(tx, ty); - if (is_yv12) { - mpglMultiTexCoord2f(GL_TEXTURE1, tx2, ty2); - mpglMultiTexCoord2f(GL_TEXTURE2, tx2, ty2); - } - mpglVertex2f(x, y); - mpglTexCoord2f(tx, ty + th); - if (is_yv12) { - mpglMultiTexCoord2f(GL_TEXTURE1, tx2, ty2 + th2); - mpglMultiTexCoord2f(GL_TEXTURE2, tx2, ty2 + th2); - } - mpglVertex2f(x, y + h); - mpglTexCoord2f(tx + tw, ty + th); - if (is_yv12) { - mpglMultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2 + th2); - mpglMultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2 + th2); - } - mpglVertex2f(x + w, y + h); - mpglTexCoord2f(tx + tw, ty); - if (is_yv12) { - mpglMultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2); - mpglMultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2); - } - mpglVertex2f(x + w, y); - mpglEnd(); -} - -#ifdef CONFIG_GL_WIN32 -#include "w32_common.h" -/** - * \brief little helper since wglGetProcAddress definition does not fit our - * getProcAddress - * \param procName name of function to look up - * \return function pointer returned by wglGetProcAddress - */ -static void *w32gpa(const GLubyte *procName) { - HMODULE oglmod; - void *res = wglGetProcAddress(procName); - if (res) return res; - oglmod = GetModuleHandle("opengl32.dll"); - return GetProcAddress(oglmod, procName); -} - -static int setGlWindow_w32(MPGLContext *ctx) -{ - HWND win = vo_w32_window; - int *vinfo = &ctx->vinfo.w32; - HGLRC *context = &ctx->context.w32; - int new_vinfo; - HDC windc = vo_w32_get_dc(win); - HGLRC new_context = 0; - int keep_context = 0; - int res = SET_WINDOW_FAILED; - - // should only be needed when keeping context, but not doing glFinish - // can cause flickering even when we do not keep it. - if (*context) - mpglFinish(); - new_vinfo = GetPixelFormat(windc); - if (*context && *vinfo && new_vinfo && *vinfo == new_vinfo) { - // we can keep the wglContext - new_context = *context; - keep_context = 1; - } else { - // create a context - new_context = wglCreateContext(windc); - if (!new_context) { - mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GL context!\n"); - goto out; - } - } - - // set context - if (!wglMakeCurrent(windc, new_context)) { - mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL context!\n"); - if (!keep_context) { - wglDeleteContext(new_context); - } - goto out; - } - - // set new values - vo_w32_window = win; - { - RECT rect; - GetClientRect(win, &rect); - vo_dwidth = rect.right; - vo_dheight = rect.bottom; - } - if (!keep_context) { - if (*context) - wglDeleteContext(*context); - *context = new_context; - *vinfo = new_vinfo; - getFunctions(w32gpa, NULL); - - // and inform that reinit is neccessary - res = SET_WINDOW_REINIT; - } else - res = SET_WINDOW_OK; - -out: - vo_w32_release_dc(win, windc); - return res; -} - -static void releaseGlContext_w32(MPGLContext *ctx) { - int *vinfo = &ctx->vinfo.w32; - HGLRC *context = &ctx->context.w32; - *vinfo = 0; - if (*context) { - wglMakeCurrent(0, 0); - wglDeleteContext(*context); - } - *context = 0; -} - -static void swapGlBuffers_w32(MPGLContext *ctx) { - HDC vo_hdc = vo_w32_get_dc(vo_w32_window); - SwapBuffers(vo_hdc); - vo_w32_release_dc(vo_w32_window, vo_hdc); -} -#endif -#ifdef CONFIG_GL_X11 -#include "x11_common.h" - -/** - * \brief Returns the XVisualInfo associated with Window win. - * \param win Window whose XVisualInfo is returne. - * \return XVisualInfo of the window. Caller must use XFree to free it. - */ -static XVisualInfo *getWindowVisualInfo(Window win) { - XWindowAttributes xw_attr; - XVisualInfo vinfo_template; - int tmp; - XGetWindowAttributes(mDisplay, win, &xw_attr); - vinfo_template.visualid = XVisualIDFromVisual(xw_attr.visual); - return XGetVisualInfo(mDisplay, VisualIDMask, &vinfo_template, &tmp); -} - -static void appendstr(char **dst, const char *str) -{ - int newsize; - char *newstr; - if (!str) - return; - newsize = strlen(*dst) + 1 + strlen(str) + 1; - newstr = realloc(*dst, newsize); - if (!newstr) - return; - *dst = newstr; - strcat(*dst, " "); - strcat(*dst, str); -} - -/** - * \brief Changes the window in which video is displayed. - * If possible only transfers the context to the new window, otherwise - * creates a new one, which must be initialized by the caller. - * \param vinfo Currently used visual. - * \param context Currently used context. - * \param win window that should be used for drawing. - * \return one of SET_WINDOW_FAILED, SET_WINDOW_OK or SET_WINDOW_REINIT. - * In case of SET_WINDOW_REINIT the context could not be transfered - * and the caller must initialize it correctly. - * \ingroup glcontext - */ -static int setGlWindow_x11(MPGLContext *ctx) -{ - XVisualInfo **vinfo = &ctx->vinfo.x11; - GLXContext *context = &ctx->context.x11; - Window win = vo_window; - XVisualInfo *new_vinfo; - GLXContext new_context = NULL; - int keep_context = 0; - - // should only be needed when keeping context, but not doing glFinish - // can cause flickering even when we do not keep it. - if (*context) - mpglFinish(); - new_vinfo = getWindowVisualInfo(win); - if (*context && *vinfo && new_vinfo && - (*vinfo)->visualid == new_vinfo->visualid) { - // we can keep the GLXContext - new_context = *context; - XFree(new_vinfo); - new_vinfo = *vinfo; - keep_context = 1; - } else { - // create a context - new_context = glXCreateContext(mDisplay, new_vinfo, NULL, True); - if (!new_context) { - mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GLX context!\n"); - XFree(new_vinfo); - return SET_WINDOW_FAILED; - } - } - - // set context - if (!glXMakeCurrent(mDisplay, vo_window, new_context)) { - mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GLX context!\n"); - if (!keep_context) { - glXDestroyContext(mDisplay, new_context); - XFree(new_vinfo); - } - return SET_WINDOW_FAILED; - } - - // set new values - vo_window = win; - vo_x11_update_geometry(); - if (!keep_context) { - void *(*getProcAddress)(const GLubyte *); - const char *(*glXExtStr)(Display *, int); - char *glxstr = strdup(""); - if (*context) - glXDestroyContext(mDisplay, *context); - *context = new_context; - if (*vinfo) - XFree(*vinfo); - *vinfo = new_vinfo; - getProcAddress = getdladdr("glXGetProcAddress"); - if (!getProcAddress) - getProcAddress = getdladdr("glXGetProcAddressARB"); - glXExtStr = getdladdr("glXQueryExtensionsString"); - if (glXExtStr) - appendstr(&glxstr, glXExtStr(mDisplay, DefaultScreen(mDisplay))); - glXExtStr = getdladdr("glXGetClientString"); - if (glXExtStr) - appendstr(&glxstr, glXExtStr(mDisplay, GLX_EXTENSIONS)); - glXExtStr = getdladdr("glXGetServerString"); - if (glXExtStr) - appendstr(&glxstr, glXExtStr(mDisplay, GLX_EXTENSIONS)); - - getFunctions(getProcAddress, glxstr); - if (!mpglGenPrograms && mpglGetString && - getProcAddress && - strstr(mpglGetString(GL_EXTENSIONS), "GL_ARB_vertex_program")) { - mp_msg(MSGT_VO, MSGL_WARN, "Broken glXGetProcAddress detected, trying workaround\n"); - getFunctions(NULL, glxstr); - } - free(glxstr); - - // and inform that reinit is neccessary - return SET_WINDOW_REINIT; - } - return SET_WINDOW_OK; -} - -/** - * \brief free the VisualInfo and GLXContext of an OpenGL context. - * \ingroup glcontext - */ -static void releaseGlContext_x11(MPGLContext *ctx) { - XVisualInfo **vinfo = &ctx->vinfo.x11; - GLXContext *context = &ctx->context.x11; - if (*vinfo) - XFree(*vinfo); - *vinfo = NULL; - if (*context) - { - mpglFinish(); - glXMakeCurrent(mDisplay, None, NULL); - glXDestroyContext(mDisplay, *context); - } - *context = 0; -} - -static void swapGlBuffers_x11(MPGLContext *ctx) { - glXSwapBuffers(mDisplay, vo_window); -} - -static int x11_check_events(void) { - return vo_x11_check_events(mDisplay); -} - -static void gl_update_xinerama_info(void) -{ - update_xinerama_info(global_vo); -} - -static void gl_border(void) -{ - vo_x11_border(); -} - -static void gl_fullscreen(void) -{ - vo_x11_fullscreen(); -} - -static void gl_ontop(void) -{ - vo_x11_ontop(); -} -#endif - -#ifdef CONFIG_GL_SDL -#include "sdl_common.h" - -static void swapGlBuffers_sdl(MPGLContext *ctx) { - SDL_GL_SwapBuffers(); -} - -static void *sdlgpa(const GLubyte *name) { - return SDL_GL_GetProcAddress(name); -} - -static int setGlWindow_sdl(MPGLContext *ctx) { - if (sdl_set_mode(0, SDL_OPENGL | SDL_RESIZABLE) < 0) - return SET_WINDOW_FAILED; - SDL_GL_LoadLibrary(NULL); - getFunctions(sdlgpa, NULL); - return SET_WINDOW_OK; -} - -static int sdl_check_events(void) { - int res = 0; - SDL_Event event; - while (SDL_PollEvent(&event)) { - res |= sdl_default_handle_event(&event); - } - // poll "events" from within MPlayer code - res |= sdl_default_handle_event(NULL); - if (res & VO_EVENT_RESIZE) - sdl_set_mode(0, SDL_OPENGL | SDL_RESIZABLE); - return res; -} - -#endif - -static int setGlWindow_dummy(MPGLContext *ctx) { - getFunctions(NULL, NULL); - return SET_WINDOW_OK; -} - -static void releaseGlContext_dummy(MPGLContext *ctx) { -} - -static int dummy_check_events(void) { - return 0; -} - -static void dummy_update_xinerama_info(void) { - if (vo_screenwidth <= 0 || vo_screenheight <= 0) { - mp_msg(MSGT_VO, MSGL_ERR, "You must specify the screen dimensions " - "with -screenw and -screenh\n"); - vo_screenwidth = 1280; - vo_screenheight = 768; - } - aspect_save_screenres(vo_screenwidth, vo_screenheight); -} - -int init_mpglcontext(MPGLContext *ctx, enum MPGLType type) { - if (type == GLTYPE_AUTO) { - int res = init_mpglcontext(ctx, GLTYPE_W32); - if (res) return res; - res = init_mpglcontext(ctx, GLTYPE_X11); - if (res) return res; - res = init_mpglcontext(ctx, GLTYPE_SDL); - return res; - } - memset(ctx, 0, sizeof(*ctx)); - ctx->setGlWindow = setGlWindow_dummy; - ctx->releaseGlContext = releaseGlContext_dummy; - ctx->update_xinerama_info = dummy_update_xinerama_info; - ctx->check_events = dummy_check_events; - ctx->type = type; - switch (ctx->type) { -#ifdef CONFIG_GL_WIN32 - case GLTYPE_W32: - ctx->setGlWindow = setGlWindow_w32; - ctx->releaseGlContext = releaseGlContext_w32; - ctx->swapGlBuffers = swapGlBuffers_w32; - ctx->update_xinerama_info = w32_update_xinerama_info; - ctx->border = vo_w32_border; - ctx->check_events = vo_w32_check_events; - ctx->fullscreen = vo_w32_fullscreen; - ctx->ontop = vo_w32_ontop; - return vo_w32_init(); -#endif -#ifdef CONFIG_GL_X11 - case GLTYPE_X11: - ctx->setGlWindow = setGlWindow_x11; - ctx->releaseGlContext = releaseGlContext_x11; - ctx->swapGlBuffers = swapGlBuffers_x11; - ctx->update_xinerama_info = gl_update_xinerama_info; - ctx->border = gl_border; - ctx->check_events = x11_check_events; - ctx->fullscreen = gl_fullscreen; - ctx->ontop = gl_ontop; - return vo_init(); -#endif -#ifdef CONFIG_GL_SDL - case GLTYPE_SDL: - SDL_Init(SDL_INIT_VIDEO); - ctx->setGlWindow = setGlWindow_sdl; - ctx->swapGlBuffers = swapGlBuffers_sdl; - ctx->check_events = sdl_check_events; - ctx->fullscreen = vo_sdl_fullscreen; - return vo_sdl_init(); -#endif - default: - return 0; - } -} - -void uninit_mpglcontext(MPGLContext *ctx) { - ctx->releaseGlContext(ctx); - switch (ctx->type) { -#ifdef CONFIG_GL_WIN32 - case GLTYPE_W32: - vo_w32_uninit(); - break; -#endif -#ifdef CONFIG_GL_X11 - case GLTYPE_X11: - vo_x11_uninit(); - break; -#endif -#ifdef CONFIG_GL_SDL - case GLTYPE_SDL: - vo_sdl_uninit(); - break; -#endif - } - memset(ctx, 0, sizeof(*ctx)); -} +/* + * common OpenGL routines + * + * copyleft (C) 2005-2010 Reimar Döffinger + * Special thanks go to the xine team and Matthias Hopf, whose video_out_opengl.c + * gave me lots of good ideas. + * + * 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. + */ + +/** + * \file gl_common.c + * \brief OpenGL helper functions used by vo_gl.c and vo_gl2.c + */ + +#include +#include +#include +#include +#include +#include +#include "talloc.h" +#include "gl_common.h" +#include "old_vo_wrapper.h" +#include "csputils.h" +#include "aspect.h" +#include "pnm_loader.h" +#include "options.h" + +//! \defgroup glgeneral OpenGL general helper functions + +//! \defgroup glcontext OpenGL context management helper functions + +//! \defgroup gltexture OpenGL texture handling helper functions + +//! \defgroup glconversion OpenGL conversion helper functions + +/** + * \brief adjusts the GL_UNPACK_ALIGNMENT to fit the stride. + * \param stride number of bytes per line for which alignment should fit. + * \ingroup glgeneral + */ +void glAdjustAlignment(GL *gl, int stride) +{ + GLint gl_alignment; + if (stride % 8 == 0) + gl_alignment = 8; + else if (stride % 4 == 0) + gl_alignment = 4; + else if (stride % 2 == 0) + gl_alignment = 2; + else + gl_alignment = 1; + gl->PixelStorei(GL_UNPACK_ALIGNMENT, gl_alignment); +} + +struct gl_name_map_struct { + GLint value; + const char *name; +}; + +#undef MAP +#define MAP(a) {a, # a} +//! mapping table for the glValName function +static const struct gl_name_map_struct gl_name_map[] = { + // internal format + MAP(GL_R3_G3_B2), MAP(GL_RGB4), MAP(GL_RGB5), MAP(GL_RGB8), + MAP(GL_RGB10), MAP(GL_RGB12), MAP(GL_RGB16), MAP(GL_RGBA2), + MAP(GL_RGBA4), MAP(GL_RGB5_A1), MAP(GL_RGBA8), MAP(GL_RGB10_A2), + MAP(GL_RGBA12), MAP(GL_RGBA16), MAP(GL_LUMINANCE8), MAP(GL_LUMINANCE16), + + // format + MAP(GL_RGB), MAP(GL_RGBA), MAP(GL_RED), MAP(GL_GREEN), MAP(GL_BLUE), + MAP(GL_ALPHA), MAP(GL_LUMINANCE), MAP(GL_LUMINANCE_ALPHA), + MAP(GL_COLOR_INDEX), + // rest 1.2 only + MAP(GL_BGR), MAP(GL_BGRA), + + //type + MAP(GL_BYTE), MAP(GL_UNSIGNED_BYTE), MAP(GL_SHORT), MAP(GL_UNSIGNED_SHORT), + MAP(GL_INT), MAP(GL_UNSIGNED_INT), MAP(GL_FLOAT), MAP(GL_DOUBLE), + MAP(GL_2_BYTES), MAP(GL_3_BYTES), MAP(GL_4_BYTES), + // rest 1.2 only + MAP(GL_UNSIGNED_BYTE_3_3_2), MAP(GL_UNSIGNED_BYTE_2_3_3_REV), + MAP(GL_UNSIGNED_SHORT_5_6_5), MAP(GL_UNSIGNED_SHORT_5_6_5_REV), + MAP(GL_UNSIGNED_SHORT_4_4_4_4), MAP(GL_UNSIGNED_SHORT_4_4_4_4_REV), + MAP(GL_UNSIGNED_SHORT_5_5_5_1), MAP(GL_UNSIGNED_SHORT_1_5_5_5_REV), + MAP(GL_UNSIGNED_INT_8_8_8_8), MAP(GL_UNSIGNED_INT_8_8_8_8_REV), + MAP(GL_UNSIGNED_INT_10_10_10_2), MAP(GL_UNSIGNED_INT_2_10_10_10_REV), + {0, 0} +}; +#undef MAP + +/** + * \brief return the name of an OpenGL constant + * \param value the constant + * \return name of the constant or "Unknown format!" + * \ingroup glgeneral + */ +const char *glValName(GLint value) +{ + int i = 0; + + while (gl_name_map[i].name) { + if (gl_name_map[i].value == value) + return gl_name_map[i].name; + i++; + } + return "Unknown format!"; +} + +//! always return this format as internal texture format in glFindFormat +#define TEXTUREFORMAT_ALWAYS GL_RGB8 +#undef TEXTUREFORMAT_ALWAYS + +/** + * \brief find the OpenGL settings coresponding to format. + * + * All parameters may be NULL. + * \param fmt MPlayer format to analyze. + * \param bpp [OUT] bits per pixel of that format. + * \param gl_texfmt [OUT] internal texture format that fits the + * image format, not necessarily the best for performance. + * \param gl_format [OUT] OpenGL format for this image format. + * \param gl_type [OUT] OpenGL type for this image format. + * \return 1 if format is supported by OpenGL, 0 if not. + * \ingroup gltexture + */ +int glFindFormat(uint32_t fmt, int *bpp, GLint *gl_texfmt, + GLenum *gl_format, GLenum *gl_type) +{ + int supported = 1; + int dummy1; + GLenum dummy2; + GLint dummy3; + if (!bpp) + bpp = &dummy1; + if (!gl_texfmt) + gl_texfmt = &dummy3; + if (!gl_format) + gl_format = &dummy2; + if (!gl_type) + gl_type = &dummy2; + + if (mp_get_chroma_shift(fmt, NULL, NULL, NULL)) { + // reduce the possible cases a bit + if (IMGFMT_IS_YUVP16_LE(fmt)) + fmt = IMGFMT_420P16_LE; + else if (IMGFMT_IS_YUVP16_BE(fmt)) + fmt = IMGFMT_420P16_BE; + else + fmt = IMGFMT_YV12; + } + + *bpp = IMGFMT_IS_BGR(fmt) ? IMGFMT_BGR_DEPTH(fmt) : IMGFMT_RGB_DEPTH(fmt); + *gl_texfmt = 3; + switch (fmt) { + case IMGFMT_RGB48NE: + *gl_format = GL_RGB; + *gl_type = GL_UNSIGNED_SHORT; + break; + case IMGFMT_RGB24: + *gl_format = GL_RGB; + *gl_type = GL_UNSIGNED_BYTE; + break; + case IMGFMT_RGBA: + *gl_texfmt = 4; + *gl_format = GL_RGBA; + *gl_type = GL_UNSIGNED_BYTE; + break; + case IMGFMT_420P16: + supported = 0; // no native YUV support + *gl_texfmt = GL_LUMINANCE16; + *bpp = 16; + *gl_format = GL_LUMINANCE; + *gl_type = GL_UNSIGNED_SHORT; + break; + case IMGFMT_YV12: + supported = 0; // no native YV12 support + case IMGFMT_Y800: + case IMGFMT_Y8: + *gl_texfmt = 1; + *bpp = 8; + *gl_format = GL_LUMINANCE; + *gl_type = GL_UNSIGNED_BYTE; + break; + case IMGFMT_UYVY: + // IMGFMT_YUY2 would be more logical for the _REV format, + // but gives clearly swapped colors. + case IMGFMT_YVYU: + *gl_texfmt = GL_YCBCR_MESA; + *bpp = 16; + *gl_format = GL_YCBCR_MESA; + *gl_type = fmt == IMGFMT_UYVY ? GL_UNSIGNED_SHORT_8_8 : GL_UNSIGNED_SHORT_8_8_REV; + break; +#if 0 + // we do not support palettized formats, although the format the + // swscale produces works + case IMGFMT_RGB8: + gl_format = GL_RGB; + gl_type = GL_UNSIGNED_BYTE_2_3_3_REV; + break; +#endif + case IMGFMT_RGB15: + *gl_format = GL_RGBA; + *gl_type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + break; + case IMGFMT_RGB16: + *gl_format = GL_RGB; + *gl_type = GL_UNSIGNED_SHORT_5_6_5_REV; + break; +#if 0 + case IMGFMT_BGR8: + // special case as red and blue have a differen number of bits. + // GL_BGR and GL_UNSIGNED_BYTE_3_3_2 isn't supported at least + // by nVidia drivers, and in addition would give more bits to + // blue than to red, which isn't wanted + gl_format = GL_RGB; + gl_type = GL_UNSIGNED_BYTE_3_3_2; + break; +#endif + case IMGFMT_BGR15: + *gl_format = GL_BGRA; + *gl_type = GL_UNSIGNED_SHORT_1_5_5_5_REV; + break; + case IMGFMT_BGR16: + *gl_format = GL_RGB; + *gl_type = GL_UNSIGNED_SHORT_5_6_5; + break; + case IMGFMT_BGR24: + *gl_format = GL_BGR; + *gl_type = GL_UNSIGNED_BYTE; + break; + case IMGFMT_BGRA: + *gl_texfmt = 4; + *gl_format = GL_BGRA; + *gl_type = GL_UNSIGNED_BYTE; + break; + default: + *gl_texfmt = 4; + *gl_format = GL_RGBA; + *gl_type = GL_UNSIGNED_BYTE; + supported = 0; + } +#ifdef TEXTUREFORMAT_ALWAYS + *gl_texfmt = TEXTUREFORMAT_ALWAYS; +#endif + return supported; +} + +#ifdef HAVE_LIBDL +#include +#endif +/** + * \brief find address of a linked function + * \param s name of function to find + * \return address of function or NULL if not found + */ +static void *getdladdr(const char *s) +{ + void *ret = NULL; +#ifdef HAVE_LIBDL + void *handle = dlopen(NULL, RTLD_LAZY); + if (!handle) + return NULL; + ret = dlsym(handle, s); + dlclose(handle); +#endif + return ret; +} + +typedef struct { + ptrdiff_t offset; // offset to the function pointer in struct GL + const char *extstr; + const char *funcnames[7]; + void *fallback; +} extfunc_desc_t; + +#define DEF_FUNC_DESC(name) \ + {offsetof(GL, name), NULL, {"gl" # name, NULL}, gl ## name} +#define DEF_EXT_FUNCS(...) __VA_ARGS__ +#define DEF_EXT_DESC(name, ext, funcnames) \ + {offsetof(GL, name), ext, {DEF_EXT_FUNCS funcnames}} + +static const extfunc_desc_t extfuncs[] = { + // these aren't extension functions but we query them anyway to allow + // different "backends" with one binary + DEF_FUNC_DESC(Begin), + DEF_FUNC_DESC(End), + DEF_FUNC_DESC(Viewport), + DEF_FUNC_DESC(MatrixMode), + DEF_FUNC_DESC(LoadIdentity), + DEF_FUNC_DESC(Translated), + DEF_FUNC_DESC(Scaled), + DEF_FUNC_DESC(Ortho), + DEF_FUNC_DESC(Frustum), + DEF_FUNC_DESC(PushMatrix), + DEF_FUNC_DESC(PopMatrix), + DEF_FUNC_DESC(Clear), + DEF_FUNC_DESC(GenLists), + DEF_FUNC_DESC(DeleteLists), + DEF_FUNC_DESC(NewList), + DEF_FUNC_DESC(EndList), + DEF_FUNC_DESC(CallList), + DEF_FUNC_DESC(CallLists), + DEF_FUNC_DESC(GenTextures), + DEF_FUNC_DESC(DeleteTextures), + DEF_FUNC_DESC(TexEnvf), + DEF_FUNC_DESC(TexEnvi), + DEF_FUNC_DESC(Color4ub), + DEF_FUNC_DESC(Color3f), + DEF_FUNC_DESC(Color4f), + DEF_FUNC_DESC(ClearColor), + DEF_FUNC_DESC(ClearDepth), + DEF_FUNC_DESC(DepthFunc), + DEF_FUNC_DESC(Enable), + DEF_FUNC_DESC(Disable), + DEF_FUNC_DESC(DrawBuffer), + DEF_FUNC_DESC(DepthMask), + DEF_FUNC_DESC(BlendFunc), + DEF_FUNC_DESC(Flush), + DEF_FUNC_DESC(Finish), + DEF_FUNC_DESC(PixelStorei), + DEF_FUNC_DESC(TexImage1D), + DEF_FUNC_DESC(TexImage2D), + DEF_FUNC_DESC(TexSubImage2D), + DEF_FUNC_DESC(GetTexImage), + DEF_FUNC_DESC(TexParameteri), + DEF_FUNC_DESC(TexParameterf), + DEF_FUNC_DESC(TexParameterfv), + DEF_FUNC_DESC(TexCoord2f), + DEF_FUNC_DESC(Vertex2f), + DEF_FUNC_DESC(Vertex3f), + DEF_FUNC_DESC(Normal3f), + DEF_FUNC_DESC(Lightfv), + DEF_FUNC_DESC(ColorMaterial), + DEF_FUNC_DESC(ShadeModel), + DEF_FUNC_DESC(GetIntegerv), + DEF_FUNC_DESC(ColorMask), + DEF_FUNC_DESC(ReadPixels), + DEF_FUNC_DESC(ReadBuffer), + + DEF_EXT_DESC(GenBuffers, NULL, + ("glGenBuffers", "glGenBuffersARB")), + DEF_EXT_DESC(DeleteBuffers, NULL, + ("glDeleteBuffers", "glDeleteBuffersARB")), + DEF_EXT_DESC(BindBuffer, NULL, + ("glBindBuffer", "glBindBufferARB")), + DEF_EXT_DESC(MapBuffer, NULL, + ("glMapBuffer", "glMapBufferARB")), + DEF_EXT_DESC(UnmapBuffer, NULL, + ("glUnmapBuffer", "glUnmapBufferARB")), + DEF_EXT_DESC(BufferData, NULL, + ("glBufferData", "glBufferDataARB")), + DEF_EXT_DESC(CombinerParameterfv, "NV_register_combiners", + ("glCombinerParameterfv", "glCombinerParameterfvNV")), + DEF_EXT_DESC(CombinerParameteri, "NV_register_combiners", + ("glCombinerParameteri", "glCombinerParameteriNV")), + DEF_EXT_DESC(CombinerInput, "NV_register_combiners", + ("glCombinerInput", "glCombinerInputNV")), + DEF_EXT_DESC(CombinerOutput, "NV_register_combiners", + ("glCombinerOutput", "glCombinerOutputNV")), + DEF_EXT_DESC(BeginFragmentShader, "ATI_fragment_shader", + ("glBeginFragmentShaderATI")), + DEF_EXT_DESC(EndFragmentShader, "ATI_fragment_shader", + ("glEndFragmentShaderATI")), + DEF_EXT_DESC(SampleMap, "ATI_fragment_shader", + ("glSampleMapATI")), + DEF_EXT_DESC(ColorFragmentOp2, "ATI_fragment_shader", + ("glColorFragmentOp2ATI")), + DEF_EXT_DESC(ColorFragmentOp3, "ATI_fragment_shader", + ("glColorFragmentOp3ATI")), + DEF_EXT_DESC(SetFragmentShaderConstant, "ATI_fragment_shader", + ("glSetFragmentShaderConstantATI")), + DEF_EXT_DESC(ActiveTexture, NULL, + ("glActiveTexture", "glActiveTextureARB")), + DEF_EXT_DESC(BindTexture, NULL, + ("glBindTexture", "glBindTextureARB", "glBindTextureEXT")), + DEF_EXT_DESC(MultiTexCoord2f, NULL, + ("glMultiTexCoord2f", "glMultiTexCoord2fARB")), + DEF_EXT_DESC(GenPrograms, "_program", + ("glGenProgramsARB")), + DEF_EXT_DESC(DeletePrograms, "_program", + ("glDeleteProgramsARB")), + DEF_EXT_DESC(BindProgram, "_program", + ("glBindProgramARB")), + DEF_EXT_DESC(ProgramString, "_program", + ("glProgramStringARB")), + DEF_EXT_DESC(GetProgramiv, "_program", + ("glGetProgramivARB")), + DEF_EXT_DESC(ProgramEnvParameter4f, "_program", + ("glProgramEnvParameter4fARB")), + DEF_EXT_DESC(SwapInterval, "_swap_control", + ("glXSwapIntervalSGI", "glXSwapInterval", "wglSwapIntervalSGI", + "wglSwapInterval", "wglSwapIntervalEXT")), + DEF_EXT_DESC(TexImage3D, NULL, + ("glTexImage3D")), + DEF_EXT_DESC(AllocateMemoryMESA, "GLX_MESA_allocate_memory", + ("glXAllocateMemoryMESA")), + DEF_EXT_DESC(FreeMemoryMESA, "GLX_MESA_allocate_memory", + ("glXFreeMemoryMESA")), + {-1} +}; + +/** + * \brief find the function pointers of some useful OpenGL extensions + * \param getProcAddress function to resolve function names, may be NULL + * \param ext2 an extra extension string + */ +static void getFunctions(GL *gl, void *(*getProcAddress)(const GLubyte *), + const char *ext2) +{ + const extfunc_desc_t *dsc; + const char *extensions; + char *allexts; + + if (!getProcAddress) + getProcAddress = (void *)getdladdr; + + // special case, we need glGetString before starting to find the other functions + gl->GetString = getProcAddress("glGetString"); + if (!gl->GetString) + gl->GetString = glGetString; + + extensions = (const char *)gl->GetString(GL_EXTENSIONS); + if (!extensions) + extensions = ""; + if (!ext2) + ext2 = ""; + allexts = malloc(strlen(extensions) + strlen(ext2) + 2); + strcpy(allexts, extensions); + strcat(allexts, " "); + strcat(allexts, ext2); + mp_msg(MSGT_VO, MSGL_DBG2, "OpenGL extensions string:\n%s\n", allexts); + for (dsc = extfuncs; dsc->offset >= 0; dsc++) { + void *ptr = NULL; + int i; + if (!dsc->extstr || strstr(allexts, dsc->extstr)) { + for (i = 0; !ptr && dsc->funcnames[i]; i++) + ptr = getProcAddress((const GLubyte *)dsc->funcnames[i]); + } + if (!ptr) + ptr = dsc->fallback; + void **funcptr = (void**)(((char*)gl) + dsc->offset); + *funcptr = ptr; + } + free(allexts); +} + +/** + * \brief create a texture and set some defaults + * \param target texture taget, usually GL_TEXTURE_2D + * \param fmt internal texture format + * \param format texture host data format + * \param type texture host data type + * \param filter filter used for scaling, e.g. GL_LINEAR + * \param w texture width + * \param h texture height + * \param val luminance value to fill texture with + * \ingroup gltexture + */ +void glCreateClearTex(GL *gl, GLenum target, GLenum fmt, GLenum format, + GLenum type, GLint filter, int w, int h, + unsigned char val) +{ + GLfloat fval = (GLfloat)val / 255.0; + GLfloat border[4] = { + fval, fval, fval, fval + }; + int stride; + char *init; + if (w == 0) + w = 1; + if (h == 0) + h = 1; + stride = w * glFmt2bpp(format, type); + if (!stride) + return; + init = malloc(stride * h); + memset(init, val, stride * h); + glAdjustAlignment(gl, stride); + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, w); + gl->TexImage2D(target, 0, fmt, w, h, 0, format, type, init); + gl->TexParameterf(target, GL_TEXTURE_PRIORITY, 1.0); + 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); + // Border texels should not be used with CLAMP_TO_EDGE + // We set a sane default anyway. + gl->TexParameterfv(target, GL_TEXTURE_BORDER_COLOR, border); + free(init); +} + +static GLint detect_hqtexfmt(GL *gl) +{ + const char *extensions = (const char *)gl->GetString(GL_EXTENSIONS); + if (strstr(extensions, "_texture_float")) + return GL_RGB32F; + else if (strstr(extensions, "NV_float_buffer")) + return GL_FLOAT_RGB32_NV; + return GL_RGB16; +} + +/** + * \brief creates a texture from a PPM file + * \param target texture taget, usually GL_TEXTURE_2D + * \param fmt internal texture format, 0 for default + * \param filter filter used for scaling, e.g. GL_LINEAR + * \param f file to read PPM from + * \param width [out] width of texture + * \param height [out] height of texture + * \param maxval [out] maxval value from PPM file + * \return 0 on error, 1 otherwise + * \ingroup gltexture + */ +int glCreatePPMTex(GL *gl, GLenum target, GLenum fmt, GLint filter, + FILE *f, int *width, int *height, int *maxval) +{ + int w, h, m, bpp; + GLenum type; + uint8_t *data = read_pnm(f, &w, &h, &bpp, &m); + GLint hqtexfmt = detect_hqtexfmt(gl); + if (!data || (bpp != 3 && bpp != 6)) { + free(data); + return 0; + } + if (!fmt) { + fmt = bpp == 6 ? hqtexfmt : 3; + if (fmt == GL_FLOAT_RGB32_NV && target != GL_TEXTURE_RECTANGLE) + fmt = GL_RGB16; + } + type = bpp == 6 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE; + glCreateClearTex(gl, target, fmt, GL_RGB, type, filter, w, h, 0); + glUploadTex(gl, target, GL_RGB, type, + data, w * bpp, 0, 0, w, h, 0); + free(data); + if (width) + *width = w; + if (height) + *height = h; + if (maxval) + *maxval = m; + return 1; +} + +/** + * \brief return the number of bytes per pixel for the given format + * \param format OpenGL format + * \param type OpenGL type + * \return bytes per pixel + * \ingroup glgeneral + * + * Does not handle all possible variants, just those used by MPlayer + */ +int glFmt2bpp(GLenum format, GLenum type) +{ + int component_size = 0; + switch (type) { + case GL_UNSIGNED_BYTE_3_3_2: + case GL_UNSIGNED_BYTE_2_3_3_REV: + return 1; + case GL_UNSIGNED_SHORT_5_5_5_1: + case GL_UNSIGNED_SHORT_1_5_5_5_REV: + case GL_UNSIGNED_SHORT_5_6_5: + case GL_UNSIGNED_SHORT_5_6_5_REV: + return 2; + case GL_UNSIGNED_BYTE: + component_size = 1; + break; + case GL_UNSIGNED_SHORT: + component_size = 2; + break; + } + switch (format) { + case GL_LUMINANCE: + case GL_ALPHA: + return component_size; + case GL_YCBCR_MESA: + return 2; + case GL_RGB: + case GL_BGR: + return 3 * component_size; + case GL_RGBA: + case GL_BGRA: + return 4 * component_size; + } + return 0; // unknown +} + +/** + * \brief upload a texture, handling things like stride and slices + * \param target texture target, usually GL_TEXTURE_2D + * \param format OpenGL format of data + * \param type OpenGL type of data + * \param dataptr data to upload + * \param stride data stride + * \param x x offset in texture + * \param y y offset in texture + * \param w width of the texture part to upload + * \param h height of the texture part to upload + * \param slice height of an upload slice, 0 for all at once + * \ingroup gltexture + */ +void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type, + const void *dataptr, int stride, + int x, int y, int w, int h, int slice) +{ + const uint8_t *data = dataptr; + int y_max = y + h; + if (w <= 0 || h <= 0) + return; + if (slice <= 0) + slice = h; + if (stride < 0) { + data += (h - 1) * stride; + stride = -stride; + } + // this is not always correct, but should work for MPlayer + glAdjustAlignment(gl, stride); + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, stride / glFmt2bpp(format, type)); + for (; y + slice <= y_max; y += slice) { + gl->TexSubImage2D(target, 0, x, y, w, slice, format, type, data); + data += stride * slice; + } + if (y < y_max) + gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data); +} + +static void fillUVcoeff(GLfloat *ucoef, GLfloat *vcoef, + float uvcos, float uvsin) +{ + int i; + ucoef[0] = 0 * uvcos + 1.403 * uvsin; + vcoef[0] = 0 * uvsin + 1.403 * uvcos; + ucoef[1] = -0.344 * uvcos + -0.714 * uvsin; + vcoef[1] = -0.344 * uvsin + -0.714 * uvcos; + ucoef[2] = 1.770 * uvcos + 0 * uvsin; + vcoef[2] = 1.770 * uvsin + 0 * uvcos; + ucoef[3] = 0; + vcoef[3] = 0; + // Coefficients (probably) must be in [0, 1] range, whereas they originally + // are in [-2, 2] range, so here comes the trick: + // First put them in the [-0.5, 0.5] range, then add 0.5. + // This can be undone with the HALF_BIAS and SCALE_BY_FOUR arguments + // for CombinerInput and CombinerOutput (or the respective ATI variants) + for (i = 0; i < 4; i++) { + ucoef[i] = ucoef[i] * 0.25 + 0.5; + vcoef[i] = vcoef[i] * 0.25 + 0.5; + } +} + +/** + * \brief Setup register combiners for YUV to RGB conversion. + * \param uvcos used for saturation and hue adjustment + * \param uvsin used for saturation and hue adjustment + */ +static void glSetupYUVCombiners(GL *gl, float uvcos, float uvsin) +{ + GLfloat ucoef[4]; + GLfloat vcoef[4]; + GLint i; + if (!gl->CombinerInput || !gl->CombinerOutput || + !gl->CombinerParameterfv || !gl->CombinerParameteri) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Combiner functions missing!\n"); + return; + } + gl->GetIntegerv(GL_MAX_GENERAL_COMBINERS_NV, &i); + if (i < 2) + mp_msg(MSGT_VO, MSGL_ERR, + "[gl] 2 general combiners needed for YUV combiner support (found %i)\n", i); + gl->GetIntegerv(GL_MAX_TEXTURE_UNITS, &i); + if (i < 3) + mp_msg(MSGT_VO, MSGL_ERR, + "[gl] 3 texture units needed for YUV combiner support (found %i)\n", i); + fillUVcoeff(ucoef, vcoef, uvcos, uvsin); + gl->CombinerParameterfv(GL_CONSTANT_COLOR0_NV, ucoef); + gl->CombinerParameterfv(GL_CONSTANT_COLOR1_NV, vcoef); + + // UV first, like this green component cannot overflow + gl->CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_A_NV, + GL_TEXTURE1, GL_HALF_BIAS_NORMAL_NV, GL_RGB); + gl->CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_B_NV, + GL_CONSTANT_COLOR0_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB); + gl->CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_C_NV, + GL_TEXTURE2, GL_HALF_BIAS_NORMAL_NV, GL_RGB); + gl->CombinerInput(GL_COMBINER0_NV, GL_RGB, GL_VARIABLE_D_NV, + GL_CONSTANT_COLOR1_NV, GL_HALF_BIAS_NORMAL_NV, GL_RGB); + gl->CombinerOutput(GL_COMBINER0_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, + GL_SPARE0_NV, GL_SCALE_BY_FOUR_NV, GL_NONE, GL_FALSE, + GL_FALSE, GL_FALSE); + + // stage 2 + gl->CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_A_NV, GL_SPARE0_NV, + GL_SIGNED_IDENTITY_NV, GL_RGB); + gl->CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_B_NV, GL_ZERO, + GL_UNSIGNED_INVERT_NV, GL_RGB); + gl->CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_C_NV, + GL_TEXTURE0, GL_SIGNED_IDENTITY_NV, GL_RGB); + gl->CombinerInput(GL_COMBINER1_NV, GL_RGB, GL_VARIABLE_D_NV, GL_ZERO, + GL_UNSIGNED_INVERT_NV, GL_RGB); + gl->CombinerOutput(GL_COMBINER1_NV, GL_RGB, GL_DISCARD_NV, GL_DISCARD_NV, + GL_SPARE0_NV, GL_NONE, GL_NONE, GL_FALSE, + GL_FALSE, GL_FALSE); + + // leave final combiner stage in default mode + gl->CombinerParameteri(GL_NUM_GENERAL_COMBINERS_NV, 2); +} + +/** + * \brief Setup ATI version of register combiners for YUV to RGB conversion. + * \param csp_params parameters used for colorspace conversion + * \param text if set use the GL_ATI_text_fragment_shader API as + * used on OS X. + */ +static void glSetupYUVFragmentATI(GL *gl, struct mp_csp_params *csp_params, + int text) +{ + GLint i; + float yuv2rgb[3][4]; + + gl->GetIntegerv(GL_MAX_TEXTURE_UNITS, &i); + if (i < 3) + mp_msg(MSGT_VO, MSGL_ERR, + "[gl] 3 texture units needed for YUV combiner (ATI) support (found %i)\n", i); + + mp_get_yuv2rgb_coeffs(csp_params, yuv2rgb); + for (i = 0; i < 3; i++) { + int j; + yuv2rgb[i][3] -= -0.5 * (yuv2rgb[i][1] + yuv2rgb[i][2]); + for (j = 0; j < 4; j++) { + yuv2rgb[i][j] *= 0.125; + yuv2rgb[i][j] += 0.5; + if (yuv2rgb[i][j] > 1) + yuv2rgb[i][j] = 1; + if (yuv2rgb[i][j] < 0) + yuv2rgb[i][j] = 0; + } + } + if (text == 0) { + GLfloat c0[4] = { yuv2rgb[0][0], yuv2rgb[1][0], yuv2rgb[2][0] }; + GLfloat c1[4] = { yuv2rgb[0][1], yuv2rgb[1][1], yuv2rgb[2][1] }; + GLfloat c2[4] = { yuv2rgb[0][2], yuv2rgb[1][2], yuv2rgb[2][2] }; + GLfloat c3[4] = { yuv2rgb[0][3], yuv2rgb[1][3], yuv2rgb[2][3] }; + if (!gl->BeginFragmentShader || !gl->EndFragmentShader || + !gl->SetFragmentShaderConstant || !gl->SampleMap || + !gl->ColorFragmentOp2 || !gl->ColorFragmentOp3) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Combiner (ATI) functions missing!\n"); + return; + } + gl->GetIntegerv(GL_NUM_FRAGMENT_REGISTERS_ATI, &i); + if (i < 3) + mp_msg(MSGT_VO, MSGL_ERR, + "[gl] 3 registers needed for YUV combiner (ATI) support (found %i)\n", i); + gl->BeginFragmentShader(); + gl->SetFragmentShaderConstant(GL_CON_0_ATI, c0); + gl->SetFragmentShaderConstant(GL_CON_1_ATI, c1); + gl->SetFragmentShaderConstant(GL_CON_2_ATI, c2); + gl->SetFragmentShaderConstant(GL_CON_3_ATI, c3); + gl->SampleMap(GL_REG_0_ATI, GL_TEXTURE0, GL_SWIZZLE_STR_ATI); + gl->SampleMap(GL_REG_1_ATI, GL_TEXTURE1, GL_SWIZZLE_STR_ATI); + gl->SampleMap(GL_REG_2_ATI, GL_TEXTURE2, GL_SWIZZLE_STR_ATI); + gl->ColorFragmentOp2(GL_MUL_ATI, GL_REG_1_ATI, GL_NONE, GL_NONE, + GL_REG_1_ATI, GL_NONE, GL_BIAS_BIT_ATI, + GL_CON_1_ATI, GL_NONE, GL_BIAS_BIT_ATI); + gl->ColorFragmentOp3(GL_MAD_ATI, GL_REG_2_ATI, GL_NONE, GL_NONE, + GL_REG_2_ATI, GL_NONE, GL_BIAS_BIT_ATI, + GL_CON_2_ATI, GL_NONE, GL_BIAS_BIT_ATI, + GL_REG_1_ATI, GL_NONE, GL_NONE); + gl->ColorFragmentOp3(GL_MAD_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE, + GL_REG_0_ATI, GL_NONE, GL_NONE, + GL_CON_0_ATI, GL_NONE, GL_BIAS_BIT_ATI, + GL_REG_2_ATI, GL_NONE, GL_NONE); + gl->ColorFragmentOp2(GL_ADD_ATI, GL_REG_0_ATI, GL_NONE, GL_8X_BIT_ATI, + GL_REG_0_ATI, GL_NONE, GL_NONE, + GL_CON_3_ATI, GL_NONE, GL_BIAS_BIT_ATI); + gl->EndFragmentShader(); + } else { + static const char template[] = + "!!ATIfs1.0\n" + "StartConstants;\n" + " CONSTANT c0 = {%e, %e, %e};\n" + " CONSTANT c1 = {%e, %e, %e};\n" + " CONSTANT c2 = {%e, %e, %e};\n" + " CONSTANT c3 = {%e, %e, %e};\n" + "EndConstants;\n" + "StartOutputPass;\n" + " SampleMap r0, t0.str;\n" + " SampleMap r1, t1.str;\n" + " SampleMap r2, t2.str;\n" + " MUL r1.rgb, r1.bias, c1.bias;\n" + " MAD r2.rgb, r2.bias, c2.bias, r1;\n" + " MAD r0.rgb, r0, c0.bias, r2;\n" + " ADD r0.rgb.8x, r0, c3.bias;\n" + "EndPass;\n"; + char buffer[512]; + snprintf(buffer, sizeof(buffer), template, + yuv2rgb[0][0], yuv2rgb[1][0], yuv2rgb[2][0], + yuv2rgb[0][1], yuv2rgb[1][1], yuv2rgb[2][1], + yuv2rgb[0][2], yuv2rgb[1][2], yuv2rgb[2][2], + yuv2rgb[0][3], yuv2rgb[1][3], yuv2rgb[2][3]); + mp_msg(MSGT_VO, MSGL_DBG2, "[gl] generated fragment program:\n%s\n", + buffer); + loadGPUProgram(gl, GL_TEXT_FRAGMENT_SHADER_ATI, buffer); + } +} + +/** + * \brief helper function for gen_spline_lookup_tex + * \param x subpixel-position ((0,1) range) to calculate weights for + * \param dst where to store transformed weights, must provide space for 4 GLfloats + * + * calculates the weights and stores them after appropriate transformation + * for the scaler fragment program. + */ +static void store_weights(float x, GLfloat *dst) +{ + float w0 = (((-1 * x + 3) * x - 3) * x + 1) / 6; + float w1 = (((3 * x - 6) * x + 0) * x + 4) / 6; + float w2 = (((-3 * x + 3) * x + 3) * x + 1) / 6; + float w3 = (((1 * x + 0) * x + 0) * x + 0) / 6; + *dst++ = 1 + x - w1 / (w0 + w1); + *dst++ = 1 - x + w3 / (w2 + w3); + *dst++ = w0 + w1; + *dst++ = 0; +} + +//! to avoid artefacts this should be rather large +#define LOOKUP_BSPLINE_RES (2 * 1024) +/** + * \brief creates the 1D lookup texture needed for fast higher-order filtering + * \param unit texture unit to attach texture to + */ +static void gen_spline_lookup_tex(GL *gl, GLenum unit) +{ + GLfloat *tex = calloc(4 * LOOKUP_BSPLINE_RES, sizeof(*tex)); + GLfloat *tp = tex; + int i; + for (i = 0; i < LOOKUP_BSPLINE_RES; i++) { + float x = (float)(i + 0.5) / LOOKUP_BSPLINE_RES; + store_weights(x, tp); + tp += 4; + } + store_weights(0, tex); + store_weights(1, &tex[4 * (LOOKUP_BSPLINE_RES - 1)]); + gl->ActiveTexture(unit); + gl->TexImage1D(GL_TEXTURE_1D, 0, GL_RGBA16, LOOKUP_BSPLINE_RES, 0, GL_RGBA, + GL_FLOAT, tex); + gl->TexParameterf(GL_TEXTURE_1D, GL_TEXTURE_PRIORITY, 1.0); + gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + gl->TexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); + gl->ActiveTexture(GL_TEXTURE0); + free(tex); +} + +static const char *bilin_filt_template = + "TEX yuv.%c, fragment.texcoord[%c], texture[%c], %s;\n"; + +#define BICUB_FILT_MAIN(textype) \ + /* first y-interpolation */ \ + "ADD coord, fragment.texcoord[%c].xyxy, cdelta.xyxw;\n" \ + "ADD coord2, fragment.texcoord[%c].xyxy, cdelta.zyzw;\n" \ + "TEX a.r, coord.xyxy, texture[%c], "textype ";\n" \ + "TEX a.g, coord.zwzw, texture[%c], "textype ";\n" \ + /* second y-interpolation */ \ + "TEX b.r, coord2.xyxy, texture[%c], "textype ";\n" \ + "TEX b.g, coord2.zwzw, texture[%c], "textype ";\n" \ + "LRP a.b, parmy.b, a.rrrr, a.gggg;\n" \ + "LRP a.a, parmy.b, b.rrrr, b.gggg;\n" \ + /* x-interpolation */ \ + "LRP yuv.%c, parmx.b, a.bbbb, a.aaaa;\n" + +static const char *bicub_filt_template_2D = + "MAD coord.xy, fragment.texcoord[%c], {%e, %e}, {0.5, 0.5};\n" + "TEX parmx, coord.x, texture[%c], 1D;\n" + "MUL cdelta.xz, parmx.rrgg, {-%e, 0, %e, 0};\n" + "TEX parmy, coord.y, texture[%c], 1D;\n" + "MUL cdelta.yw, parmy.rrgg, {0, -%e, 0, %e};\n" + BICUB_FILT_MAIN("2D"); + +static const char *bicub_filt_template_RECT = + "ADD coord, fragment.texcoord[%c], {0.5, 0.5};\n" + "TEX parmx, coord.x, texture[%c], 1D;\n" + "MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};\n" + "TEX parmy, coord.y, texture[%c], 1D;\n" + "MUL cdelta.yw, parmy.rrgg, {0, -1, 0, 1};\n" + BICUB_FILT_MAIN("RECT"); + +#define CALCWEIGHTS(t, s) \ + "MAD "t ", {-0.5, 0.1666, 0.3333, -0.3333}, "s ", {1, 0, -0.5, 0.5};\n" \ + "MAD "t ", "t ", "s ", {0, 0, -0.5, 0.5};\n" \ + "MAD "t ", "t ", "s ", {-0.6666, 0, 0.8333, 0.1666};\n" \ + "RCP a.x, "t ".z;\n" \ + "RCP a.y, "t ".w;\n" \ + "MAD "t ".xy, "t ".xyxy, a.xyxy, {1, 1, 0, 0};\n" \ + "ADD "t ".x, "t ".xxxx, "s ";\n" \ + "SUB "t ".y, "t ".yyyy, "s ";\n" + +static const char *bicub_notex_filt_template_2D = + "MAD coord.xy, fragment.texcoord[%c], {%e, %e}, {0.5, 0.5};\n" + "FRC coord.xy, coord.xyxy;\n" + CALCWEIGHTS("parmx", "coord.xxxx") + "MUL cdelta.xz, parmx.rrgg, {-%e, 0, %e, 0};\n" + CALCWEIGHTS("parmy", "coord.yyyy") + "MUL cdelta.yw, parmy.rrgg, {0, -%e, 0, %e};\n" + BICUB_FILT_MAIN("2D"); + +static const char *bicub_notex_filt_template_RECT = + "ADD coord, fragment.texcoord[%c], {0.5, 0.5};\n" + "FRC coord.xy, coord.xyxy;\n" + CALCWEIGHTS("parmx", "coord.xxxx") + "MUL cdelta.xz, parmx.rrgg, {-1, 0, 1, 0};\n" + CALCWEIGHTS("parmy", "coord.yyyy") + "MUL cdelta.yw, parmy.rrgg, {0, -1, 0, 1};\n" + BICUB_FILT_MAIN("RECT"); + +#define BICUB_X_FILT_MAIN(textype) \ + "ADD coord.xy, fragment.texcoord[%c].xyxy, cdelta.xyxy;\n" \ + "ADD coord2.xy, fragment.texcoord[%c].xyxy, cdelta.zyzy;\n" \ + "TEX a.r, coord, texture[%c], "textype ";\n" \ + "TEX b.r, coord2, texture[%c], "textype ";\n" \ + /* x-interpolation */ \ + "LRP yuv.%c, parmx.b, a.rrrr, b.rrrr;\n" + +static const char *bicub_x_filt_template_2D = + "MAD coord.x, fragment.texcoord[%c], {%e}, {0.5};\n" + "TEX parmx, coord, texture[%c], 1D;\n" + "MUL cdelta.xyz, parmx.rrgg, {-%e, 0, %e};\n" + BICUB_X_FILT_MAIN("2D"); + +static const char *bicub_x_filt_template_RECT = + "ADD coord.x, fragment.texcoord[%c], {0.5};\n" + "TEX parmx, coord, texture[%c], 1D;\n" + "MUL cdelta.xyz, parmx.rrgg, {-1, 0, 1};\n" + BICUB_X_FILT_MAIN("RECT"); + +static const char *unsharp_filt_template = + "PARAM dcoord%c = {%e, %e, %e, %e};\n" + "ADD coord, fragment.texcoord[%c].xyxy, dcoord%c;\n" + "SUB coord2, fragment.texcoord[%c].xyxy, dcoord%c;\n" + "TEX a.r, fragment.texcoord[%c], texture[%c], %s;\n" + "TEX b.r, coord.xyxy, texture[%c], %s;\n" + "TEX b.g, coord.zwzw, texture[%c], %s;\n" + "ADD b.r, b.r, b.g;\n" + "TEX b.b, coord2.xyxy, texture[%c], %s;\n" + "TEX b.g, coord2.zwzw, texture[%c], %s;\n" + "DP3 b, b, {0.25, 0.25, 0.25};\n" + "SUB b.r, a.r, b.r;\n" + "MAD yuv.%c, b.r, {%e}, a.r;\n"; + +static const char *unsharp_filt_template2 = + "PARAM dcoord%c = {%e, %e, %e, %e};\n" + "PARAM dcoord2%c = {%e, 0, 0, %e};\n" + "ADD coord, fragment.texcoord[%c].xyxy, dcoord%c;\n" + "SUB coord2, fragment.texcoord[%c].xyxy, dcoord%c;\n" + "TEX a.r, fragment.texcoord[%c], texture[%c], %s;\n" + "TEX b.r, coord.xyxy, texture[%c], %s;\n" + "TEX b.g, coord.zwzw, texture[%c], %s;\n" + "ADD b.r, b.r, b.g;\n" + "TEX b.b, coord2.xyxy, texture[%c], %s;\n" + "TEX b.g, coord2.zwzw, texture[%c], %s;\n" + "ADD b.r, b.r, b.b;\n" + "ADD b.a, b.r, b.g;\n" + "ADD coord, fragment.texcoord[%c].xyxy, dcoord2%c;\n" + "SUB coord2, fragment.texcoord[%c].xyxy, dcoord2%c;\n" + "TEX b.r, coord.xyxy, texture[%c], %s;\n" + "TEX b.g, coord.zwzw, texture[%c], %s;\n" + "ADD b.r, b.r, b.g;\n" + "TEX b.b, coord2.xyxy, texture[%c], %s;\n" + "TEX b.g, coord2.zwzw, texture[%c], %s;\n" + "DP4 b.r, b, {-0.1171875, -0.1171875, -0.1171875, -0.09765625};\n" + "MAD b.r, a.r, {0.859375}, b.r;\n" + "MAD yuv.%c, b.r, {%e}, a.r;\n"; + +static const char *yuv_prog_template = + "PARAM ycoef = {%e, %e, %e};\n" + "PARAM ucoef = {%e, %e, %e};\n" + "PARAM vcoef = {%e, %e, %e};\n" + "PARAM offsets = {%e, %e, %e};\n" + "TEMP res;\n" + "MAD res.rgb, yuv.rrrr, ycoef, offsets;\n" + "MAD res.rgb, yuv.gggg, ucoef, res;\n" + "MAD result.color.rgb, yuv.bbbb, vcoef, res;\n" + "END"; + +static const char *yuv_pow_prog_template = + "PARAM ycoef = {%e, %e, %e};\n" + "PARAM ucoef = {%e, %e, %e};\n" + "PARAM vcoef = {%e, %e, %e};\n" + "PARAM offsets = {%e, %e, %e};\n" + "PARAM gamma = {%e, %e, %e};\n" + "TEMP res;\n" + "MAD res.rgb, yuv.rrrr, ycoef, offsets;\n" + "MAD res.rgb, yuv.gggg, ucoef, res;\n" + "MAD_SAT res.rgb, yuv.bbbb, vcoef, res;\n" + "POW result.color.r, res.r, gamma.r;\n" + "POW result.color.g, res.g, gamma.g;\n" + "POW result.color.b, res.b, gamma.b;\n" + "END"; + +static const char *yuv_lookup_prog_template = + "PARAM ycoef = {%e, %e, %e, 0};\n" + "PARAM ucoef = {%e, %e, %e, 0};\n" + "PARAM vcoef = {%e, %e, %e, 0};\n" + "PARAM offsets = {%e, %e, %e, 0.125};\n" + "TEMP res;\n" + "MAD res, yuv.rrrr, ycoef, offsets;\n" + "MAD res.rgb, yuv.gggg, ucoef, res;\n" + "MAD res.rgb, yuv.bbbb, vcoef, res;\n" + "TEX result.color.r, res.raaa, texture[%c], 2D;\n" + "ADD res.a, res.a, 0.25;\n" + "TEX result.color.g, res.gaaa, texture[%c], 2D;\n" + "ADD res.a, res.a, 0.25;\n" + "TEX result.color.b, res.baaa, texture[%c], 2D;\n" + "END"; + +static const char *yuv_lookup3d_prog_template = + "TEX result.color, yuv, texture[%c], 3D;\n" + "END"; + +/** + * \brief creates and initializes helper textures needed for scaling texture read + * \param scaler scaler type to create texture for + * \param texu contains next free texture unit number + * \param texs texture unit ids for the scaler are stored in this array + */ +static void create_scaler_textures(GL *gl, int scaler, int *texu, char *texs) +{ + switch (scaler) { + case YUV_SCALER_BILIN: + case YUV_SCALER_BICUB_NOTEX: + case YUV_SCALER_UNSHARP: + case YUV_SCALER_UNSHARP2: + break; + case YUV_SCALER_BICUB: + case YUV_SCALER_BICUB_X: + texs[0] = (*texu)++; + gen_spline_lookup_tex(gl, GL_TEXTURE0 + texs[0]); + texs[0] += '0'; + break; + default: + mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown scaler type %i\n", scaler); + } +} + +//! resolution of texture for gamma lookup table +#define LOOKUP_RES 512 +//! resolution for 3D yuv->rgb conversion lookup table +#define LOOKUP_3DRES 32 +/** + * \brief creates and initializes helper textures needed for yuv conversion + * \param params struct containing parameters like brightness, gamma, ... + * \param texu contains next free texture unit number + * \param texs texture unit ids for the conversion are stored in this array + */ +static void create_conv_textures(GL *gl, gl_conversion_params_t *params, + int *texu, char *texs) +{ + unsigned char *lookup_data = NULL; + int conv = YUV_CONVERSION(params->type); + switch (conv) { + case YUV_CONVERSION_FRAGMENT: + case YUV_CONVERSION_FRAGMENT_POW: + break; + case YUV_CONVERSION_FRAGMENT_LOOKUP: + texs[0] = (*texu)++; + gl->ActiveTexture(GL_TEXTURE0 + texs[0]); + lookup_data = malloc(4 * LOOKUP_RES); + mp_gen_gamma_map(lookup_data, LOOKUP_RES, params->csp_params.rgamma); + mp_gen_gamma_map(&lookup_data[LOOKUP_RES], LOOKUP_RES, + params->csp_params.ggamma); + mp_gen_gamma_map(&lookup_data[2 * LOOKUP_RES], LOOKUP_RES, + params->csp_params.bgamma); + glCreateClearTex(gl, GL_TEXTURE_2D, GL_LUMINANCE8, GL_LUMINANCE, + GL_UNSIGNED_BYTE, GL_LINEAR, LOOKUP_RES, 4, 0); + glUploadTex(gl, GL_TEXTURE_2D, GL_LUMINANCE, GL_UNSIGNED_BYTE, + lookup_data, LOOKUP_RES, 0, 0, LOOKUP_RES, 4, 0); + gl->ActiveTexture(GL_TEXTURE0); + texs[0] += '0'; + break; + case YUV_CONVERSION_FRAGMENT_LOOKUP3D: + { + int sz = LOOKUP_3DRES + 2; // texture size including borders + if (!gl->TexImage3D) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] Missing 3D texture function!\n"); + break; + } + texs[0] = (*texu)++; + gl->ActiveTexture(GL_TEXTURE0 + texs[0]); + lookup_data = malloc(3 * sz * sz * sz); + mp_gen_yuv2rgb_map(¶ms->csp_params, lookup_data, LOOKUP_3DRES); + glAdjustAlignment(gl, sz); + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0); + gl->TexImage3D(GL_TEXTURE_3D, 0, 3, sz, sz, sz, 1, + GL_RGB, GL_UNSIGNED_BYTE, lookup_data); + gl->TexParameterf(GL_TEXTURE_3D, GL_TEXTURE_PRIORITY, 1.0); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP); + gl->TexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP); + gl->ActiveTexture(GL_TEXTURE0); + texs[0] += '0'; + } + break; + default: + mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown conversion type %i\n", conv); + } + free(lookup_data); +} + +/** + * \brief adds a scaling texture read at the current fragment program position + * \param scaler type of scaler to insert + * \param prog_pos current position in fragment program + * \param remain how many bytes remain in the buffer given by prog_pos + * \param texs array containing the texture unit identifiers for this scaler + * \param in_tex texture unit the scaler should read from + * \param out_comp component of the yuv variable the scaler stores the result in + * \param rect if rectangular (pixel) adressing should be used for in_tex + * \param texw width of the in_tex texture + * \param texh height of the in_tex texture + * \param strength strength of filter effect if the scaler does some kind of filtering + */ +static void add_scaler(int scaler, char **prog_pos, int *remain, char *texs, + char in_tex, char out_comp, int rect, int texw, int texh, + double strength) +{ + const char *ttype = rect ? "RECT" : "2D"; + const float ptw = rect ? 1.0 : 1.0 / texw; + const float pth = rect ? 1.0 : 1.0 / texh; + switch (scaler) { + case YUV_SCALER_BILIN: + snprintf(*prog_pos, *remain, bilin_filt_template, out_comp, in_tex, + in_tex, ttype); + break; + case YUV_SCALER_BICUB: + if (rect) + snprintf(*prog_pos, *remain, bicub_filt_template_RECT, + in_tex, texs[0], texs[0], + in_tex, in_tex, in_tex, in_tex, in_tex, in_tex, out_comp); + else + snprintf(*prog_pos, *remain, bicub_filt_template_2D, + in_tex, (float)texw, (float)texh, + texs[0], ptw, ptw, texs[0], pth, pth, + in_tex, in_tex, in_tex, in_tex, in_tex, in_tex, out_comp); + break; + case YUV_SCALER_BICUB_X: + if (rect) + snprintf(*prog_pos, *remain, bicub_x_filt_template_RECT, + in_tex, texs[0], + in_tex, in_tex, in_tex, in_tex, out_comp); + else + snprintf(*prog_pos, *remain, bicub_x_filt_template_2D, + in_tex, (float)texw, + texs[0], ptw, ptw, + in_tex, in_tex, in_tex, in_tex, out_comp); + break; + case YUV_SCALER_BICUB_NOTEX: + if (rect) + snprintf(*prog_pos, *remain, bicub_notex_filt_template_RECT, + in_tex, + in_tex, in_tex, in_tex, in_tex, in_tex, in_tex, out_comp); + else + snprintf(*prog_pos, *remain, bicub_notex_filt_template_2D, + in_tex, (float)texw, (float)texh, ptw, ptw, pth, pth, + in_tex, in_tex, in_tex, in_tex, in_tex, in_tex, out_comp); + break; + case YUV_SCALER_UNSHARP: + snprintf(*prog_pos, *remain, unsharp_filt_template, + out_comp, 0.5 * ptw, 0.5 * pth, 0.5 * ptw, -0.5 * pth, + in_tex, out_comp, in_tex, out_comp, in_tex, + in_tex, ttype, in_tex, ttype, in_tex, ttype, in_tex, ttype, + in_tex, ttype, out_comp, strength); + break; + case YUV_SCALER_UNSHARP2: + snprintf(*prog_pos, *remain, unsharp_filt_template2, + out_comp, 1.2 * ptw, 1.2 * pth, 1.2 * ptw, -1.2 * pth, + out_comp, 1.5 * ptw, 1.5 * pth, + in_tex, out_comp, in_tex, out_comp, in_tex, + in_tex, ttype, in_tex, ttype, in_tex, ttype, in_tex, ttype, + in_tex, ttype, in_tex, out_comp, in_tex, out_comp, + in_tex, ttype, in_tex, ttype, in_tex, ttype, + in_tex, ttype, out_comp, strength); + break; + } + *remain -= strlen(*prog_pos); + *prog_pos += strlen(*prog_pos); +} + +static const struct { + const char *name; + GLenum cur; + GLenum max; +} progstats[] = { + {"instructions", 0x88A0, 0x88A1}, + {"native instructions", 0x88A2, 0x88A3}, + {"temporaries", 0x88A4, 0x88A5}, + {"native temporaries", 0x88A6, 0x88A7}, + {"parameters", 0x88A8, 0x88A9}, + {"native parameters", 0x88AA, 0x88AB}, + {"attribs", 0x88AC, 0x88AD}, + {"native attribs", 0x88AE, 0x88AF}, + {"ALU instructions", 0x8805, 0x880B}, + {"TEX instructions", 0x8806, 0x880C}, + {"TEX indirections", 0x8807, 0x880D}, + {"native ALU instructions", 0x8808, 0x880E}, + {"native TEX instructions", 0x8809, 0x880F}, + {"native TEX indirections", 0x880A, 0x8810}, + {NULL, 0, 0} +}; + +/** + * \brief load the specified GPU Program + * \param target program target to load into, only GL_FRAGMENT_PROGRAM is tested + * \param prog program string + * \return 1 on success, 0 otherwise + */ +int loadGPUProgram(GL *gl, GLenum target, char *prog) +{ + int i; + GLint cur = 0, max = 0, err = 0; + if (!gl->ProgramString) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] Missing GPU program function\n"); + return 0; + } + gl->ProgramString(target, GL_PROGRAM_FORMAT_ASCII, strlen(prog), prog); + gl->GetIntegerv(GL_PROGRAM_ERROR_POSITION, &err); + if (err != -1) { + mp_msg(MSGT_VO, MSGL_ERR, + "[gl] Error compiling fragment program, make sure your card supports\n" + "[gl] GL_ARB_fragment_program (use glxinfo to check).\n" + "[gl] Error message:\n %s at %.10s\n", + gl->GetString(GL_PROGRAM_ERROR_STRING), &prog[err]); + return 0; + } + if (!gl->GetProgramiv || !mp_msg_test(MSGT_VO, MSGL_DBG2)) + return 1; + mp_msg(MSGT_VO, MSGL_V, "[gl] Program statistics:\n"); + for (i = 0; progstats[i].name; i++) { + gl->GetProgramiv(target, progstats[i].cur, &cur); + gl->GetProgramiv(target, progstats[i].max, &max); + mp_msg(MSGT_VO, MSGL_V, "[gl] %s: %i/%i\n", progstats[i].name, cur, + max); + } + return 1; +} + +#define MAX_PROGSZ (1024 * 1024) + +/** + * \brief setup a fragment program that will do YUV->RGB conversion + * \param parms struct containing parameters like conversion and scaler type, + * brightness, ... + */ +static void glSetupYUVFragprog(GL *gl, gl_conversion_params_t *params) +{ + int type = params->type; + int texw = params->texw; + int texh = params->texh; + int rect = params->target == GL_TEXTURE_RECTANGLE; + static const char prog_hdr[] = + "!!ARBfp1.0\n" + "OPTION ARB_precision_hint_fastest;\n" + // all scaler variables must go here so they aren't defined + // multiple times when the same scaler is used more than once + "TEMP coord, coord2, cdelta, parmx, parmy, a, b, yuv;\n"; + int prog_remain; + char *yuv_prog, *prog_pos; + int cur_texu = 3; + char lum_scale_texs[1]; + char chrom_scale_texs[1]; + char conv_texs[1]; + GLint i; + // this is the conversion matrix, with y, u, v factors + // for red, green, blue and the constant offsets + float yuv2rgb[3][4]; + create_conv_textures(gl, params, &cur_texu, conv_texs); + create_scaler_textures(gl, YUV_LUM_SCALER(type), &cur_texu, lum_scale_texs); + if (YUV_CHROM_SCALER(type) == YUV_LUM_SCALER(type)) + memcpy(chrom_scale_texs, lum_scale_texs, sizeof(chrom_scale_texs)); + else + create_scaler_textures(gl, YUV_CHROM_SCALER(type), &cur_texu, + chrom_scale_texs); + gl->GetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &i); + if (i < cur_texu) + mp_msg(MSGT_VO, MSGL_ERR, + "[gl] %i texture units needed for this type of YUV fragment support (found %i)\n", + cur_texu, i); + if (!gl->ProgramString) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] ProgramString function missing!\n"); + return; + } + yuv_prog = malloc(MAX_PROGSZ); + strcpy(yuv_prog, prog_hdr); + prog_pos = yuv_prog + sizeof(prog_hdr) - 1; + prog_remain = MAX_PROGSZ - sizeof(prog_hdr); + add_scaler(YUV_LUM_SCALER(type), &prog_pos, &prog_remain, lum_scale_texs, + '0', 'r', rect, texw, texh, params->filter_strength); + add_scaler(YUV_CHROM_SCALER(type), &prog_pos, &prog_remain, + chrom_scale_texs, '1', 'g', rect, params->chrom_texw, + params->chrom_texh, params->filter_strength); + add_scaler(YUV_CHROM_SCALER(type), &prog_pos, &prog_remain, + chrom_scale_texs, '2', 'b', rect, params->chrom_texw, + params->chrom_texh, params->filter_strength); + mp_get_yuv2rgb_coeffs(¶ms->csp_params, yuv2rgb); + switch (YUV_CONVERSION(type)) { + case YUV_CONVERSION_FRAGMENT: + snprintf(prog_pos, prog_remain, yuv_prog_template, + yuv2rgb[ROW_R][COL_Y], yuv2rgb[ROW_G][COL_Y], yuv2rgb[ROW_B][COL_Y], + yuv2rgb[ROW_R][COL_U], yuv2rgb[ROW_G][COL_U], yuv2rgb[ROW_B][COL_U], + yuv2rgb[ROW_R][COL_V], yuv2rgb[ROW_G][COL_V], yuv2rgb[ROW_B][COL_V], + yuv2rgb[ROW_R][COL_C], yuv2rgb[ROW_G][COL_C], yuv2rgb[ROW_B][COL_C]); + break; + case YUV_CONVERSION_FRAGMENT_POW: + snprintf(prog_pos, prog_remain, yuv_pow_prog_template, + yuv2rgb[ROW_R][COL_Y], yuv2rgb[ROW_G][COL_Y], yuv2rgb[ROW_B][COL_Y], + yuv2rgb[ROW_R][COL_U], yuv2rgb[ROW_G][COL_U], yuv2rgb[ROW_B][COL_U], + yuv2rgb[ROW_R][COL_V], yuv2rgb[ROW_G][COL_V], yuv2rgb[ROW_B][COL_V], + yuv2rgb[ROW_R][COL_C], yuv2rgb[ROW_G][COL_C], yuv2rgb[ROW_B][COL_C], + (float)1.0 / params->csp_params.rgamma, + (float)1.0 / params->csp_params.bgamma, + (float)1.0 / params->csp_params.bgamma); + break; + case YUV_CONVERSION_FRAGMENT_LOOKUP: + snprintf(prog_pos, prog_remain, yuv_lookup_prog_template, + yuv2rgb[ROW_R][COL_Y], yuv2rgb[ROW_G][COL_Y], yuv2rgb[ROW_B][COL_Y], + yuv2rgb[ROW_R][COL_U], yuv2rgb[ROW_G][COL_U], yuv2rgb[ROW_B][COL_U], + yuv2rgb[ROW_R][COL_V], yuv2rgb[ROW_G][COL_V], yuv2rgb[ROW_B][COL_V], + yuv2rgb[ROW_R][COL_C], yuv2rgb[ROW_G][COL_C], yuv2rgb[ROW_B][COL_C], + conv_texs[0], conv_texs[0], conv_texs[0]); + break; + case YUV_CONVERSION_FRAGMENT_LOOKUP3D: + snprintf(prog_pos, prog_remain, yuv_lookup3d_prog_template, + conv_texs[0]); + break; + default: + mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown conversion type %i\n", + YUV_CONVERSION(type)); + break; + } + mp_msg(MSGT_VO, MSGL_DBG2, "[gl] generated fragment program:\n%s\n", + yuv_prog); + loadGPUProgram(gl, GL_FRAGMENT_PROGRAM, yuv_prog); + free(yuv_prog); +} + +/** + * \brief detect the best YUV->RGB conversion method available + */ +int glAutodetectYUVConversion(GL *gl) +{ + const char *extensions = gl->GetString(GL_EXTENSIONS); + if (!extensions || !gl->MultiTexCoord2f) + return YUV_CONVERSION_NONE; + if (strstr(extensions, "GL_ARB_fragment_program")) + return YUV_CONVERSION_FRAGMENT; + if (strstr(extensions, "GL_ATI_text_fragment_shader")) + return YUV_CONVERSION_TEXT_FRAGMENT; + if (strstr(extensions, "GL_ATI_fragment_shader")) + return YUV_CONVERSION_COMBINERS_ATI; + return YUV_CONVERSION_NONE; +} + +/** + * \brief setup YUV->RGB conversion + * \param parms struct containing parameters like conversion and scaler type, + * brightness, ... + * \ingroup glconversion + */ +void glSetupYUVConversion(GL *gl, gl_conversion_params_t *params) +{ + float uvcos = params->csp_params.saturation * cos(params->csp_params.hue); + float uvsin = params->csp_params.saturation * sin(params->csp_params.hue); + if (params->chrom_texw == 0) + params->chrom_texw = 1; + if (params->chrom_texh == 0) + params->chrom_texh = 1; + switch (YUV_CONVERSION(params->type)) { + case YUV_CONVERSION_COMBINERS: + glSetupYUVCombiners(gl, uvcos, uvsin); + break; + case YUV_CONVERSION_COMBINERS_ATI: + glSetupYUVFragmentATI(gl, ¶ms->csp_params, 0); + break; + case YUV_CONVERSION_TEXT_FRAGMENT: + glSetupYUVFragmentATI(gl, ¶ms->csp_params, 1); + break; + case YUV_CONVERSION_FRAGMENT_LOOKUP: + case YUV_CONVERSION_FRAGMENT_LOOKUP3D: + case YUV_CONVERSION_FRAGMENT: + case YUV_CONVERSION_FRAGMENT_POW: + glSetupYUVFragprog(gl, params); + break; + case YUV_CONVERSION_NONE: + break; + default: + mp_msg(MSGT_VO, MSGL_ERR, "[gl] unknown conversion type %i\n", + YUV_CONVERSION(params->type)); + } +} + +/** + * \brief enable the specified YUV conversion + * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D) + * \param type type of YUV conversion + * \ingroup glconversion + */ +void glEnableYUVConversion(GL *gl, GLenum target, int type) +{ + switch (YUV_CONVERSION(type)) { + case YUV_CONVERSION_COMBINERS: + gl->ActiveTexture(GL_TEXTURE1); + gl->Enable(target); + gl->ActiveTexture(GL_TEXTURE2); + gl->Enable(target); + gl->ActiveTexture(GL_TEXTURE0); + gl->Enable(GL_REGISTER_COMBINERS_NV); + break; + case YUV_CONVERSION_COMBINERS_ATI: + gl->ActiveTexture(GL_TEXTURE1); + gl->Enable(target); + gl->ActiveTexture(GL_TEXTURE2); + gl->Enable(target); + gl->ActiveTexture(GL_TEXTURE0); + gl->Enable(GL_FRAGMENT_SHADER_ATI); + break; + case YUV_CONVERSION_TEXT_FRAGMENT: + gl->ActiveTexture(GL_TEXTURE1); + gl->Enable(target); + gl->ActiveTexture(GL_TEXTURE2); + gl->Enable(target); + gl->ActiveTexture(GL_TEXTURE0); + gl->Enable(GL_TEXT_FRAGMENT_SHADER_ATI); + break; + case YUV_CONVERSION_FRAGMENT_LOOKUP3D: + case YUV_CONVERSION_FRAGMENT_LOOKUP: + case YUV_CONVERSION_FRAGMENT_POW: + case YUV_CONVERSION_FRAGMENT: + case YUV_CONVERSION_NONE: + gl->Enable(GL_FRAGMENT_PROGRAM); + break; + } +} + +/** + * \brief disable the specified YUV conversion + * \param target texture target for Y, U and V textures (e.g. GL_TEXTURE_2D) + * \param type type of YUV conversion + * \ingroup glconversion + */ +void glDisableYUVConversion(GL *gl, GLenum target, int type) +{ + switch (YUV_CONVERSION(type)) { + case YUV_CONVERSION_COMBINERS: + gl->ActiveTexture(GL_TEXTURE1); + gl->Disable(target); + gl->ActiveTexture(GL_TEXTURE2); + gl->Disable(target); + gl->ActiveTexture(GL_TEXTURE0); + gl->Disable(GL_REGISTER_COMBINERS_NV); + break; + case YUV_CONVERSION_COMBINERS_ATI: + gl->ActiveTexture(GL_TEXTURE1); + gl->Disable(target); + gl->ActiveTexture(GL_TEXTURE2); + gl->Disable(target); + gl->ActiveTexture(GL_TEXTURE0); + gl->Disable(GL_FRAGMENT_SHADER_ATI); + break; + case YUV_CONVERSION_TEXT_FRAGMENT: + gl->Disable(GL_TEXT_FRAGMENT_SHADER_ATI); + // HACK: at least the Mac OS X 10.5 PPC Radeon drivers are broken and + // without this disable the texture units while the program is still + // running (10.4 PPC seems to work without this though). + gl->Flush(); + gl->ActiveTexture(GL_TEXTURE1); + gl->Disable(target); + gl->ActiveTexture(GL_TEXTURE2); + gl->Disable(target); + gl->ActiveTexture(GL_TEXTURE0); + break; + case YUV_CONVERSION_FRAGMENT_LOOKUP3D: + case YUV_CONVERSION_FRAGMENT_LOOKUP: + case YUV_CONVERSION_FRAGMENT_POW: + case YUV_CONVERSION_FRAGMENT: + case YUV_CONVERSION_NONE: + gl->Disable(GL_FRAGMENT_PROGRAM); + break; + } +} + +void glEnable3DLeft(GL *gl, int type) +{ + GLint buffer; + switch (type) { + case GL_3D_RED_CYAN: + gl->ColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_FALSE); + break; + case GL_3D_GREEN_MAGENTA: + gl->ColorMask(GL_FALSE, GL_TRUE, GL_FALSE, GL_FALSE); + break; + case GL_3D_QUADBUFFER: + gl->GetIntegerv(GL_DRAW_BUFFER, &buffer); + switch (buffer) { + case GL_FRONT: + case GL_FRONT_LEFT: + case GL_FRONT_RIGHT: + buffer = GL_FRONT_LEFT; + break; + case GL_BACK: + case GL_BACK_LEFT: + case GL_BACK_RIGHT: + buffer = GL_BACK_LEFT; + break; + } + gl->DrawBuffer(buffer); + break; + } +} + +void glEnable3DRight(GL *gl, int type) +{ + GLint buffer; + switch (type) { + case GL_3D_RED_CYAN: + gl->ColorMask(GL_FALSE, GL_TRUE, GL_TRUE, GL_FALSE); + break; + case GL_3D_GREEN_MAGENTA: + gl->ColorMask(GL_TRUE, GL_FALSE, GL_TRUE, GL_FALSE); + break; + case GL_3D_QUADBUFFER: + gl->GetIntegerv(GL_DRAW_BUFFER, &buffer); + switch (buffer) { + case GL_FRONT: + case GL_FRONT_LEFT: + case GL_FRONT_RIGHT: + buffer = GL_FRONT_RIGHT; + break; + case GL_BACK: + case GL_BACK_LEFT: + case GL_BACK_RIGHT: + buffer = GL_BACK_RIGHT; + break; + } + gl->DrawBuffer(buffer); + break; + } +} + +void glDisable3D(GL *gl, int type) +{ + GLint buffer; + switch (type) { + case GL_3D_RED_CYAN: + case GL_3D_GREEN_MAGENTA: + gl->ColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + break; + case GL_3D_QUADBUFFER: + gl->DrawBuffer(vo_doublebuffering ? GL_BACK : GL_FRONT); + gl->GetIntegerv(GL_DRAW_BUFFER, &buffer); + switch (buffer) { + case GL_FRONT: + case GL_FRONT_LEFT: + case GL_FRONT_RIGHT: + buffer = GL_FRONT; + break; + case GL_BACK: + case GL_BACK_LEFT: + case GL_BACK_RIGHT: + buffer = GL_BACK; + break; + } + gl->DrawBuffer(buffer); + break; + } +} + +/** + * \brief draw a texture part at given 2D coordinates + * \param x screen top coordinate + * \param y screen left coordinate + * \param w screen width coordinate + * \param h screen height coordinate + * \param tx texture top coordinate in pixels + * \param ty texture left coordinate in pixels + * \param tw texture part width in pixels + * \param th texture part height in pixels + * \param sx width of texture in pixels + * \param sy height of texture in pixels + * \param rect_tex whether this texture uses texture_rectangle extension + * \param is_yv12 if != 0, also draw the textures from units 1 and 2, + * bits 8 - 15 and 16 - 23 specify the x and y scaling of those textures + * \param flip flip the texture upside down + * \ingroup gltexture + */ +void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h, + GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th, + int sx, int sy, int rect_tex, int is_yv12, int flip) +{ + int chroma_x_shift = (is_yv12 >> 8) & 31; + int chroma_y_shift = (is_yv12 >> 16) & 31; + GLfloat xscale = 1 << chroma_x_shift; + GLfloat yscale = 1 << chroma_y_shift; + GLfloat tx2 = tx / xscale, ty2 = ty / yscale, tw2 = tw / xscale, th2 = th / yscale; + if (!rect_tex) { + tx /= sx; + ty /= sy; + tw /= sx; + th /= sy; + tx2 = tx, ty2 = ty, tw2 = tw, th2 = th; + } + if (flip) { + y += h; + h = -h; + } + gl->Begin(GL_QUADS); + gl->TexCoord2f(tx, ty); + if (is_yv12) { + gl->MultiTexCoord2f(GL_TEXTURE1, tx2, ty2); + gl->MultiTexCoord2f(GL_TEXTURE2, tx2, ty2); + } + gl->Vertex2f(x, y); + gl->TexCoord2f(tx, ty + th); + if (is_yv12) { + gl->MultiTexCoord2f(GL_TEXTURE1, tx2, ty2 + th2); + gl->MultiTexCoord2f(GL_TEXTURE2, tx2, ty2 + th2); + } + gl->Vertex2f(x, y + h); + gl->TexCoord2f(tx + tw, ty + th); + if (is_yv12) { + gl->MultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2 + th2); + gl->MultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2 + th2); + } + gl->Vertex2f(x + w, y + h); + gl->TexCoord2f(tx + tw, ty); + if (is_yv12) { + gl->MultiTexCoord2f(GL_TEXTURE1, tx2 + tw2, ty2); + gl->MultiTexCoord2f(GL_TEXTURE2, tx2 + tw2, ty2); + } + gl->Vertex2f(x + w, y); + gl->End(); +} + +#ifdef CONFIG_GL_WIN32 +#include "w32_common.h" +/** + * \brief little helper since wglGetProcAddress definition does not fit our + * getProcAddress + * \param procName name of function to look up + * \return function pointer returned by wglGetProcAddress + */ +static void *w32gpa(const GLubyte *procName) +{ + HMODULE oglmod; + void *res = wglGetProcAddress(procName); + if (res) + return res; + oglmod = GetModuleHandle("opengl32.dll"); + return GetProcAddress(oglmod, procName); +} + +static int setGlWindow_w32(MPGLContext *ctx) +{ + HWND win = vo_w32_window; + int *vinfo = &ctx->vinfo.w32; + HGLRC *context = &ctx->context.w32; + int new_vinfo; + HDC windc = vo_w32_get_dc(win); + HGLRC new_context = 0; + int keep_context = 0; + int res = SET_WINDOW_FAILED; + GL *gl = ctx->gl; + + // should only be needed when keeping context, but not doing glFinish + // can cause flickering even when we do not keep it. + if (*context) + gl->Finish(); + new_vinfo = GetPixelFormat(windc); + if (*context && *vinfo && new_vinfo && *vinfo == new_vinfo) { + // we can keep the wglContext + new_context = *context; + keep_context = 1; + } else { + // create a context + new_context = wglCreateContext(windc); + if (!new_context) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GL context!\n"); + goto out; + } + } + + // set context + if (!wglMakeCurrent(windc, new_context)) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL context!\n"); + if (!keep_context) + wglDeleteContext(new_context); + goto out; + } + + // set new values + vo_w32_window = win; + { + RECT rect; + GetClientRect(win, &rect); + ctx->vo->dwidth = rect.right; + ctx->vo->dheight = rect.bottom; + } + if (!keep_context) { + if (*context) + wglDeleteContext(*context); + *context = new_context; + *vinfo = new_vinfo; + getFunctions(gl, w32gpa, NULL); + + // and inform that reinit is neccessary + res = SET_WINDOW_REINIT; + } else + res = SET_WINDOW_OK; + +out: + vo_w32_release_dc(win, windc); + return res; +} + +static void releaseGlContext_w32(MPGLContext *ctx) +{ + int *vinfo = &ctx->vinfo.w32; + HGLRC *context = &ctx->context.w32; + *vinfo = 0; + if (*context) { + wglMakeCurrent(0, 0); + wglDeleteContext(*context); + } + *context = 0; +} + +static void swapGlBuffers_w32(MPGLContext *ctx) +{ + HDC vo_hdc = vo_w32_get_dc(vo_w32_window); + SwapBuffers(vo_hdc); + vo_w32_release_dc(vo_w32_window, vo_hdc); +} + +//trivial wrappers (w32 code uses old vo API) +static void new_vo_w32_ontop(struct vo *vo) { vo_w32_ontop(); } +static void new_vo_w32_border(struct vo *vo) { vo_w32_border(); } +static void new_vo_w32_fullscreen(struct vo *vo) { vo_w32_fullscreen(); } +static int new_vo_w32_check_events(struct vo *vo) { return vo_w32_check_events(); } +static void new_w32_update_xinerama_info(struct vo *vo) { w32_update_xinerama_info(); } +#endif +#ifdef CONFIG_GL_X11 +#include "x11_common.h" + +/** + * \brief Returns the XVisualInfo associated with Window win. + * \param win Window whose XVisualInfo is returne. + * \return XVisualInfo of the window. Caller must use XFree to free it. + */ +static XVisualInfo *getWindowVisualInfo(MPGLContext *ctx, Window win) +{ + XWindowAttributes xw_attr; + XVisualInfo vinfo_template; + int tmp; + XGetWindowAttributes(ctx->vo->x11->display, win, &xw_attr); + vinfo_template.visualid = XVisualIDFromVisual(xw_attr.visual); + return XGetVisualInfo(ctx->vo->x11->display, VisualIDMask, &vinfo_template, &tmp); +} + +static void appendstr(char **dst, const char *str) +{ + int newsize; + char *newstr; + if (!str) + return; + newsize = strlen(*dst) + 1 + strlen(str) + 1; + newstr = realloc(*dst, newsize); + if (!newstr) + return; + *dst = newstr; + strcat(*dst, " "); + strcat(*dst, str); +} + +/** + * \brief Changes the window in which video is displayed. + * If possible only transfers the context to the new window, otherwise + * creates a new one, which must be initialized by the caller. + * \param vinfo Currently used visual. + * \param context Currently used context. + * \param win window that should be used for drawing. + * \return one of SET_WINDOW_FAILED, SET_WINDOW_OK or SET_WINDOW_REINIT. + * In case of SET_WINDOW_REINIT the context could not be transfered + * and the caller must initialize it correctly. + * \ingroup glcontext + */ +static int setGlWindow_x11(MPGLContext *ctx) +{ + XVisualInfo **vinfo = &ctx->vinfo.x11; + GLXContext *context = &ctx->context.x11; + Display *display = ctx->vo->x11->display; + Window win = ctx->vo->x11->window; + XVisualInfo *new_vinfo; + GLXContext new_context = NULL; + int keep_context = 0; + GL *gl = ctx->gl; + + // should only be needed when keeping context, but not doing glFinish + // can cause flickering even when we do not keep it. + if (*context) + gl->Finish(); + new_vinfo = getWindowVisualInfo(ctx, win); + if (*context && *vinfo && new_vinfo && + (*vinfo)->visualid == new_vinfo->visualid) { + // we can keep the GLXContext + new_context = *context; + XFree(new_vinfo); + new_vinfo = *vinfo; + keep_context = 1; + } else { + // create a context + new_context = glXCreateContext(display, new_vinfo, NULL, True); + if (!new_context) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GLX context!\n"); + XFree(new_vinfo); + return SET_WINDOW_FAILED; + } + } + + // set context + if (!glXMakeCurrent(display, ctx->vo->x11->window, new_context)) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GLX context!\n"); + if (!keep_context) { + glXDestroyContext(display, new_context); + XFree(new_vinfo); + } + return SET_WINDOW_FAILED; + } + + // set new values + ctx->vo->x11->window = win; + vo_x11_update_geometry(ctx->vo, 1); + if (!keep_context) { + void *(*getProcAddress)(const GLubyte *); + const char *(*glXExtStr)(Display *, int); + char *glxstr = strdup(""); + if (*context) + glXDestroyContext(display, *context); + *context = new_context; + if (*vinfo) + XFree(*vinfo); + *vinfo = new_vinfo; + getProcAddress = getdladdr("glXGetProcAddress"); + if (!getProcAddress) + getProcAddress = getdladdr("glXGetProcAddressARB"); + glXExtStr = getdladdr("glXQueryExtensionsString"); + if (glXExtStr) + appendstr(&glxstr, glXExtStr(display, DefaultScreen(display))); + glXExtStr = getdladdr("glXGetClientString"); + if (glXExtStr) + appendstr(&glxstr, glXExtStr(display, GLX_EXTENSIONS)); + glXExtStr = getdladdr("glXGetServerString"); + if (glXExtStr) + appendstr(&glxstr, glXExtStr(display, GLX_EXTENSIONS)); + + getFunctions(gl, getProcAddress, glxstr); + if (!gl->GenPrograms && gl->GetString && + getProcAddress && + strstr(gl->GetString(GL_EXTENSIONS), "GL_ARB_vertex_program")) { + mp_msg(MSGT_VO, MSGL_WARN, + "Broken glXGetProcAddress detected, trying workaround\n"); + getFunctions(gl, NULL, glxstr); + } + free(glxstr); + + // and inform that reinit is neccessary + return SET_WINDOW_REINIT; + } + return SET_WINDOW_OK; +} + +/** + * \brief free the VisualInfo and GLXContext of an OpenGL context. + * \ingroup glcontext + */ +static void releaseGlContext_x11(MPGLContext *ctx) +{ + XVisualInfo **vinfo = &ctx->vinfo.x11; + GLXContext *context = &ctx->context.x11; + Display *display = ctx->vo->x11->display; + GL *gl = ctx->gl; + if (*vinfo) + XFree(*vinfo); + *vinfo = NULL; + if (*context) { + gl->Finish(); + glXMakeCurrent(display, None, NULL); + glXDestroyContext(display, *context); + } + *context = 0; +} + +static void swapGlBuffers_x11(MPGLContext *ctx) +{ + glXSwapBuffers(ctx->vo->x11->display, ctx->vo->x11->window); +} +#endif + +#ifdef CONFIG_GL_SDL +#include "sdl_common.h" + +static void swapGlBuffers_sdl(MPGLContext *ctx) +{ + SDL_GL_SwapBuffers(); +} + +static void *sdlgpa(const GLubyte *name) +{ + return SDL_GL_GetProcAddress(name); +} + +static int setGlWindow_sdl(MPGLContext *ctx) +{ + if (sdl_set_mode(0, SDL_OPENGL | SDL_RESIZABLE) < 0) + return SET_WINDOW_FAILED; + SDL_GL_LoadLibrary(NULL); + getFunctions(ctx->gl, sdlgpa, NULL); + return SET_WINDOW_OK; +} + +static int sdl_check_events(struct vo *vo) +{ + int res = 0; + SDL_Event event; + while (SDL_PollEvent(&event)) + res |= sdl_default_handle_event(&event); + // poll "events" from within MPlayer code + res |= sdl_default_handle_event(NULL); + if (res & VO_EVENT_RESIZE) + sdl_set_mode(0, SDL_OPENGL | SDL_RESIZABLE); + return res; +} + +static void new_vo_sdl_fullscreen(struct vo *vo) { vo_sdl_fullscreen(); } + +#endif + +static int setGlWindow_dummy(MPGLContext *ctx) +{ + getFunctions(ctx->gl, NULL, NULL); + return SET_WINDOW_OK; +} + +static void releaseGlContext_dummy(MPGLContext *ctx) +{ +} + +static int dummy_check_events(struct vo *vo) +{ + return 0; +} + +static void dummy_update_xinerama_info(struct vo *vo) +{ + struct MPOpts *opts = vo->opts; + if (opts->vo_screenwidth <= 0 || opts->vo_screenheight <= 0) { + mp_msg(MSGT_VO, MSGL_ERR, "You must specify the screen dimensions " + "with -screenw and -screenh\n"); + opts->vo_screenwidth = 1280; + opts->vo_screenheight = 768; + } + aspect_save_screenres(vo, opts->vo_screenwidth, opts->vo_screenheight); +} + +MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) +{ + MPGLContext *ctx; + if (type == GLTYPE_AUTO) { + ctx = init_mpglcontext(GLTYPE_W32, vo); + if (ctx) + return ctx; + ctx = init_mpglcontext(GLTYPE_X11, vo); + if (ctx) + return ctx; + return init_mpglcontext(GLTYPE_SDL, vo); + } + ctx = talloc_zero(NULL, MPGLContext); + ctx->gl = talloc_zero(ctx, GL); + ctx->setGlWindow = setGlWindow_dummy; + ctx->releaseGlContext = releaseGlContext_dummy; + ctx->update_xinerama_info = dummy_update_xinerama_info; + ctx->check_events = dummy_check_events; + ctx->type = type; + ctx->vo = vo; + switch (ctx->type) { +#ifdef CONFIG_GL_WIN32 + case GLTYPE_W32: + ctx->setGlWindow = setGlWindow_w32; + ctx->releaseGlContext = releaseGlContext_w32; + ctx->swapGlBuffers = swapGlBuffers_w32; + ctx->update_xinerama_info = new_w32_update_xinerama_info; + ctx->border = new_vo_w32_border; + ctx->check_events = new_vo_w32_check_events; + ctx->fullscreen = new_vo_w32_fullscreen; + ctx->ontop = new_vo_w32_ontop; + //the win32 code is hardcoded to use the deprecated vo API + global_vo = vo; + if (vo_w32_init()) + return ctx; + break; +#endif +#ifdef CONFIG_GL_X11 + case GLTYPE_X11: + ctx->setGlWindow = setGlWindow_x11; + ctx->releaseGlContext = releaseGlContext_x11; + ctx->swapGlBuffers = swapGlBuffers_x11; + ctx->update_xinerama_info = update_xinerama_info; + ctx->border = vo_x11_border; + ctx->check_events = vo_x11_check_events; + ctx->fullscreen = vo_x11_fullscreen; + ctx->ontop = vo_x11_ontop; + if (vo_init(vo)) + return ctx; + break; +#endif +#ifdef CONFIG_GL_SDL + case GLTYPE_SDL: + SDL_Init(SDL_INIT_VIDEO); + ctx->setGlWindow = setGlWindow_sdl; + ctx->swapGlBuffers = swapGlBuffers_sdl; + ctx->check_events = sdl_check_events; + ctx->fullscreen = new_vo_sdl_fullscreen; + //the SDL code is hardcoded to use the deprecated vo API + global_vo = vo; + if (vo_sdl_init()) + return ctx; + break; +#endif + } + talloc_free(ctx); + return NULL; +} + +void uninit_mpglcontext(MPGLContext *ctx) +{ + if (!ctx) + return; + ctx->releaseGlContext(ctx); + switch (ctx->type) { +#ifdef CONFIG_GL_WIN32 + case GLTYPE_W32: + vo_w32_uninit(); + break; +#endif +#ifdef CONFIG_GL_X11 + case GLTYPE_X11: + vo_x11_uninit(ctx->vo); + break; +#endif +#ifdef CONFIG_GL_SDL + case GLTYPE_SDL: + vo_sdl_uninit(); + break; +#endif + } + talloc_free(ctx); +} diff --git a/libvo/gl_common.h b/libvo/gl_common.h index fca91d6f7d..d7fb80252f 100644 --- a/libvo/gl_common.h +++ b/libvo/gl_common.h @@ -289,27 +289,31 @@ #endif /** \} */ // end of glextdefines group -void glAdjustAlignment(int stride); +struct GL; +typedef struct GL GL; + +void glAdjustAlignment(GL *gl, int stride); const char *glValName(GLint value); int glFindFormat(uint32_t format, int *bpp, GLint *gl_texfmt, - GLenum *gl_format, GLenum *gl_type); + GLenum *gl_format, GLenum *gl_type); int glFmt2bpp(GLenum format, GLenum type); -void glCreateClearTex(GLenum target, GLenum fmt, GLenum format, GLenum type, GLint filter, - int w, int h, unsigned char val); -int glCreatePPMTex(GLenum target, GLenum fmt, GLint filter, +void glCreateClearTex(GL *gl, GLenum target, GLenum fmt, GLenum format, + GLenum type, GLint filter, int w, int h, + unsigned char val); +int glCreatePPMTex(GL *gl, GLenum target, GLenum fmt, GLint filter, FILE *f, int *width, int *height, int *maxval); -void glUploadTex(GLenum target, GLenum format, GLenum type, +void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type, const void *dataptr, int stride, int x, int y, int w, int h, int slice); -void glDrawTex(GLfloat x, GLfloat y, GLfloat w, GLfloat h, +void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h, GLfloat tx, GLfloat ty, GLfloat tw, GLfloat th, int sx, int sy, int rect_tex, int is_yv12, int flip); -int loadGPUProgram(GLenum target, char *prog); +int loadGPUProgram(GL *gl, GLenum target, char *prog); /** \addtogroup glconversion - * \{ */ + * \{ */ //! do not use YUV conversion, this should always stay 0 #define YUV_CONVERSION_NONE 0 //! use nVidia specific register combiners for YUV conversion @@ -356,45 +360,44 @@ int loadGPUProgram(GLenum target, char *prog); //! returns whether the yuv conversion supports large brightness range etc. static inline int glYUVLargeRange(int conv) { - switch (conv) - { - case YUV_CONVERSION_NONE: - case YUV_CONVERSION_COMBINERS: - case YUV_CONVERSION_COMBINERS_ATI: - case YUV_CONVERSION_FRAGMENT_LOOKUP3D: - case YUV_CONVERSION_TEXT_FRAGMENT: - return 0; - } - return 1; + switch (conv) { + case YUV_CONVERSION_NONE: + case YUV_CONVERSION_COMBINERS: + case YUV_CONVERSION_COMBINERS_ATI: + case YUV_CONVERSION_FRAGMENT_LOOKUP3D: + case YUV_CONVERSION_TEXT_FRAGMENT: + return 0; + } + return 1; } /** \} */ typedef struct { - GLenum target; - int type; - struct mp_csp_params csp_params; - int texw; - int texh; - int chrom_texw; - int chrom_texh; - float filter_strength; + GLenum target; + int type; + struct mp_csp_params csp_params; + int texw; + int texh; + int chrom_texw; + int chrom_texh; + float filter_strength; } gl_conversion_params_t; -int glAutodetectYUVConversion(void); -void glSetupYUVConversion(gl_conversion_params_t *params); -void glEnableYUVConversion(GLenum target, int type); -void glDisableYUVConversion(GLenum target, int type); +int glAutodetectYUVConversion(GL *gl); +void glSetupYUVConversion(GL *gl, gl_conversion_params_t *params); +void glEnableYUVConversion(GL *gl, GLenum target, int type); +void glDisableYUVConversion(GL *gl, GLenum target, int type); #define GL_3D_RED_CYAN 1 #define GL_3D_GREEN_MAGENTA 2 #define GL_3D_QUADBUFFER 3 -void glEnable3DLeft(int type); -void glEnable3DRight(int type); -void glDisable3D(int type); +void glEnable3DLeft(GL *gl, int type); +void glEnable3DRight(GL *gl, int type); +void glDisable3D(GL *gl, int type); /** \addtogroup glcontext - * \{ */ + * \{ */ //! could not set new window, will continue drawing into the old one. #define SET_WINDOW_FAILED -1 //! new window is set, could even transfer the OpenGL context. @@ -404,129 +407,146 @@ void glDisable3D(int type); /** \} */ enum MPGLType { - GLTYPE_AUTO, - GLTYPE_W32, - GLTYPE_X11, - GLTYPE_SDL, + GLTYPE_AUTO, + GLTYPE_W32, + GLTYPE_X11, + GLTYPE_SDL, }; typedef struct MPGLContext { - enum MPGLType type; - union { - int w32; + GL *gl; + enum MPGLType type; + struct vo *vo; + union { + int w32; #ifdef CONFIG_GL_X11 - XVisualInfo *x11; + XVisualInfo *x11; #endif - } vinfo; - union { + } vinfo; + union { #ifdef CONFIG_GL_WIN32 - HGLRC w32; + HGLRC w32; #endif #ifdef CONFIG_GL_X11 - GLXContext x11; -#endif - } context; - int (*setGlWindow)(struct MPGLContext *); - void (*releaseGlContext)(struct MPGLContext *); - void (*swapGlBuffers)(struct MPGLContext *); - void (*update_xinerama_info)(void); - void (*border)(void); - int (*check_events)(void); - void (*fullscreen)(void); - void (*ontop)(void); + GLXContext x11; +#endif + } context; + int (*setGlWindow)(struct MPGLContext *); + void (*releaseGlContext)(struct MPGLContext *); + void (*swapGlBuffers)(struct MPGLContext *); + void (*update_xinerama_info)(struct vo *vo); + void (*border)(struct vo *vo); + int (*check_events)(struct vo *vo); + void (*fullscreen)(struct vo *vo); + void (*ontop)(struct vo *vo); } MPGLContext; -int init_mpglcontext(MPGLContext *ctx, enum MPGLType type); +MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo); void uninit_mpglcontext(MPGLContext *ctx); -extern void (GLAPIENTRY *mpglBegin)(GLenum); -extern void (GLAPIENTRY *mpglEnd)(void); -extern void (GLAPIENTRY *mpglViewport)(GLint, GLint, GLsizei, GLsizei); -extern void (GLAPIENTRY *mpglMatrixMode)(GLenum); -extern void (GLAPIENTRY *mpglLoadIdentity)(void); -extern void (GLAPIENTRY *mpglTranslated)(double, double, double); -extern void (GLAPIENTRY *mpglScaled)(double, double, double); -extern void (GLAPIENTRY *mpglOrtho)(double, double, double, double, double, double); -extern void (GLAPIENTRY *mpglFrustum)(double, double, double, double, double, double); -extern void (GLAPIENTRY *mpglPushMatrix)(void); -extern void (GLAPIENTRY *mpglPopMatrix)(void); -extern void (GLAPIENTRY *mpglClear)(GLbitfield); -extern GLuint (GLAPIENTRY *mpglGenLists)(GLsizei); -extern void (GLAPIENTRY *mpglDeleteLists)(GLuint, GLsizei); -extern void (GLAPIENTRY *mpglNewList)(GLuint, GLenum); -extern void (GLAPIENTRY *mpglEndList)(void); -extern void (GLAPIENTRY *mpglCallList)(GLuint); -extern void (GLAPIENTRY *mpglCallLists)(GLsizei, GLenum, const GLvoid *); -extern void (GLAPIENTRY *mpglGenTextures)(GLsizei, GLuint *); -extern void (GLAPIENTRY *mpglDeleteTextures)(GLsizei, const GLuint *); -extern void (GLAPIENTRY *mpglTexEnvf)(GLenum, GLenum, GLfloat); -extern void (GLAPIENTRY *mpglTexEnvi)(GLenum, GLenum, GLint); -extern void (GLAPIENTRY *mpglColor4ub)(GLubyte, GLubyte, GLubyte, GLubyte); -extern void (GLAPIENTRY *mpglColor3f)(GLfloat, GLfloat, GLfloat); -extern void (GLAPIENTRY *mpglColor4f)(GLfloat, GLfloat, GLfloat, GLfloat); -extern void (GLAPIENTRY *mpglClearColor)(GLclampf, GLclampf, GLclampf, GLclampf); -extern void (GLAPIENTRY *mpglClearDepth)(GLclampd); -extern void (GLAPIENTRY *mpglDepthFunc)(GLenum); -extern void (GLAPIENTRY *mpglEnable)(GLenum); -extern void (GLAPIENTRY *mpglDisable)(GLenum); -extern const GLubyte *(GLAPIENTRY *mpglGetString)(GLenum); -extern void (GLAPIENTRY *mpglDrawBuffer)(GLenum); -extern void (GLAPIENTRY *mpglDepthMask)(GLboolean); -extern void (GLAPIENTRY *mpglBlendFunc)(GLenum, GLenum); -extern void (GLAPIENTRY *mpglFlush)(void); -extern void (GLAPIENTRY *mpglFinish)(void); -extern void (GLAPIENTRY *mpglPixelStorei)(GLenum, GLint); -extern void (GLAPIENTRY *mpglTexImage1D)(GLenum, GLint, GLint, GLsizei, GLint, GLenum, GLenum, const GLvoid *); -extern void (GLAPIENTRY *mpglTexImage2D)(GLenum, GLint, GLint, GLsizei, GLsizei, GLint, GLenum, GLenum, const GLvoid *); -extern void (GLAPIENTRY *mpglTexSubImage2D)(GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const GLvoid *); -extern void (GLAPIENTRY *mpglTexParameteri)(GLenum, GLenum, GLint); -extern void (GLAPIENTRY *mpglTexParameterf)(GLenum, GLenum, GLfloat); -extern void (GLAPIENTRY *mpglTexParameterfv)(GLenum, GLenum, const GLfloat *); -extern void (GLAPIENTRY *mpglTexCoord2f)(GLfloat, GLfloat); -extern void (GLAPIENTRY *mpglVertex2f)(GLfloat, GLfloat); -extern void (GLAPIENTRY *mpglVertex3f)(GLfloat, GLfloat, GLfloat); -extern void (GLAPIENTRY *mpglNormal3f)(GLfloat, GLfloat, GLfloat); -extern void (GLAPIENTRY *mpglLightfv)(GLenum, GLenum, const GLfloat *); -extern void (GLAPIENTRY *mpglColorMaterial)(GLenum, GLenum); -extern void (GLAPIENTRY *mpglShadeModel)(GLenum); -extern void (GLAPIENTRY *mpglGetIntegerv)(GLenum, GLint *); -extern void (GLAPIENTRY *mpglColorMask)(GLboolean, GLboolean, GLboolean, GLboolean); +//function pointers loaded from the OpenGL library +struct GL { + void (GLAPIENTRY *Begin)(GLenum); + void (GLAPIENTRY *End)(void); + void (GLAPIENTRY *Viewport)(GLint, GLint, GLsizei, GLsizei); + void (GLAPIENTRY *MatrixMode)(GLenum); + void (GLAPIENTRY *LoadIdentity)(void); + void (GLAPIENTRY *Translated)(double, double, double); + void (GLAPIENTRY *Scaled)(double, double, double); + void (GLAPIENTRY *Ortho)(double, double, double, double, double,double); + void (GLAPIENTRY *Frustum)(double, double, double, double, double, double); + void (GLAPIENTRY *PushMatrix)(void); + void (GLAPIENTRY *PopMatrix)(void); + void (GLAPIENTRY *Clear)(GLbitfield); + GLuint (GLAPIENTRY *GenLists)(GLsizei); + void (GLAPIENTRY *DeleteLists)(GLuint, GLsizei); + void (GLAPIENTRY *NewList)(GLuint, GLenum); + void (GLAPIENTRY *EndList)(void); + void (GLAPIENTRY *CallList)(GLuint); + void (GLAPIENTRY *CallLists)(GLsizei, GLenum, const GLvoid *); + void (GLAPIENTRY *GenTextures)(GLsizei, GLuint *); + void (GLAPIENTRY *DeleteTextures)(GLsizei, const GLuint *); + void (GLAPIENTRY *TexEnvf)(GLenum, GLenum, GLfloat); + void (GLAPIENTRY *TexEnvi)(GLenum, GLenum, GLint); + void (GLAPIENTRY *Color4ub)(GLubyte, GLubyte, GLubyte, GLubyte); + void (GLAPIENTRY *Color3f)(GLfloat, GLfloat, GLfloat); + void (GLAPIENTRY *Color4f)(GLfloat, GLfloat, GLfloat, GLfloat); + void (GLAPIENTRY *ClearColor)(GLclampf, GLclampf, GLclampf, GLclampf); + void (GLAPIENTRY *ClearDepth)(GLclampd); + void (GLAPIENTRY *DepthFunc)(GLenum); + void (GLAPIENTRY *Enable)(GLenum); + void (GLAPIENTRY *Disable)(GLenum); + const GLubyte *(GLAPIENTRY * GetString)(GLenum); + void (GLAPIENTRY *DrawBuffer)(GLenum); + void (GLAPIENTRY *DepthMask)(GLboolean); + void (GLAPIENTRY *BlendFunc)(GLenum, GLenum); + void (GLAPIENTRY *Flush)(void); + void (GLAPIENTRY *Finish)(void); + void (GLAPIENTRY *PixelStorei)(GLenum, GLint); + void (GLAPIENTRY *TexImage1D)(GLenum, GLint, GLint, GLsizei, GLint, + GLenum, GLenum, const GLvoid *); + void (GLAPIENTRY *TexImage2D)(GLenum, GLint, GLint, GLsizei, GLsizei, + GLint, GLenum, GLenum, const GLvoid *); + void (GLAPIENTRY *TexSubImage2D)(GLenum, GLint, GLint, GLint, + GLsizei, GLsizei, GLenum, GLenum, + const GLvoid *); + void (GLAPIENTRY *GetTexImage)(GLenum, GLint, GLenum, GLenum, GLvoid *); + void (GLAPIENTRY *TexParameteri)(GLenum, GLenum, GLint); + void (GLAPIENTRY *TexParameterf)(GLenum, GLenum, GLfloat); + void (GLAPIENTRY *TexParameterfv)(GLenum, GLenum, const GLfloat *); + void (GLAPIENTRY *TexCoord2f)(GLfloat, GLfloat); + void (GLAPIENTRY *Vertex2f)(GLfloat, GLfloat); + void (GLAPIENTRY *Vertex3f)(GLfloat, GLfloat, GLfloat); + void (GLAPIENTRY *Normal3f)(GLfloat, GLfloat, GLfloat); + void (GLAPIENTRY *Lightfv)(GLenum, GLenum, const GLfloat *); + void (GLAPIENTRY *ColorMaterial)(GLenum, GLenum); + void (GLAPIENTRY *ShadeModel)(GLenum); + void (GLAPIENTRY *GetIntegerv)(GLenum, GLint *); + void (GLAPIENTRY *ColorMask)(GLboolean, GLboolean, GLboolean, GLboolean); + void (GLAPIENTRY *ReadPixels)(GLint, GLint, GLsizei, GLsizei, GLenum, + GLenum, GLvoid *); + void (GLAPIENTRY *ReadBuffer)(GLenum); -extern void (GLAPIENTRY *mpglGenBuffers)(GLsizei, GLuint *); -extern void (GLAPIENTRY *mpglDeleteBuffers)(GLsizei, const GLuint *); -extern void (GLAPIENTRY *mpglBindBuffer)(GLenum, GLuint); -extern GLvoid* (GLAPIENTRY *mpglMapBuffer)(GLenum, GLenum); -extern GLboolean (GLAPIENTRY *mpglUnmapBuffer)(GLenum); -extern void (GLAPIENTRY *mpglBufferData)(GLenum, intptr_t, const GLvoid *, GLenum); -extern void (GLAPIENTRY *mpglCombinerParameterfv)(GLenum, const GLfloat *); -extern void (GLAPIENTRY *mpglCombinerParameteri)(GLenum, GLint); -extern void (GLAPIENTRY *mpglCombinerInput)(GLenum, GLenum, GLenum, GLenum, GLenum, - GLenum); -extern void (GLAPIENTRY *mpglCombinerOutput)(GLenum, GLenum, GLenum, GLenum, GLenum, - GLenum, GLenum, GLboolean, GLboolean, - GLboolean); -extern void (GLAPIENTRY *mpglBeginFragmentShader)(void); -extern void (GLAPIENTRY *mpglEndFragmentShader)(void); -extern void (GLAPIENTRY *mpglSampleMap)(GLuint, GLuint, GLenum); -extern void (GLAPIENTRY *mpglColorFragmentOp2)(GLenum, GLuint, GLuint, GLuint, GLuint, - GLuint, GLuint, GLuint, GLuint, GLuint); -extern void (GLAPIENTRY *mpglColorFragmentOp3)(GLenum, GLuint, GLuint, GLuint, GLuint, - GLuint, GLuint, GLuint, GLuint, GLuint, - GLuint, GLuint, GLuint); -extern void (GLAPIENTRY *mpglSetFragmentShaderConstant)(GLuint, const GLfloat *); -extern void (GLAPIENTRY *mpglActiveTexture)(GLenum); -extern void (GLAPIENTRY *mpglBindTexture)(GLenum, GLuint); -extern void (GLAPIENTRY *mpglMultiTexCoord2f)(GLenum, GLfloat, GLfloat); -extern void (GLAPIENTRY *mpglGenPrograms)(GLsizei, GLuint *); -extern void (GLAPIENTRY *mpglDeletePrograms)(GLsizei, const GLuint *); -extern void (GLAPIENTRY *mpglBindProgram)(GLenum, GLuint); -extern void (GLAPIENTRY *mpglProgramString)(GLenum, GLenum, GLsizei, const GLvoid *); -extern void (GLAPIENTRY *mpglProgramEnvParameter4f)(GLenum, GLuint, GLfloat, GLfloat, - GLfloat, GLfloat); -extern int (GLAPIENTRY *mpglSwapInterval)(int); -extern void (GLAPIENTRY *mpglTexImage3D)(GLenum, GLint, GLenum, GLsizei, GLsizei, - GLsizei, GLint, GLenum, GLenum, const GLvoid *); -extern void* (GLAPIENTRY *mpglAllocateMemoryMESA)(void *, int, size_t, float, float, float); -extern void (GLAPIENTRY *mpglFreeMemoryMESA)(void *, int, void *); + // OpenGL extension functions + void (GLAPIENTRY *GenBuffers)(GLsizei, GLuint *); + void (GLAPIENTRY *DeleteBuffers)(GLsizei, const GLuint *); + void (GLAPIENTRY *BindBuffer)(GLenum, GLuint); + GLvoid * (GLAPIENTRY * MapBuffer)(GLenum, GLenum); + GLboolean (GLAPIENTRY *UnmapBuffer)(GLenum); + void (GLAPIENTRY *BufferData)(GLenum, intptr_t, const GLvoid *, GLenum); + void (GLAPIENTRY *CombinerParameterfv)(GLenum, const GLfloat *); + void (GLAPIENTRY *CombinerParameteri)(GLenum, GLint); + void (GLAPIENTRY *CombinerInput)(GLenum, GLenum, GLenum, GLenum, GLenum, + GLenum); + void (GLAPIENTRY *CombinerOutput)(GLenum, GLenum, GLenum, GLenum, GLenum, + GLenum, GLenum, GLboolean, GLboolean, + GLboolean); + void (GLAPIENTRY *BeginFragmentShader)(void); + void (GLAPIENTRY *EndFragmentShader)(void); + void (GLAPIENTRY *SampleMap)(GLuint, GLuint, GLenum); + void (GLAPIENTRY *ColorFragmentOp2)(GLenum, GLuint, GLuint, GLuint, GLuint, + GLuint, GLuint, GLuint, GLuint, GLuint); + void (GLAPIENTRY *ColorFragmentOp3)(GLenum, GLuint, GLuint, GLuint, GLuint, + GLuint, GLuint, GLuint, GLuint, GLuint, + GLuint, GLuint, GLuint); + void (GLAPIENTRY *SetFragmentShaderConstant)(GLuint, const GLfloat *); + void (GLAPIENTRY *ActiveTexture)(GLenum); + void (GLAPIENTRY *BindTexture)(GLenum, GLuint); + void (GLAPIENTRY *MultiTexCoord2f)(GLenum, GLfloat, GLfloat); + void (GLAPIENTRY *GenPrograms)(GLsizei, GLuint *); + void (GLAPIENTRY *DeletePrograms)(GLsizei, const GLuint *); + void (GLAPIENTRY *BindProgram)(GLenum, GLuint); + void (GLAPIENTRY *ProgramString)(GLenum, GLenum, GLsizei, const GLvoid *); + void (GLAPIENTRY *GetProgramiv)(GLenum, GLenum, GLint *); + void (GLAPIENTRY *ProgramEnvParameter4f)(GLenum, GLuint, GLfloat, GLfloat, + GLfloat, GLfloat); + int (GLAPIENTRY *SwapInterval)(int); + void (GLAPIENTRY *TexImage3D)(GLenum, GLint, GLenum, GLsizei, GLsizei, + GLsizei, GLint, GLenum, GLenum, + const GLvoid *); + void * (GLAPIENTRY * AllocateMemoryMESA)(void *, int, size_t, float, float, + float); + void (GLAPIENTRY *FreeMemoryMESA)(void *, int, void *); +}; #endif /* MPLAYER_GL_COMMON_H */ diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c dissimilarity index 89% index cb1ccc593b..7a7c718c88 100644 --- a/libvo/vo_gl.c +++ b/libvo/vo_gl.c @@ -1,1397 +1,1584 @@ -/* - * 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 -#include -#include -#include -#include - -#include "config.h" -#include "mp_msg.h" -#include "subopt-helper.h" -#include "video_out.h" -#include "video_out_internal.h" -#include "sub/font_load.h" -#include "sub/sub.h" - -#include "gl_common.h" -#include "aspect.h" -#include "fastmemcpy.h" -#include "sub/ass_mp.h" - -#ifdef CONFIG_GL_SDL -#ifdef CONFIG_SDL_SDL_H -#include -#else -#include -#endif -#endif - -static const vo_info_t info = -{ - "OpenGL", - "gl", - "Reimar Doeffinger ", - "" -}; - -const LIBVO_EXTERN(gl) - - -static const vo_info_t info_nosw = -{ - "OpenGL no software rendering", - "gl_nosw", - "Reimar Doeffinger ", - "" -}; -static int preinit_nosw(const char *arg); -const struct vo_driver video_out_gl_nosw = -{ - .is_new = 0, - .info = &info_nosw, - .preinit = old_vo_preinit, - .config = old_vo_config, - .control = old_vo_control, - .draw_slice = old_vo_draw_slice, - .draw_osd = old_vo_draw_osd, - .flip_page = old_vo_flip_page, - .check_events = old_vo_check_events, - .uninit = old_vo_uninit, - .old_functions = &(struct vo_old_functions){ - preinit_nosw, - config, - control, - draw_frame, - draw_slice, - draw_osd, - flip_page, - check_events, - uninit, - } -}; - -static MPGLContext glctx; - -static int use_osd; -static int scaled_osd; -//! How many parts the OSD may consist of at most -#define MAX_OSD_PARTS 20 -//! Textures for OSD -static GLuint osdtex[MAX_OSD_PARTS]; -#ifndef FAST_OSD -//! Alpha textures for OSD -static GLuint osdatex[MAX_OSD_PARTS]; -#endif -static GLuint *eosdtex; -#define LARGE_EOSD_TEX_SIZE 512 -#define TINYTEX_SIZE 16 -#define TINYTEX_COLS (LARGE_EOSD_TEX_SIZE/TINYTEX_SIZE) -#define TINYTEX_MAX (TINYTEX_COLS*TINYTEX_COLS) -#define SMALLTEX_SIZE 32 -#define SMALLTEX_COLS (LARGE_EOSD_TEX_SIZE/SMALLTEX_SIZE) -#define SMALLTEX_MAX (SMALLTEX_COLS*SMALLTEX_COLS) -static GLuint largeeosdtex[2]; -//! Display lists that draw the OSD parts -static GLuint osdDispList[MAX_OSD_PARTS]; -#ifndef FAST_OSD -static GLuint osdaDispList[MAX_OSD_PARTS]; -#endif -static GLuint eosdDispList; -//! How many parts the OSD currently consists of -static int osdtexCnt; -static int eosdtexCnt; -static int osd_color; - -static int use_aspect; -static int use_ycbcr; -#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)) -static int use_yuv; -static struct mp_csp_details colorspace = MP_CSP_DETAILS_DEFAULTS; -static int user_colorspace; //essentially unused; legacy warning -static int levelconv; //essentially unused; legacy warning -static int is_yuv; -static int lscale; -static int cscale; -static float filter_strength; -static int yuvconvtype; -static int use_rectangle; -static int err_shown; -static uint32_t image_width; -static uint32_t image_height; -static uint32_t image_format; -static int many_fmts; -static int ati_hack; -static int force_pbo; -static int mesa_buffer; -static int use_glFinish; -static int swap_interval; -static GLenum gl_target; -static GLint gl_texfmt; -static GLenum gl_format; -static GLenum gl_type; -static GLuint gl_buffer; -static GLuint gl_buffer_uv[2]; -static int gl_buffersize; -static int gl_buffersize_uv; -static void *gl_bufferptr; -static void *gl_bufferptr_uv[2]; -static int mesa_buffersize; -static void *mesa_bufferptr; -static GLuint fragprog; -static GLuint default_texs[22]; -static char *custom_prog; -static char *custom_tex; -static int custom_tlin; -static int custom_trect; -static int mipmap_gen; -static int stereo_mode; - -static int int_pause; - -static struct mp_csp_equalizer video_eq; - -static int texture_width; -static int texture_height; -static int mpi_flipped; -static int vo_flipped; -static int ass_border_x, ass_border_y; - -static unsigned int slice_height = 1; - -static void redraw(void); - -static void resize(int x,int y){ - mp_msg(MSGT_VO, MSGL_V, "[gl] Resize: %dx%d\n",x,y); - if (WinID >= 0) { - int left = 0, top = 0, w = x, h = y; - geometry(&left, &top, &w, &h, vo_dwidth, vo_dheight); - top = y - h - top; - mpglViewport(left, top, w, h); - } else - mpglViewport( 0, 0, x, y ); - - mpglMatrixMode(GL_PROJECTION); - mpglLoadIdentity(); - ass_border_x = ass_border_y = 0; - if (aspect_scaling() && use_aspect) { - int new_w, new_h; - GLdouble scale_x, scale_y; - aspect(&new_w, &new_h, A_WINZOOM); - panscan_calc_windowed(); - new_w += vo_panscan_x; - new_h += vo_panscan_y; - scale_x = (GLdouble)new_w / (GLdouble)x; - scale_y = (GLdouble)new_h / (GLdouble)y; - mpglScaled(scale_x, scale_y, 1); - ass_border_x = (vo_dwidth - new_w) / 2; - ass_border_y = (vo_dheight - new_h) / 2; - } - mpglOrtho(0, image_width, image_height, 0, -1,1); - - mpglMatrixMode(GL_MODELVIEW); - mpglLoadIdentity(); - - if (!scaled_osd) { -#ifdef CONFIG_FREETYPE - // adjust font size to display size - force_load_font = 1; -#endif - vo_osd_changed(OSDTYPE_OSD); - } - mpglClear(GL_COLOR_BUFFER_BIT); - redraw(); -} - -static void texSize(int w, int h, int *texw, int *texh) { - if (use_rectangle) { - *texw = w; *texh = h; - } else { - *texw = 32; - while (*texw < w) - *texw *= 2; - *texh = 32; - while (*texh < h) - *texh *= 2; - } - if (mesa_buffer) *texw = (*texw + 63) & ~63; - else if (ati_hack) *texw = (*texw + 511) & ~511; -} - -//! maximum size of custom fragment program -#define MAX_CUSTOM_PROG_SIZE (1024 * 1024) -static void update_yuvconv(void) { - int xs, ys, depth; - struct mp_csp_params cparams = { .colorspace = colorspace }; - mp_csp_copy_equalizer_values(&cparams, &video_eq); - gl_conversion_params_t params = {gl_target, yuvconvtype, cparams, - texture_width, texture_height, 0, 0, filter_strength}; - mp_get_chroma_shift(image_format, &xs, &ys, &depth); - params.chrom_texw = params.texw >> xs; - params.chrom_texh = params.texh >> ys; - params.csp_params.input_shift = -depth & 7; - glSetupYUVConversion(¶ms); - if (custom_prog) { - FILE *f = fopen(custom_prog, "rb"); - if (!f) { - mp_msg(MSGT_VO, MSGL_WARN, - "[gl] Could not read customprog %s\n", custom_prog); - } else { - char *prog = calloc(1, MAX_CUSTOM_PROG_SIZE + 1); - fread(prog, 1, MAX_CUSTOM_PROG_SIZE, f); - fclose(f); - loadGPUProgram(GL_FRAGMENT_PROGRAM, prog); - free(prog); - } - mpglProgramEnvParameter4f(GL_FRAGMENT_PROGRAM, 0, - 1.0 / texture_width, 1.0 / texture_height, - texture_width, texture_height); - } - if (custom_tex) { - FILE *f = fopen(custom_tex, "rb"); - if (!f) { - mp_msg(MSGT_VO, MSGL_WARN, - "[gl] Could not read customtex %s\n", custom_tex); - } else { - int width, height, maxval; - mpglActiveTexture(GL_TEXTURE3); - if (glCreatePPMTex(custom_trect?GL_TEXTURE_RECTANGLE:GL_TEXTURE_2D, 0, - custom_tlin?GL_LINEAR:GL_NEAREST, - f, &width, &height, &maxval)) { - mpglProgramEnvParameter4f(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", custom_tex); - fclose(f); - mpglActiveTexture(GL_TEXTURE0); - } - } -} - -/** - * \brief remove all OSD textures and display-lists, thus clearing it. - */ -static void clearOSD(void) { - int i; - if (!osdtexCnt) - return; - mpglDeleteTextures(osdtexCnt, osdtex); -#ifndef FAST_OSD - mpglDeleteTextures(osdtexCnt, osdatex); - for (i = 0; i < osdtexCnt; i++) - mpglDeleteLists(osdaDispList[i], 1); -#endif - for (i = 0; i < osdtexCnt; i++) - mpglDeleteLists(osdDispList[i], 1); - osdtexCnt = 0; -} - -/** - * \brief remove textures, display list and free memory used by EOSD - */ -static void clearEOSD(void) { - if (eosdDispList) - mpglDeleteLists(eosdDispList, 1); - eosdDispList = 0; - if (eosdtexCnt) - mpglDeleteTextures(eosdtexCnt, eosdtex); - eosdtexCnt = 0; - free(eosdtex); - eosdtex = NULL; -} - -static inline int is_tinytex(ASS_Image *i, int tinytexcur) { - return i->w < TINYTEX_SIZE && i->h < TINYTEX_SIZE && tinytexcur < TINYTEX_MAX; -} - -static inline int is_smalltex(ASS_Image *i, int smalltexcur) { - return i->w < SMALLTEX_SIZE && i->h < SMALLTEX_SIZE && smalltexcur < SMALLTEX_MAX; -} - -static inline void tinytex_pos(int tinytexcur, int *x, int *y) { - *x = (tinytexcur % TINYTEX_COLS) * TINYTEX_SIZE; - *y = (tinytexcur / TINYTEX_COLS) * TINYTEX_SIZE; -} - -static inline void smalltex_pos(int smalltexcur, int *x, int *y) { - *x = (smalltexcur % SMALLTEX_COLS) * SMALLTEX_SIZE; - *y = (smalltexcur / SMALLTEX_COLS) * SMALLTEX_SIZE; -} - -/** - * \brief construct display list from ass image list - * \param img image list to create OSD from. - * A value of NULL has the same effect as clearEOSD() - */ -static void genEOSD(mp_eosd_images_t *imgs) { - int sx, sy; - int tinytexcur = 0; - int smalltexcur = 0; - GLuint *curtex; - GLint scale_type = scaled_osd ? GL_LINEAR : GL_NEAREST; - ASS_Image *img = imgs->imgs; - ASS_Image *i; - - if (imgs->changed == 0) // there are elements, but they are unchanged - return; - if (img && imgs->changed == 1) // there are elements, but they just moved - goto skip_upload; - - clearEOSD(); - if (!img) - return; - if (!largeeosdtex[0]) { - mpglGenTextures(2, largeeosdtex); - mpglBindTexture(gl_target, largeeosdtex[0]); - glCreateClearTex(gl_target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, scale_type, LARGE_EOSD_TEX_SIZE, LARGE_EOSD_TEX_SIZE, 0); - mpglBindTexture(gl_target, largeeosdtex[1]); - glCreateClearTex(gl_target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, scale_type, LARGE_EOSD_TEX_SIZE, LARGE_EOSD_TEX_SIZE, 0); - } - for (i = img; i; i = i->next) - { - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) - continue; - if (is_tinytex(i, tinytexcur)) - tinytexcur++; - else if (is_smalltex(i, smalltexcur)) - smalltexcur++; - else - eosdtexCnt++; - } - mp_msg(MSGT_VO, MSGL_DBG2, "EOSD counts (tiny, small, all): %i, %i, %i\n", - tinytexcur, smalltexcur, eosdtexCnt); - if (eosdtexCnt) { - eosdtex = calloc(eosdtexCnt, sizeof(GLuint)); - mpglGenTextures(eosdtexCnt, eosdtex); - } - tinytexcur = smalltexcur = 0; - for (i = img, curtex = eosdtex; i; i = i->next) { - int x = 0, y = 0; - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) { - mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n"); - continue; - } - if (is_tinytex(i, tinytexcur)) { - tinytex_pos(tinytexcur, &x, &y); - mpglBindTexture(gl_target, largeeosdtex[0]); - tinytexcur++; - } else if (is_smalltex(i, smalltexcur)) { - smalltex_pos(smalltexcur, &x, &y); - mpglBindTexture(gl_target, largeeosdtex[1]); - smalltexcur++; - } else { - texSize(i->w, i->h, &sx, &sy); - mpglBindTexture(gl_target, *curtex++); - glCreateClearTex(gl_target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, scale_type, sx, sy, 0); - } - glUploadTex(gl_target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap, i->stride, - x, y, i->w, i->h, 0); - } - eosdDispList = mpglGenLists(1); -skip_upload: - mpglNewList(eosdDispList, GL_COMPILE); - tinytexcur = smalltexcur = 0; - for (i = img, curtex = eosdtex; i; i = i->next) { - int x = 0, y = 0; - if (i->w <= 0 || i->h <= 0 || i->stride < i->w) - continue; - mpglColor4ub(i->color >> 24, (i->color >> 16) & 0xff, (i->color >> 8) & 0xff, 255 - (i->color & 0xff)); - if (is_tinytex(i, tinytexcur)) { - tinytex_pos(tinytexcur, &x, &y); - sx = sy = LARGE_EOSD_TEX_SIZE; - mpglBindTexture(gl_target, largeeosdtex[0]); - tinytexcur++; - } else if (is_smalltex(i, smalltexcur)) { - smalltex_pos(smalltexcur, &x, &y); - sx = sy = LARGE_EOSD_TEX_SIZE; - mpglBindTexture(gl_target, largeeosdtex[1]); - smalltexcur++; - } else { - texSize(i->w, i->h, &sx, &sy); - mpglBindTexture(gl_target, *curtex++); - } - glDrawTex(i->dst_x, i->dst_y, i->w, i->h, x, y, i->w, i->h, sx, sy, use_rectangle == 1, 0, 0); - } - mpglEndList(); - mpglBindTexture(gl_target, 0); -} - -/** - * \brief uninitialize OpenGL context, freeing textures, buffers etc. - */ -static void uninitGl(void) { - int i = 0; - if (mpglDeletePrograms && fragprog) - mpglDeletePrograms(1, &fragprog); - fragprog = 0; - while (default_texs[i] != 0) - i++; - if (i) - mpglDeleteTextures(i, default_texs); - default_texs[0] = 0; - clearOSD(); - clearEOSD(); - if (largeeosdtex[0]) - mpglDeleteTextures(2, largeeosdtex); - largeeosdtex[0] = 0; - if (mpglDeleteBuffers && gl_buffer) - mpglDeleteBuffers(1, &gl_buffer); - gl_buffer = 0; gl_buffersize = 0; - gl_bufferptr = NULL; - if (mpglDeleteBuffers && gl_buffer_uv[0]) - mpglDeleteBuffers(2, gl_buffer_uv); - gl_buffer_uv[0] = gl_buffer_uv[1] = 0; gl_buffersize_uv = 0; - gl_bufferptr_uv[0] = gl_bufferptr_uv[1] = 0; -#ifdef CONFIG_GL_X11 - if (mesa_bufferptr) - mpglFreeMemoryMESA(mDisplay, mScreen, mesa_bufferptr); -#endif - mesa_bufferptr = NULL; - err_shown = 0; -} - -static int isSoftwareGl(void) -{ - const char *renderer = mpglGetString(GL_RENDERER); - return !renderer || strcmp(renderer, "Software Rasterizer") == 0 || - strstr(renderer, "llvmpipe"); -} - -static void autodetectGlExtensions(void) { - const char *extensions = mpglGetString(GL_EXTENSIONS); - const char *vendor = mpglGetString(GL_VENDOR); - const char *version = mpglGetString(GL_VERSION); - const char *renderer = mpglGetString(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 (ati_hack == -1) ati_hack = ati_broken_pbo; - if (force_pbo == -1) { - force_pbo = 0; - if (extensions && strstr(extensions, "_pixel_buffer_object")) - force_pbo = is_ati; - } - if (use_rectangle == -1) { - use_rectangle = 0; - if (extensions) { -// if (strstr(extensions, "_texture_non_power_of_two")) - if (strstr(extensions, "_texture_rectangle")) - use_rectangle = renderer && strstr(renderer, "Mesa DRI R200") ? 1 : 0; - } - } - if (use_osd == -1) - use_osd = mpglBindTexture != NULL; - if (use_yuv == -1) - use_yuv = glAutodetectYUVConversion(); - - int eq_caps = 0; - int yuv_mask = (1 << 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; - } - video_eq.capabilities = eq_caps; - - if (is_ati && (lscale == 1 || lscale == 2 || cscale == 1 || 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", - ati_hack, force_pbo, use_rectangle, use_yuv); -} - -static GLint get_scale_type(int chroma) { - int nearest = (chroma ? cscale : lscale) & 64; - if (nearest) - return mipmap_gen ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST; - return mipmap_gen ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR; -} - -/** - * \brief Initialize a (new or reused) OpenGL context. - * set global gl-related variables to their default values - */ -static int initGl(uint32_t d_width, uint32_t d_height) { - GLint scale_type = get_scale_type(0); - autodetectGlExtensions(); - gl_target = use_rectangle == 1 ? GL_TEXTURE_RECTANGLE : GL_TEXTURE_2D; - yuvconvtype = SET_YUV_CONVERSION(use_yuv) | - SET_YUV_LUM_SCALER(lscale) | - SET_YUV_CHROM_SCALER(cscale); - - texSize(image_width, image_height, &texture_width, &texture_height); - - mpglDisable(GL_BLEND); - mpglDisable(GL_DEPTH_TEST); - mpglDepthMask(GL_FALSE); - mpglDisable(GL_CULL_FACE); - mpglEnable(gl_target); - mpglDrawBuffer(vo_doublebuffering?GL_BACK:GL_FRONT); - mpglTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - - mp_msg(MSGT_VO, MSGL_V, "[gl] Creating %dx%d texture...\n", - texture_width, texture_height); - - glCreateClearTex(gl_target, gl_texfmt, gl_format, gl_type, scale_type, - texture_width, texture_height, 0); - if (mipmap_gen) - mpglTexParameteri(gl_target, GL_GENERATE_MIPMAP, GL_TRUE); - - if (is_yuv) { - int i; - int xs, ys, depth; - int chroma_clear_val = 128; - scale_type = get_scale_type(1); - mp_get_chroma_shift(image_format, &xs, &ys, &depth); - chroma_clear_val >>= -depth & 7; - mpglGenTextures(21, default_texs); - default_texs[21] = 0; - for (i = 0; i < 7; i++) { - mpglActiveTexture(GL_TEXTURE1 + i); - mpglBindTexture(GL_TEXTURE_2D, default_texs[i]); - mpglBindTexture(GL_TEXTURE_RECTANGLE, default_texs[i + 7]); - mpglBindTexture(GL_TEXTURE_3D, default_texs[i + 14]); - } - mpglActiveTexture(GL_TEXTURE1); - glCreateClearTex(gl_target, gl_texfmt, gl_format, gl_type, scale_type, - texture_width >> xs, texture_height >> ys, - chroma_clear_val); - if (mipmap_gen) - mpglTexParameteri(gl_target, GL_GENERATE_MIPMAP, GL_TRUE); - mpglActiveTexture(GL_TEXTURE2); - glCreateClearTex(gl_target, gl_texfmt, gl_format, gl_type, scale_type, - texture_width >> xs, texture_height >> ys, - chroma_clear_val); - if (mipmap_gen) - mpglTexParameteri(gl_target, GL_GENERATE_MIPMAP, GL_TRUE); - mpglActiveTexture(GL_TEXTURE0); - mpglBindTexture(gl_target, 0); - } - if (is_yuv || custom_prog) - { - if ((MASK_NOT_COMBINERS & (1 << use_yuv)) || custom_prog) { - if (!mpglGenPrograms || !mpglBindProgram) { - mp_msg(MSGT_VO, MSGL_ERR, "[gl] fragment program functions missing!\n"); - } else { - mpglGenPrograms(1, &fragprog); - mpglBindProgram(GL_FRAGMENT_PROGRAM, fragprog); - } - } - update_yuvconv(); - } - - resize(d_width, d_height); - - mpglClearColor( 0.0f,0.0f,0.0f,0.0f ); - mpglClear( GL_COLOR_BUFFER_BIT ); - if (mpglSwapInterval && swap_interval >= 0) - mpglSwapInterval(swap_interval); - return 1; -} - -static int create_window(uint32_t d_width, uint32_t d_height, uint32_t flags, const char *title) -{ - if (stereo_mode == GL_3D_QUADBUFFER) - flags |= VOFLAG_STEREO; -#ifdef CONFIG_GL_WIN32 - if (glctx.type == GLTYPE_W32 && !vo_w32_config(d_width, d_height, flags)) - return -1; -#endif -#ifdef CONFIG_GL_X11 - if (glctx.type == GLTYPE_X11) { - static int default_glx_attribs[] = { - GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, - GLX_DOUBLEBUFFER, None - }; - static int stereo_glx_attribs[] = { - GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, - GLX_DOUBLEBUFFER, GLX_STEREO, None - }; - XVisualInfo *vinfo = NULL; - if (stereo_mode == GL_3D_QUADBUFFER) { - vinfo = glXChooseVisual(mDisplay, mScreen, stereo_glx_attribs); - if (!vinfo) - mp_msg(MSGT_VO, MSGL_ERR, "[gl] Could not find a stereo visual, " - "3D will probably not work!\n"); - } - if (!vinfo) - vinfo = glXChooseVisual(mDisplay, mScreen, default_glx_attribs); - if (!vinfo) { - mp_msg(MSGT_VO, MSGL_ERR, "[gl] no GLX support present\n"); - return -1; - } - mp_msg(MSGT_VO, MSGL_V, "[gl] GLX chose visual with ID 0x%x\n", (int)vinfo->visualid); - - vo_x11_create_vo_window(vinfo, vo_dx, vo_dy, d_width, d_height, flags, - XCreateColormap(mDisplay, mRootWin, vinfo->visual, AllocNone), - "gl", title); - } -#endif -#ifdef CONFIG_GL_SDL - if (glctx.type == GLTYPE_SDL) { - SDL_WM_SetCaption(title, NULL); - vo_dwidth = d_width; - vo_dheight = d_height; - } -#endif - return 0; -} - -/* connect to server, create and map window, - * allocate colors and (shared) memory - */ -static int -config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, char *title, uint32_t format) -{ - int xs, ys; - image_height = height; - image_width = width; - image_format = format; - is_yuv = mp_get_chroma_shift(image_format, &xs, &ys, NULL) > 0; - is_yuv |= (xs << 8) | (ys << 16); - glFindFormat(format, NULL, &gl_texfmt, &gl_format, &gl_type); - - vo_flipped = !!(flags & VOFLAG_FLIPPING); - - if (create_window(d_width, d_height, flags, title) < 0) - return -1; - - if (vo_config_count) - uninitGl(); - if (glctx.setGlWindow(&glctx) == SET_WINDOW_FAILED) - return -1; - if (mesa_buffer && !mpglAllocateMemoryMESA) { - mp_msg(MSGT_VO, MSGL_ERR, "Can not enable mesa-buffer because AllocateMemoryMESA was not found\n"); - mesa_buffer = 0; - } - initGl(vo_dwidth, vo_dheight); - - return 0; -} - -static void check_events(void) -{ - int e=glctx.check_events(); - if(e&VO_EVENT_REINIT) { - uninitGl(); - initGl(vo_dwidth, vo_dheight); - } - if(e&VO_EVENT_RESIZE) resize(vo_dwidth,vo_dheight); - if(e&VO_EVENT_EXPOSE && int_pause) redraw(); -} - -/** - * Creates the textures and the display list needed for displaying - * an OSD part. - * Callback function for vo_draw_text(). - */ -static void create_osd_texture(int x0, int y0, int w, int h, - unsigned char *src, unsigned char *srca, - int stride) -{ - // initialize to 8 to avoid special-casing on alignment - int sx = 8, sy = 8; - GLint scale_type = scaled_osd ? GL_LINEAR : GL_NEAREST; - - if (w <= 0 || h <= 0 || stride < w) { - mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n"); - return; - } - texSize(w, h, &sx, &sy); - - if (osdtexCnt >= MAX_OSD_PARTS) { - mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the developers!\n"); - return; - } - - // create Textures for OSD part - mpglGenTextures(1, &osdtex[osdtexCnt]); - mpglBindTexture(gl_target, osdtex[osdtexCnt]); - glCreateClearTex(gl_target, GL_LUMINANCE, GL_LUMINANCE, GL_UNSIGNED_BYTE, scale_type, sx, sy, 0); - glUploadTex(gl_target, GL_LUMINANCE, GL_UNSIGNED_BYTE, src, stride, - 0, 0, w, h, 0); - -#ifndef FAST_OSD - mpglGenTextures(1, &osdatex[osdtexCnt]); - mpglBindTexture(gl_target, osdatex[osdtexCnt]); - glCreateClearTex(gl_target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, scale_type, sx, sy, 0); - { - int i; - char *tmp = malloc(stride * h); - // convert alpha from weird MPlayer scale. - // in-place is not possible since it is reused for future OSDs - for (i = h * stride - 1; i >= 0; i--) - tmp[i] = -srca[i]; - glUploadTex(gl_target, GL_ALPHA, GL_UNSIGNED_BYTE, tmp, stride, - 0, 0, w, h, 0); - free(tmp); - } -#endif - - mpglBindTexture(gl_target, 0); - - // Create a list for rendering this OSD part -#ifndef FAST_OSD - osdaDispList[osdtexCnt] = mpglGenLists(1); - mpglNewList(osdaDispList[osdtexCnt], GL_COMPILE); - // render alpha - mpglBindTexture(gl_target, osdatex[osdtexCnt]); - glDrawTex(x0, y0, w, h, 0, 0, w, h, sx, sy, use_rectangle == 1, 0, 0); - mpglEndList(); -#endif - osdDispList[osdtexCnt] = mpglGenLists(1); - mpglNewList(osdDispList[osdtexCnt], GL_COMPILE); - // render OSD - mpglBindTexture(gl_target, osdtex[osdtexCnt]); - glDrawTex(x0, y0, w, h, 0, 0, w, h, sx, sy, use_rectangle == 1, 0, 0); - mpglEndList(); - - osdtexCnt++; -} - -#define RENDER_OSD 1 -#define RENDER_EOSD 2 - -/** - * \param type bit 0: render OSD, bit 1: render EOSD - */ -static void do_render_osd(int type) { - int draw_osd = (type & RENDER_OSD) && osdtexCnt > 0; - int draw_eosd = (type & RENDER_EOSD) && eosdDispList; - if (!draw_osd && !draw_eosd) - return; - // set special rendering parameters - if (!scaled_osd) { - mpglMatrixMode(GL_PROJECTION); - mpglPushMatrix(); - mpglLoadIdentity(); - mpglOrtho(0, vo_dwidth, vo_dheight, 0, -1, 1); - } - mpglEnable(GL_BLEND); - if (draw_eosd) { - mpglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - mpglCallList(eosdDispList); - } - if (draw_osd) { - mpglColor4ub((osd_color >> 16) & 0xff, (osd_color >> 8) & 0xff, osd_color & 0xff, 0xff - (osd_color >> 24)); - // draw OSD -#ifndef FAST_OSD - mpglBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); - mpglCallLists(osdtexCnt, GL_UNSIGNED_INT, osdaDispList); -#endif - mpglBlendFunc(GL_SRC_ALPHA, GL_ONE); - mpglCallLists(osdtexCnt, GL_UNSIGNED_INT, osdDispList); - } - // set rendering parameters back to defaults - mpglDisable(GL_BLEND); - if (!scaled_osd) - mpglPopMatrix(); - mpglBindTexture(gl_target, 0); -} - -static void draw_osd(void) -{ - if (!use_osd) return; - if (vo_osd_changed(0)) { - int osd_h, osd_w; - clearOSD(); - osd_w = scaled_osd ? image_width : vo_dwidth; - osd_h = scaled_osd ? image_height : vo_dheight; - vo_draw_text_ext(osd_w, osd_h, ass_border_x, ass_border_y, ass_border_x, ass_border_y, - image_width, image_height, create_osd_texture); - } - if (vo_doublebuffering) do_render_osd(RENDER_OSD); -} - -static void do_render(void) { -// Enable(GL_TEXTURE_2D); -// BindTexture(GL_TEXTURE_2D, texture_id); - - mpglColor3f(1,1,1); - if (is_yuv || custom_prog) - glEnableYUVConversion(gl_target, yuvconvtype); - if (stereo_mode) { - glEnable3DLeft(stereo_mode); - glDrawTex(0, 0, image_width, image_height, - 0, 0, image_width >> 1, image_height, - texture_width, texture_height, - use_rectangle == 1, is_yuv, - mpi_flipped ^ vo_flipped); - glEnable3DRight(stereo_mode); - glDrawTex(0, 0, image_width, image_height, - image_width >> 1, 0, image_width >> 1, image_height, - texture_width, texture_height, - use_rectangle == 1, is_yuv, - mpi_flipped ^ vo_flipped); - glDisable3D(stereo_mode); - } else { - glDrawTex(0, 0, image_width, image_height, - 0, 0, image_width, image_height, - texture_width, texture_height, - use_rectangle == 1, is_yuv, - mpi_flipped ^ vo_flipped); - } - if (is_yuv || custom_prog) - glDisableYUVConversion(gl_target, yuvconvtype); -} - -static void flip_page(void) { - if (vo_doublebuffering) { - if (use_glFinish) mpglFinish(); - glctx.swapGlBuffers(&glctx); - if (aspect_scaling() && use_aspect) - mpglClear(GL_COLOR_BUFFER_BIT); - } else { - do_render(); - do_render_osd(RENDER_OSD | RENDER_EOSD); - if (use_glFinish) mpglFinish(); - else mpglFlush(); - } -} - -static void redraw(void) { - if (vo_doublebuffering) { do_render(); do_render_osd(RENDER_OSD | RENDER_EOSD); } - flip_page(); -} - -static int draw_slice(uint8_t *src[], int stride[], int w,int h,int x,int y) -{ - mpi_flipped = stride[0] < 0; - glUploadTex(gl_target, gl_format, gl_type, src[0], stride[0], - x, y, w, h, slice_height); - if (is_yuv) { - int xs, ys; - mp_get_chroma_shift(image_format, &xs, &ys, NULL); - mpglActiveTexture(GL_TEXTURE1); - glUploadTex(gl_target, gl_format, gl_type, src[1], stride[1], - x >> xs, y >> ys, w >> xs, h >> ys, slice_height); - mpglActiveTexture(GL_TEXTURE2); - glUploadTex(gl_target, gl_format, gl_type, src[2], stride[2], - x >> xs, y >> ys, w >> xs, h >> ys, slice_height); - mpglActiveTexture(GL_TEXTURE0); - } - return 0; -} - -static uint32_t get_image(mp_image_t *mpi) { - int needed_size; - if (!mpglGenBuffers || !mpglBindBuffer || !mpglBufferData || !mpglMapBuffer) { - if (!err_shown) - mp_msg(MSGT_VO, MSGL_ERR, "[gl] extensions missing for dr\n" - "Expect a _major_ speed penalty\n"); - 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 (mesa_buffer) mpi->width = texture_width; - else if (ati_hack) { - mpi->width = texture_width; - mpi->height = texture_height; - } - mpi->stride[0] = mpi->width * mpi->bpp / 8; - needed_size = mpi->stride[0] * mpi->height; - if (mesa_buffer) { -#ifdef CONFIG_GL_X11 - if (mesa_bufferptr && needed_size > mesa_buffersize) { - mpglFreeMemoryMESA(mDisplay, mScreen, mesa_bufferptr); - mesa_bufferptr = NULL; - } - if (!mesa_bufferptr) - mesa_bufferptr = mpglAllocateMemoryMESA(mDisplay, mScreen, needed_size, 0, 1.0, 1.0); - mesa_buffersize = needed_size; -#endif - mpi->planes[0] = mesa_bufferptr; - } else { - if (!gl_buffer) - mpglGenBuffers(1, &gl_buffer); - mpglBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_buffer); - if (needed_size > gl_buffersize) { - gl_buffersize = needed_size; - mpglBufferData(GL_PIXEL_UNPACK_BUFFER, gl_buffersize, - NULL, GL_DYNAMIC_DRAW); - } - if (!gl_bufferptr) - gl_bufferptr = mpglMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); - mpi->planes[0] = gl_bufferptr; - mpglBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - } - if (!mpi->planes[0]) { - if (!err_shown) - mp_msg(MSGT_VO, MSGL_ERR, "[gl] could not acquire buffer for dr\n" - "Expect a _major_ speed penalty\n"); - err_shown = 1; - return VO_FALSE; - } - if (is_yuv) { - // planar YUV - int xs, ys, component_bits; - mp_get_chroma_shift(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 (ati_hack && !mesa_buffer) { - mpi->flags &= ~MP_IMGFLAG_COMMON_PLANE; - if (!gl_buffer_uv[0]) mpglGenBuffers(2, gl_buffer_uv); - if (mpi->stride[1] * mpi->height > gl_buffersize_uv) { - mpglBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_buffer_uv[0]); - mpglBufferData(GL_PIXEL_UNPACK_BUFFER, mpi->stride[1] * mpi->height, - NULL, GL_DYNAMIC_DRAW); - mpglBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_buffer_uv[1]); - mpglBufferData(GL_PIXEL_UNPACK_BUFFER, mpi->stride[1] * mpi->height, - NULL, GL_DYNAMIC_DRAW); - gl_buffersize_uv = mpi->stride[1] * mpi->height; - } - if (!gl_bufferptr_uv[0]) { - mpglBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_buffer_uv[0]); - gl_bufferptr_uv[0] = mpglMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); - mpglBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_buffer_uv[1]); - gl_bufferptr_uv[1] = mpglMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY); - } - mpi->planes[1] = gl_bufferptr_uv[0]; - mpi->planes[2] = gl_bufferptr_uv[1]; - } - } - mpi->flags |= MP_IMGFLAG_DIRECT; - return VO_TRUE; -} - -static void clear_border(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) { - memset(dst + start, value, right_border); - dst += stride; - height--; - } - if (bottom_border > 0) - memset(dst, value, stride * bottom_border); -} - -static uint32_t draw_image(mp_image_t *mpi) { - int slice = 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 (force_pbo && !(mpi->flags & MP_IMGFLAG_DIRECT) && !gl_bufferptr && get_image(&mpi2) == VO_TRUE) { - int bpp = mpi->bpp; - int xs, ys, component_bits; - mp_get_chroma_shift(image_format, &xs, &ys, &component_bits); - if (is_yuv) - bpp = component_bits + 7; - memcpy_pic(mpi2.planes[0], mpi->planes[0], mpi->w * bpp / 8, mpi->h, mpi2.stride[0], mpi->stride[0]); - if (is_yuv) { - int bp = (component_bits + 7) / 8; - memcpy_pic(mpi2.planes[1], mpi->planes[1], (mpi->w >> xs) * bp, mpi->h >> ys, mpi2.stride[1], mpi->stride[1]); - memcpy_pic(mpi2.planes[2], mpi->planes[2], (mpi->w >> xs) * bp, mpi->h >> ys, mpi2.stride[2], mpi->stride[2]); - } - if (ati_hack) { // since we have to do a full upload we need to clear the borders - clear_border(mpi2.planes[0], mpi->w * bpp / 8, mpi2.stride[0], mpi->h, mpi2.height, 0); - if (is_yuv) { - clear_border(mpi2.planes[1], mpi->w >> xs, mpi2.stride[1], mpi->h >> ys, mpi2.height >> ys, 128); - clear_border(mpi2.planes[2], mpi->w >> xs, mpi2.stride[2], mpi->h >> ys, mpi2.height >> ys, 128); - } - } - 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]; - mpi_flipped = stride[0] < 0; - if (mpi->flags & MP_IMGFLAG_DIRECT) { - if (mesa_buffer) { - mpglPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, 1); - w = texture_width; - } else { - intptr_t base = (intptr_t)planes[0]; - if (ati_hack) { w = texture_width; h = texture_height; } - if (mpi_flipped) - base += (mpi->h - 1) * stride[0]; - planes[0] -= base; - planes[1] -= base; - planes[2] -= base; - mpglBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_buffer); - mpglUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - gl_bufferptr = NULL; - if (!(mpi->flags & MP_IMGFLAG_COMMON_PLANE)) - planes[0] = planes[1] = planes[2] = NULL; - } - slice = 0; // always "upload" full texture - } - glUploadTex(gl_target, gl_format, gl_type, planes[0], stride[0], - mpi->x, mpi->y, w, h, slice); - if (is_yuv) { - int xs, ys; - mp_get_chroma_shift(image_format, &xs, &ys, NULL); - if ((mpi->flags & MP_IMGFLAG_DIRECT) && !(mpi->flags & MP_IMGFLAG_COMMON_PLANE)) { - mpglBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_buffer_uv[0]); - mpglUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - gl_bufferptr_uv[0] = NULL; - } - mpglActiveTexture(GL_TEXTURE1); - glUploadTex(gl_target, gl_format, gl_type, planes[1], stride[1], - mpi->x >> xs, mpi->y >> ys, w >> xs, h >> ys, slice); - if ((mpi->flags & MP_IMGFLAG_DIRECT) && !(mpi->flags & MP_IMGFLAG_COMMON_PLANE)) { - mpglBindBuffer(GL_PIXEL_UNPACK_BUFFER, gl_buffer_uv[1]); - mpglUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - gl_bufferptr_uv[1] = NULL; - } - mpglActiveTexture(GL_TEXTURE2); - glUploadTex(gl_target, gl_format, gl_type, planes[2], stride[2], - mpi->x >> xs, mpi->y >> ys, w >> xs, h >> ys, slice); - mpglActiveTexture(GL_TEXTURE0); - } - if (mpi->flags & MP_IMGFLAG_DIRECT) { - if (mesa_buffer) mpglPixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, 0); - else mpglBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - } -skip_upload: - if (vo_doublebuffering) do_render(); - return VO_TRUE; -} - -static int -draw_frame(uint8_t *src[]) -{ - return VO_ERROR; -} - -static int -query_format(uint32_t format) -{ - int depth; - int caps = VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW | - VFCAP_FLIP | - VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE; - if (use_osd) - caps |= VFCAP_OSD | VFCAP_EOSD | (scaled_osd ? 0 : VFCAP_EOSD_UNSCALED); - if (format == IMGFMT_RGB24 || format == IMGFMT_RGBA) - return caps; - if (use_yuv && mp_get_chroma_shift(format, NULL, NULL, &depth) && - (depth == 8 || depth == 16 || glYUVLargeRange(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 (!use_yuv && (format == IMGFMT_Y8 || format == IMGFMT_Y800)) - return 0; - if (!use_ycbcr && (format == IMGFMT_UYVY || format == IMGFMT_YVYU)) - return 0; - if (many_fmts && - glFindFormat(format, NULL, NULL, NULL, NULL)) - return caps; - return 0; -} - - -static void -uninit(void) -{ - uninitGl(); - free(custom_prog); - custom_prog = NULL; - free(custom_tex); - custom_tex = NULL; - uninit_mpglcontext(&glctx); -} - -static const opt_t subopts[] = { - {"manyfmts", OPT_ARG_BOOL, &many_fmts, NULL}, - {"osd", OPT_ARG_BOOL, &use_osd, NULL}, - {"scaled-osd", OPT_ARG_BOOL, &scaled_osd, NULL}, - {"aspect", OPT_ARG_BOOL, &use_aspect, NULL}, - {"ycbcr", OPT_ARG_BOOL, &use_ycbcr, NULL}, - {"slice-height", OPT_ARG_INT, &slice_height, int_non_neg}, - {"rectangle", OPT_ARG_INT, &use_rectangle,int_non_neg}, - {"yuv", OPT_ARG_INT, &use_yuv, int_non_neg}, - {"colorspace", OPT_ARG_INT, &user_colorspace, NULL}, - {"levelconv", OPT_ARG_INT, &levelconv, NULL}, - {"lscale", OPT_ARG_INT, &lscale, int_non_neg}, - {"cscale", OPT_ARG_INT, &cscale, int_non_neg}, - {"filter-strength", OPT_ARG_FLOAT, &filter_strength, NULL}, - {"ati-hack", OPT_ARG_BOOL, &ati_hack, NULL}, - {"force-pbo", OPT_ARG_BOOL, &force_pbo, NULL}, - {"mesa-buffer", OPT_ARG_BOOL, &mesa_buffer, NULL}, - {"glfinish", OPT_ARG_BOOL, &use_glFinish, NULL}, - {"swapinterval", OPT_ARG_INT, &swap_interval,NULL}, - {"customprog", OPT_ARG_MSTRZ,&custom_prog, NULL}, - {"customtex", OPT_ARG_MSTRZ,&custom_tex, NULL}, - {"customtlin", OPT_ARG_BOOL, &custom_tlin, NULL}, - {"customtrect", OPT_ARG_BOOL, &custom_trect, NULL}, - {"mipmapgen", OPT_ARG_BOOL, &mipmap_gen, NULL}, - {"osdcolor", OPT_ARG_INT, &osd_color, NULL}, - {"stereo", OPT_ARG_INT, &stereo_mode, NULL}, - {NULL} -}; - -static int preinit_internal(const char *arg, int allow_sw) -{ - // set defaults - enum MPGLType gltype = GLTYPE_AUTO; - many_fmts = 1; - use_osd = -1; - scaled_osd = 0; - use_aspect = 1; - use_ycbcr = 0; - use_yuv = -1; - user_colorspace = 0; - levelconv = -1; - lscale = 0; - cscale = 0; - filter_strength = 0.5; - use_rectangle = -1; - use_glFinish = 0; - ati_hack = -1; - force_pbo = -1; - mesa_buffer = 0; - swap_interval = 1; - slice_height = 0; - custom_prog = NULL; - custom_tex = NULL; - custom_tlin = 1; - custom_trect = 0; - mipmap_gen = 0; - osd_color = 0xffffff; - stereo_mode = 0; - if (subopt_parse(arg, subopts) != 0) { - mp_msg(MSGT_VO, MSGL_FATAL, - "\n-vo gl command line help:\n" - "Example: mplayer -vo gl:slice-height=4\n" - "\nOptions:\n" - " nomanyfmts\n" - " Disable extended color formats for OpenGL 1.2 and later\n" - " slice-height=<0-...>\n" - " Slice size for texture transfer, 0 for whole image\n" - " noosd\n" - " Do not use OpenGL OSD code\n" - " scaled-osd\n" - " Render OSD at movie resolution and scale it\n" - " noaspect\n" - " Do not do aspect scaling\n" - " rectangle=<0,1,2>\n" - " 0: use power-of-two textures\n" - " 1: use texture_rectangle\n" - " 2: use texture_non_power_of_two\n" - " ati-hack\n" - " Workaround ATI bug with PBOs\n" - " force-pbo\n" - " Force use of PBO even if this involves an extra memcpy\n" - " glfinish\n" - " Call glFinish() before swapping buffers\n" - " swapinterval=\n" - " Interval in displayed frames between to buffer swaps.\n" - " 1 is equivalent to enable VSYNC, 0 to disable VSYNC.\n" - " Requires GLX_SGI_swap_control support to work.\n" - " ycbcr\n" - " also try to use the GL_MESA_ycbcr_texture extension\n" - " yuv=\n" - " 0: use software YUV to RGB conversion.\n" - " 1: use register combiners (nVidia only, for older cards).\n" - " 2: use fragment program.\n" - " 3: use fragment program with gamma correction.\n" - " 4: use fragment program with gamma correction via lookup.\n" - " 5: use ATI-specific method (for older cards).\n" - " 6: use lookup via 3D texture.\n" - " lscale=\n" - " 0: use standard bilinear scaling for luma.\n" - " 1: use improved bicubic scaling for luma.\n" - " 2: use cubic in X, linear in Y direction scaling for luma.\n" - " 3: as 1 but without using a lookup texture.\n" - " 4: experimental unsharp masking (sharpening).\n" - " 5: experimental unsharp masking (sharpening) with larger radius.\n" - " cscale=\n" - " as lscale but for chroma (2x slower with little visible effect).\n" - " filter-strength=\n" - " set the effect strength for some lscale/cscale filters\n" - " customprog=\n" - " use a custom YUV conversion program\n" - " customtex=\n" - " use a custom YUV conversion lookup texture\n" - " nocustomtlin\n" - " use GL_NEAREST scaling for customtex texture\n" - " customtrect\n" - " use texture_rectangle for customtex texture\n" - " mipmapgen\n" - " generate mipmaps for the video image (use with TXB in customprog)\n" - " osdcolor=<0xAARRGGBB>\n" - " use the given color for the OSD\n" - " stereo=\n" - " 0: normal display\n" - " 1: side-by-side to red-cyan stereo\n" - " 2: side-by-side to green-magenta stereo\n" - " 3: side-by-side to quadbuffer stereo\n" - "\n" ); - return -1; - } - if (user_colorspace != 0 || levelconv != -1) { - mp_msg(MSGT_VO, MSGL_ERR, "[gl] \"colorspace\" and \"levelconv\" " - "suboptions have been removed. Use options --colormatrix and" - " --colormatrix-input-range/--colormatrix-output-range instead.\n"); - return -1; - } - if (!init_mpglcontext(&glctx, gltype)) - goto err_out; - if (use_yuv == -1 || !allow_sw) { - if (create_window(320, 200, VOFLAG_HIDDEN, NULL) < 0) - goto err_out; - if (glctx.setGlWindow(&glctx) == SET_WINDOW_FAILED) - goto err_out; - if (!allow_sw && isSoftwareGl()) - goto err_out; - autodetectGlExtensions(); - } - if (many_fmts) - mp_msg(MSGT_VO, MSGL_INFO, "[gl] using extended formats. " - "Use -vo gl:nomanyfmts if playback fails.\n"); - mp_msg(MSGT_VO, MSGL_V, "[gl] Using %d as slice height " - "(0 means image height).\n", slice_height); - - return 0; - -err_out: - uninit(); - return -1; -} - -static int preinit(const char *arg) -{ - return preinit_internal(arg, 1); -} - -static int preinit_nosw(const char *arg) -{ - return preinit_internal(arg, 0); -} - -static int control(uint32_t request, void *data) -{ - switch (request) { - case VOCTRL_PAUSE: - case VOCTRL_RESUME: - int_pause = (request == VOCTRL_PAUSE); - return VO_TRUE; - case VOCTRL_QUERY_FORMAT: - return query_format(*(uint32_t*)data); - case VOCTRL_GET_IMAGE: - return get_image(data); - case VOCTRL_DRAW_IMAGE: - return draw_image(data); - case VOCTRL_DRAW_EOSD: - if (!data) - return VO_FALSE; - genEOSD(data); - if (vo_doublebuffering) do_render_osd(RENDER_EOSD); - return VO_TRUE; - case VOCTRL_GET_EOSD_RES: - { - mp_eosd_res_t *r = data; - r->w = vo_dwidth; r->h = vo_dheight; - r->mt = r->mb = r->ml = r->mr = 0; - if (scaled_osd) {r->w = image_width; r->h = image_height;} - else if (aspect_scaling()) { - r->ml = r->mr = ass_border_x; - r->mt = r->mb = ass_border_y; - } - } - return VO_TRUE; - case VOCTRL_ONTOP: - glctx.ontop(); - return VO_TRUE; - case VOCTRL_FULLSCREEN: - glctx.fullscreen(); - resize(vo_dwidth, vo_dheight); - return VO_TRUE; - case VOCTRL_BORDER: - glctx.border(); - resize(vo_dwidth, vo_dheight); - return VO_TRUE; - case VOCTRL_GET_PANSCAN: - if (!use_aspect) return VO_NOTIMPL; - return VO_TRUE; - case VOCTRL_SET_PANSCAN: - if (!use_aspect) return VO_NOTIMPL; - resize(vo_dwidth, vo_dheight); - return VO_TRUE; - case VOCTRL_GET_EQUALIZER: - if (is_yuv) { - struct voctrl_get_equalizer_args *args = data; - return mp_csp_equalizer_get(&video_eq, args->name, args->valueptr) >= 0 ? - VO_TRUE : VO_NOTIMPL; - } - break; - case VOCTRL_SET_EQUALIZER: - if (is_yuv) { - struct voctrl_set_equalizer_args *args = data; - if (mp_csp_equalizer_set(&video_eq, args->name, args->value) < 0) - return VO_NOTIMPL; - update_yuvconv(); - return VO_TRUE; - } - break; - case VOCTRL_SET_YUV_COLORSPACE: { - bool supports_csp = (1 << use_yuv) & MASK_NOT_COMBINERS; - if (vo_config_count && supports_csp) { - colorspace = *(struct mp_csp_details *)data; - update_yuvconv(); - } - return VO_TRUE; - } - case VOCTRL_GET_YUV_COLORSPACE: - *(struct mp_csp_details *)data = colorspace; - return VO_TRUE; - case VOCTRL_UPDATE_SCREENINFO: - glctx.update_xinerama_info(); - return VO_TRUE; - case VOCTRL_REDRAW_OSD: - if (vo_doublebuffering) - do_render(); - draw_osd(); - if (vo_doublebuffering) - do_render_osd(2); - flip_page(); - return VO_TRUE; - } - return VO_NOTIMPL; -} +/* + * 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 +#include +#include +#include +#include + +#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 "osd.h" +#include "sub/font_load.h" +#include "sub/sub.h" + +#ifdef CONFIG_GL_X11 +#include "x11_common.h" +#endif + +#include "gl_common.h" +#include "aspect.h" +#include "fastmemcpy.h" +#include "sub/ass_mp.h" + +#ifdef CONFIG_GL_SDL +#ifdef CONFIG_SDL_SDL_H +#include +#else +#include +#endif +#endif + + +static int preinit_nosw(struct vo *vo, const char *arg); + +//! How many parts the OSD may consist of at most +#define MAX_OSD_PARTS 20 + +#define LARGE_EOSD_TEX_SIZE 512 +#define TINYTEX_SIZE 16 +#define TINYTEX_COLS (LARGE_EOSD_TEX_SIZE / TINYTEX_SIZE) +#define TINYTEX_MAX (TINYTEX_COLS * TINYTEX_COLS) +#define SMALLTEX_SIZE 32 +#define SMALLTEX_COLS (LARGE_EOSD_TEX_SIZE / SMALLTEX_SIZE) +#define SMALLTEX_MAX (SMALLTEX_COLS * SMALLTEX_COLS) + +//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 use_osd; + int scaled_osd; + //! Textures for OSD + GLuint osdtex[MAX_OSD_PARTS]; +#ifndef FAST_OSD + //! Alpha textures for OSD + GLuint osdatex[MAX_OSD_PARTS]; +#endif + GLuint *eosdtex; + GLuint largeeosdtex[2]; + //! Display lists that draw the OSD parts + GLuint osdDispList[MAX_OSD_PARTS]; +#ifndef FAST_OSD + GLuint osdaDispList[MAX_OSD_PARTS]; +#endif + GLuint eosdDispList; + //! How many parts the OSD currently consists of + int osdtexCnt; + int eosdtexCnt; + int osd_color; + + int use_aspect; + int use_ycbcr; + int use_yuv; + struct mp_csp_details colorspace; + int is_yuv; + int lscale; + int cscale; + float filter_strength; + int yuvconvtype; + int use_rectangle; + int err_shown; + uint32_t image_width; + uint32_t image_height; + uint32_t image_format; + uint32_t image_d_width; + uint32_t image_d_height; + int many_fmts; + int ati_hack; + int force_pbo; + int mesa_buffer; + 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]; + int mesa_buffersize; + void *mesa_bufferptr; + GLuint fragprog; + GLuint default_texs[22]; + char *custom_prog; + char *custom_tex; + int custom_tlin; + int custom_trect; + int mipmap_gen; + int stereo_mode; + + int int_pause; + + 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 redraw(struct vo *vo); + +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); + if (WinID >= 0) { + int left = 0, top = 0, w = x, h = y; + geometry(&left, &top, &w, &h, vo->dwidth, vo->dheight); + top = y - h - top; + gl->Viewport(left, top, w, h); + } else + gl->Viewport(0, 0, x, y); + + gl->MatrixMode(GL_PROJECTION); + gl->LoadIdentity(); + p->ass_border_x = p->ass_border_y = 0; + if (aspect_scaling() && p->use_aspect) { + 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(); + + if (!p->scaled_osd) { +#ifdef CONFIG_FREETYPE + // adjust font size to display size + force_load_font = 1; +#endif + vo_osd_changed(OSDTYPE_OSD); + } + gl->Clear(GL_COLOR_BUFFER_BIT); + redraw(vo); +} + +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->mesa_buffer) + *texw = (*texw + 63) & ~63; + else 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 + }; + 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_shift = -depth & 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); + } + } +} + +/** + * \brief remove all OSD textures and display-lists, thus clearing it. + */ +static void clearOSD(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int i; + if (!p->osdtexCnt) + return; + gl->DeleteTextures(p->osdtexCnt, p->osdtex); +#ifndef FAST_OSD + gl->DeleteTextures(p->osdtexCnt, p->osdatex); + for (i = 0; i < p->osdtexCnt; i++) + gl->DeleteLists(p->osdaDispList[i], 1); +#endif + for (i = 0; i < p->osdtexCnt; i++) + gl->DeleteLists(p->osdDispList[i], 1); + p->osdtexCnt = 0; +} + +/** + * \brief remove textures, display list and free memory used by EOSD + */ +static void clearEOSD(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + if (p->eosdDispList) + gl->DeleteLists(p->eosdDispList, 1); + p->eosdDispList = 0; + if (p->eosdtexCnt) + gl->DeleteTextures(p->eosdtexCnt, p->eosdtex); + p->eosdtexCnt = 0; + free(p->eosdtex); + p->eosdtex = NULL; +} + +static inline int is_tinytex(ASS_Image *i, int tinytexcur) +{ + return i->w < TINYTEX_SIZE && i->h < TINYTEX_SIZE + && tinytexcur < TINYTEX_MAX; +} + +static inline int is_smalltex(ASS_Image *i, int smalltexcur) +{ + return i->w < SMALLTEX_SIZE && i->h < SMALLTEX_SIZE + && smalltexcur < SMALLTEX_MAX; +} + +static inline void tinytex_pos(int tinytexcur, int *x, int *y) +{ + *x = (tinytexcur % TINYTEX_COLS) * TINYTEX_SIZE; + *y = (tinytexcur / TINYTEX_COLS) * TINYTEX_SIZE; +} + +static inline void smalltex_pos(int smalltexcur, int *x, int *y) +{ + *x = (smalltexcur % SMALLTEX_COLS) * SMALLTEX_SIZE; + *y = (smalltexcur / SMALLTEX_COLS) * SMALLTEX_SIZE; +} + +/** + * \brief construct display list from ass image list + * \param img image list to create OSD from. + * A value of NULL has the same effect as clearEOSD() + */ +static void genEOSD(struct vo *vo, mp_eosd_images_t *imgs) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int sx, sy; + int tinytexcur = 0; + int smalltexcur = 0; + GLuint *curtex; + GLint scale_type = p->scaled_osd ? GL_LINEAR : GL_NEAREST; + ASS_Image *img = imgs->imgs; + ASS_Image *i; + + if (imgs->changed == 0) // there are elements, but they are unchanged + return; + if (img && imgs->changed == 1) // there are elements, but they just moved + goto skip_upload; + + clearEOSD(vo); + if (!img) + return; + if (!p->largeeosdtex[0]) { + gl->GenTextures(2, p->largeeosdtex); + for (int n = 0; n < 2; n++) { + gl->BindTexture(p->target, p->largeeosdtex[n]); + glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, + GL_UNSIGNED_BYTE, scale_type, + LARGE_EOSD_TEX_SIZE, LARGE_EOSD_TEX_SIZE, 0); + } + } + for (i = img; i; i = i->next) { + if (i->w <= 0 || i->h <= 0 || i->stride < i->w) + continue; + if (is_tinytex(i, tinytexcur)) + tinytexcur++; + else if (is_smalltex(i, smalltexcur)) + smalltexcur++; + else + p->eosdtexCnt++; + } + mp_msg(MSGT_VO, MSGL_DBG2, "EOSD counts (tiny, small, all): %i, %i, %i\n", + tinytexcur, smalltexcur, p->eosdtexCnt); + if (p->eosdtexCnt) { + p->eosdtex = calloc(p->eosdtexCnt, sizeof(GLuint)); + gl->GenTextures(p->eosdtexCnt, p->eosdtex); + } + tinytexcur = smalltexcur = 0; + for (i = img, curtex = p->eosdtex; i; i = i->next) { + int x = 0, y = 0; + if (i->w <= 0 || i->h <= 0 || i->stride < i->w) { + mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n"); + continue; + } + if (is_tinytex(i, tinytexcur)) { + tinytex_pos(tinytexcur, &x, &y); + gl->BindTexture(p->target, p->largeeosdtex[0]); + tinytexcur++; + } else if (is_smalltex(i, smalltexcur)) { + smalltex_pos(smalltexcur, &x, &y); + gl->BindTexture(p->target, p->largeeosdtex[1]); + smalltexcur++; + } else { + texSize(vo, i->w, i->h, &sx, &sy); + gl->BindTexture(p->target, *curtex++); + glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, + GL_UNSIGNED_BYTE, scale_type, sx, sy, 0); + } + glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, i->bitmap, + i->stride, x, y, i->w, i->h, 0); + } + p->eosdDispList = gl->GenLists(1); +skip_upload: + gl->NewList(p->eosdDispList, GL_COMPILE); + tinytexcur = smalltexcur = 0; + for (i = img, curtex = p->eosdtex; i; i = i->next) { + int x = 0, y = 0; + if (i->w <= 0 || i->h <= 0 || i->stride < i->w) + continue; + gl->Color4ub(i->color >> 24, (i->color >> 16) & 0xff, + (i->color >> 8) & 0xff, 255 - (i->color & 0xff)); + if (is_tinytex(i, tinytexcur)) { + tinytex_pos(tinytexcur, &x, &y); + sx = sy = LARGE_EOSD_TEX_SIZE; + gl->BindTexture(p->target, p->largeeosdtex[0]); + tinytexcur++; + } else if (is_smalltex(i, smalltexcur)) { + smalltex_pos(smalltexcur, &x, &y); + sx = sy = LARGE_EOSD_TEX_SIZE; + gl->BindTexture(p->target, p->largeeosdtex[1]); + smalltexcur++; + } else { + texSize(vo, i->w, i->h, &sx, &sy); + gl->BindTexture(p->target, *curtex++); + } + glDrawTex(gl, i->dst_x, i->dst_y, i->w, i->h, x, y, i->w, i->h, sx, sy, + p->use_rectangle == 1, 0, 0); + } + gl->EndList(); + gl->BindTexture(p->target, 0); +} + +/** + * \brief uninitialize OpenGL context, freeing textures, buffers etc. + */ +static void uninitGl(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + 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; + clearOSD(vo); + clearEOSD(vo); + if (p->largeeosdtex[0]) + gl->DeleteTextures(2, p->largeeosdtex); + p->largeeosdtex[0] = 0; + if (gl->DeleteBuffers && p->buffer) + gl->DeleteBuffers(1, &p->buffer); + 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; +#ifdef CONFIG_GL_X11 + if (p->mesa_bufferptr) + gl->FreeMemoryMESA(vo->x11->display, vo->x11->screen, + p->mesa_bufferptr); +#endif + p->mesa_bufferptr = NULL; + p->err_shown = 0; +} + +static int isSoftwareGl(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + const char *renderer = p->gl->GetString(GL_RENDERER); + return !renderer || strcmp(renderer, "Software Rasterizer") == 0 || + strstr(renderer, "llvmpipe"); +} + +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; + } + 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; +} + +/** + * \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(vo_doublebuffering ? GL_BACK : GL_FRONT); + 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; + int chroma_clear_val = 128; + scale_type = get_scale_type(vo, 1); + mp_get_chroma_shift(p->image_format, &xs, &ys, &depth); + chroma_clear_val >>= -depth & 7; + 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, + chroma_clear_val); + 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, + chroma_clear_val); + 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); + } + + 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 int create_window(struct vo *vo, uint32_t d_width, uint32_t d_height, + uint32_t flags, const char *title) +{ + struct gl_priv *p = vo->priv; + + if (p->stereo_mode == GL_3D_QUADBUFFER) + flags |= VOFLAG_STEREO; +#ifdef CONFIG_GL_WIN32 + if (p->glctx->type == GLTYPE_W32 && !vo_w32_config(d_width, d_height, flags)) + return -1; +#endif +#ifdef CONFIG_GL_X11 + if (p->glctx->type == GLTYPE_X11) { + static int default_glx_attribs[] = { + GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, None + }; + static int stereo_glx_attribs[] = { + GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, + GLX_DOUBLEBUFFER, GLX_STEREO, None + }; + XVisualInfo *vinfo = NULL; + if (p->stereo_mode == GL_3D_QUADBUFFER) { + vinfo = glXChooseVisual(vo->x11->display, vo->x11->screen, + stereo_glx_attribs); + if (!vinfo) + mp_msg(MSGT_VO, MSGL_ERR, "[gl] Could not find a stereo visual," + " 3D will probably not work!\n"); + } + if (!vinfo) + vinfo = glXChooseVisual(vo->x11->display, vo->x11->screen, + default_glx_attribs); + if (!vinfo) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] no GLX support present\n"); + return -1; + } + mp_msg(MSGT_VO, MSGL_V, "[gl] GLX chose visual with ID 0x%x\n", + (int)vinfo->visualid); + + Colormap colormap = XCreateColormap(vo->x11->display, vo->x11->rootwin, + vinfo->visual, AllocNone); + vo_x11_create_vo_window(vo, vinfo, vo->dx, vo->dy, d_width, d_height, + flags, colormap, "gl", title); + } +#endif +#ifdef CONFIG_GL_SDL + if (p->glctx->type == GLTYPE_SDL) { + SDL_WM_SetCaption(title, NULL); + vo->dwidth = d_width; + vo->dheight = d_height; + } +#endif + return 0; +} + +static int config(struct vo *vo, uint32_t width, uint32_t height, + uint32_t d_width, uint32_t d_height, uint32_t flags, + char *title, 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->image_d_width = d_width; + p->image_d_height = d_height; + p->is_yuv = mp_get_chroma_shift(p->image_format, &xs, &ys, NULL) > 0; + p->is_yuv |= (xs << 8) | (ys << 16); + glFindFormat(format, NULL, &p->texfmt, &p->gl_format, &p->gl_type); + + p->vo_flipped = !!(flags & VOFLAG_FLIPPING); + + if (create_window(vo, d_width, d_height, flags, title) < 0) + return -1; + + if (vo->config_count) + uninitGl(vo); + if (p->glctx->setGlWindow(p->glctx) == SET_WINDOW_FAILED) + return -1; + if (p->mesa_buffer && !p->gl->AllocateMemoryMESA) { + mp_msg(MSGT_VO, MSGL_ERR, "Can not enable mesa-buffer because " + "AllocateMemoryMESA was not found\n"); + p->mesa_buffer = 0; + } + 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 && p->int_pause) + redraw(vo); +} + +/** + * Creates the textures and the display list needed for displaying + * an OSD part. + * Callback function for osd_draw_text_ext(). + */ +static void create_osd_texture(void *ctx, int x0, int y0, int w, int h, + unsigned char *src, unsigned char *srca, + int stride) +{ + struct vo *vo = ctx; + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + // initialize to 8 to avoid special-casing on alignment + int sx = 8, sy = 8; + GLint scale_type = p->scaled_osd ? GL_LINEAR : GL_NEAREST; + + if (w <= 0 || h <= 0 || stride < w) { + mp_msg(MSGT_VO, MSGL_V, "Invalid dimensions OSD for part!\n"); + return; + } + texSize(vo, w, h, &sx, &sy); + + if (p->osdtexCnt >= MAX_OSD_PARTS) { + mp_msg(MSGT_VO, MSGL_ERR, "Too many OSD parts, contact the developers!\n"); + return; + } + + // create Textures for OSD part + gl->GenTextures(1, &p->osdtex[p->osdtexCnt]); + gl->BindTexture(p->target, p->osdtex[p->osdtexCnt]); + glCreateClearTex(gl, p->target, GL_LUMINANCE, GL_LUMINANCE, + GL_UNSIGNED_BYTE, scale_type, sx, sy, 0); + glUploadTex(gl, p->target, GL_LUMINANCE, GL_UNSIGNED_BYTE, src, stride, + 0, 0, w, h, 0); + +#ifndef FAST_OSD + gl->GenTextures(1, &p->osdatex[p->osdtexCnt]); + gl->BindTexture(p->target, p->osdatex[p->osdtexCnt]); + glCreateClearTex(gl, p->target, GL_ALPHA, GL_ALPHA, GL_UNSIGNED_BYTE, + scale_type, sx, sy, 0); + { + int i; + char *tmp = malloc(stride * h); + // convert alpha from weird MPlayer scale. + // in-place is not possible since it is reused for future OSDs + for (i = h * stride - 1; i >= 0; i--) + tmp[i] = -srca[i]; + glUploadTex(gl, p->target, GL_ALPHA, GL_UNSIGNED_BYTE, tmp, stride, + 0, 0, w, h, 0); + free(tmp); + } +#endif + + gl->BindTexture(p->target, 0); + + // Create a list for rendering this OSD part +#ifndef FAST_OSD + p->osdaDispList[p->osdtexCnt] = gl->GenLists(1); + gl->NewList(p->osdaDispList[p->osdtexCnt], GL_COMPILE); + // render alpha + gl->BindTexture(p->target, p->osdatex[p->osdtexCnt]); + glDrawTex(gl, x0, y0, w, h, 0, 0, w, h, sx, sy, p->use_rectangle == 1, 0, 0); + gl->EndList(); +#endif + p->osdDispList[p->osdtexCnt] = gl->GenLists(1); + gl->NewList(p->osdDispList[p->osdtexCnt], GL_COMPILE); + // render OSD + gl->BindTexture(p->target, p->osdtex[p->osdtexCnt]); + glDrawTex(gl, x0, y0, w, h, 0, 0, w, h, sx, sy, p->use_rectangle == 1, 0, 0); + gl->EndList(); + + p->osdtexCnt++; +} + +#define RENDER_OSD 1 +#define RENDER_EOSD 2 + +/** + * \param type bit 0: render OSD, bit 1: render EOSD + */ +static void do_render_osd(struct vo *vo, int type) +{ + struct gl_priv *p = vo->priv; + GL *gl = p->gl; + + int draw_osd = (type & RENDER_OSD) && p->osdtexCnt > 0; + int draw_eosd = (type & RENDER_EOSD) && p->eosdDispList; + if (!draw_osd && !draw_eosd) + return; + // set special rendering parameters + if (!p->scaled_osd) { + gl->MatrixMode(GL_PROJECTION); + gl->PushMatrix(); + gl->LoadIdentity(); + gl->Ortho(0, vo->dwidth, vo->dheight, 0, -1, 1); + } + gl->Enable(GL_BLEND); + if (draw_eosd) { + gl->BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl->CallList(p->eosdDispList); + } + if (draw_osd) { + gl->Color4ub((p->osd_color >> 16) & 0xff, (p->osd_color >> 8) & 0xff, + p->osd_color & 0xff, 0xff - (p->osd_color >> 24)); + // draw OSD +#ifndef FAST_OSD + gl->BlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_ALPHA); + gl->CallLists(p->osdtexCnt, GL_UNSIGNED_INT, p->osdaDispList); +#endif + gl->BlendFunc(GL_SRC_ALPHA, GL_ONE); + gl->CallLists(p->osdtexCnt, GL_UNSIGNED_INT, p->osdDispList); + } + // set rendering parameters back to defaults + gl->Disable(GL_BLEND); + if (!p->scaled_osd) + gl->PopMatrix(); + gl->BindTexture(p->target, 0); +} + +static void draw_osd(struct vo *vo, struct osd_state *osd) +{ + struct gl_priv *p = vo->priv; + + if (!p->use_osd) + return; + if (vo_osd_changed(0)) { + int osd_h, osd_w; + clearOSD(vo); + osd_w = p->scaled_osd ? p->image_width : vo->dwidth; + osd_h = p->scaled_osd ? p->image_height : vo->dheight; + osd_draw_text_ext(osd, osd_w, osd_h, p->ass_border_x, + p->ass_border_y, p->ass_border_x, + p->ass_border_y, p->image_width, + p->image_height, create_osd_texture, vo); + } + if (vo_doublebuffering) + do_render_osd(vo, RENDER_OSD); +} + +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->Color3f(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 (vo_doublebuffering) { + if (p->use_glFinish) + gl->Finish(); + p->glctx->swapGlBuffers(p->glctx); + if (aspect_scaling() && p->use_aspect) + gl->Clear(GL_COLOR_BUFFER_BIT); + } else { + do_render(vo); + do_render_osd(vo, RENDER_OSD | RENDER_EOSD); + if (p->use_glFinish) + gl->Finish(); + else + gl->Flush(); + } +} + +static void redraw(struct vo *vo) +{ + if (vo_doublebuffering) { + do_render(vo); + do_render_osd(vo, RENDER_OSD | RENDER_EOSD); + } + flip_page(vo); +} + +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->mesa_buffer) + mpi->width = p->texture_width; + else 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->mesa_buffer) { +#ifdef CONFIG_GL_X11 + if (p->mesa_bufferptr && needed_size > p->mesa_buffersize) { + gl->FreeMemoryMESA(vo->x11->display, vo->x11->screen, + p->mesa_bufferptr); + p->mesa_bufferptr = NULL; + } + if (!p->mesa_bufferptr) + p->mesa_bufferptr = gl->AllocateMemoryMESA(vo->x11->display, + vo->x11->screen, + needed_size, 0, 1.0, + 1.0); + p->mesa_buffersize = needed_size; +#endif + mpi->planes[0] = p->mesa_bufferptr; + } else { + 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 && !p->mesa_buffer) { + mpi->flags &= ~MP_IMGFLAG_COMMON_PLANE; + if (!p->buffer_uv[0]) + gl->GenBuffers(2, p->buffer_uv); + if (mpi->stride[1] * mpi->height > p->buffersize_uv) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[0]); + gl->BufferData(GL_PIXEL_UNPACK_BUFFER, + mpi->stride[1] * mpi->height, + NULL, GL_DYNAMIC_DRAW); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, p->buffer_uv[1]); + gl->BufferData(GL_PIXEL_UNPACK_BUFFER, + mpi->stride[1] * mpi->height, + NULL, GL_DYNAMIC_DRAW); + p->buffersize_uv = mpi->stride[1] * mpi->height; + } + 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) { + 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 bpp = mpi->bpp; + int xs, ys, component_bits; + mp_get_chroma_shift(p->image_format, &xs, &ys, &component_bits); + if (p->is_yuv) + bpp = component_bits + 7; + memcpy_pic(mpi2.planes[0], mpi->planes[0], mpi->w * bpp / 8, mpi->h, + mpi2.stride[0], mpi->stride[0]); + if (p->is_yuv) { + int bp = (component_bits + 7) / 8; + memcpy_pic(mpi2.planes[1], mpi->planes[1], (mpi->w >> xs) * bp, + mpi->h >> ys, mpi2.stride[1], mpi->stride[1]); + memcpy_pic(mpi2.planes[2], mpi->planes[2], (mpi->w >> xs) * bp, + 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 * bpp / 8, mpi2.stride[0], + mpi->h, mpi2.height, 0); + if (p->is_yuv) { + clear_border(vo, mpi2.planes[1], mpi->w >> xs, mpi2.stride[1], + mpi->h >> ys, mpi2.height >> ys, 128); + clear_border(vo, mpi2.planes[2], mpi->w >> xs, mpi2.stride[2], + mpi->h >> ys, mpi2.height >> ys, 128); + } + } + 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) { + if (p->mesa_buffer) { + gl->PixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, 1); + w = p->texture_width; + } else { + 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], mpi->x, mpi->y, 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], mpi->x >> xs, mpi->y >> ys, 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], mpi->x >> xs, mpi->y >> ys, w >> xs, h >> ys, + slice); + gl->ActiveTexture(GL_TEXTURE0); + } + if (mpi->flags & MP_IMGFLAG_DIRECT) { + if (p->mesa_buffer) + gl->PixelStorei(GL_UNPACK_CLIENT_STORAGE_APPLE, 0); + else + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } +skip_upload: + if (vo_doublebuffering) + do_render(vo); + return VO_TRUE; +} + +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 | VFCAP_EOSD | (p->scaled_osd ? 0 : VFCAP_EOSD_UNSCALED); + 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, NULL, NULL, NULL, NULL)) + return caps; + return 0; +} + +static void uninit(struct vo *vo) +{ + struct gl_priv *p = vo->priv; + + if (p->glctx) + uninitGl(vo); + free(p->custom_prog); + p->custom_prog = NULL; + free(p->custom_tex); + p->custom_tex = NULL; + uninit_mpglcontext(p->glctx); + p->glctx = NULL; + p->gl = NULL; +} + +static int preinit_internal(struct vo *vo, const char *arg, int allow_sw) +{ + struct gl_priv *p = talloc_zero(vo, struct gl_priv); + vo->priv = p; + + enum MPGLType gltype = GLTYPE_AUTO; + + *p = (struct gl_priv) { + .many_fmts = 1, + .use_osd = -1, + .use_aspect = 1, + .use_yuv = -1, + .colorspace = MP_CSP_DETAILS_DEFAULTS, + .filter_strength = 0.5, + .use_rectangle = -1, + .ati_hack = -1, + .force_pbo = -1, + .swap_interval = 1, + .custom_prog = NULL, + .custom_tex = NULL, + .custom_tlin = 1, + .osd_color = 0xffffff, + }; + + //essentially unused; for legacy warnings only + int user_colorspace = 0; + int levelconv = -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}, + {"aspect", OPT_ARG_BOOL, &p->use_aspect, 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}, + {"colorspace", OPT_ARG_INT, &user_colorspace, NULL}, + {"levelconv", OPT_ARG_INT, &levelconv, NULL}, + {"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}, + {"ati-hack", OPT_ARG_BOOL, &p->ati_hack, NULL}, + {"force-pbo", OPT_ARG_BOOL, &p->force_pbo, NULL}, + {"mesa-buffer", OPT_ARG_BOOL, &p->mesa_buffer, 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_ARG_BOOL, &p->custom_tlin, NULL}, + {"customtrect", OPT_ARG_BOOL, &p->custom_trect, NULL}, + {"mipmapgen", OPT_ARG_BOOL, &p->mipmap_gen, NULL}, + {"osdcolor", OPT_ARG_INT, &p->osd_color, NULL}, + {"stereo", OPT_ARG_INT, &p->stereo_mode, NULL}, + {NULL} + }; + + if (subopt_parse(arg, subopts) != 0) { + mp_msg(MSGT_VO, MSGL_FATAL, + "\n-vo gl command line help:\n" + "Example: mplayer -vo gl:slice-height=4\n" + "\nOptions:\n" + " nomanyfmts\n" + " Disable extended color formats for OpenGL 1.2 and later\n" + " slice-height=<0-...>\n" + " Slice size for texture transfer, 0 for whole image\n" + " noosd\n" + " Do not use OpenGL OSD code\n" + " scaled-osd\n" + " Render OSD at movie resolution and scale it\n" + " noaspect\n" + " Do not do aspect scaling\n" + " rectangle=<0,1,2>\n" + " 0: use power-of-two textures\n" + " 1: use texture_rectangle\n" + " 2: use texture_non_power_of_two\n" + " ati-hack\n" + " Workaround ATI bug with PBOs\n" + " force-pbo\n" + " Force use of PBO even if this involves an extra memcpy\n" + " glfinish\n" + " Call glFinish() before swapping buffers\n" + " swapinterval=\n" + " Interval in displayed frames between to buffer swaps.\n" + " 1 is equivalent to enable VSYNC, 0 to disable VSYNC.\n" + " Requires GLX_SGI_swap_control support to work.\n" + " ycbcr\n" + " also try to use the GL_MESA_ycbcr_texture extension\n" + " yuv=\n" + " 0: use software YUV to RGB conversion.\n" + " 1: use register combiners (nVidia only, for older cards).\n" + " 2: use fragment program.\n" + " 3: use fragment program with gamma correction.\n" + " 4: use fragment program with gamma correction via lookup.\n" + " 5: use ATI-specific method (for older cards).\n" + " 6: use lookup via 3D texture.\n" + " lscale=\n" + " 0: use standard bilinear scaling for luma.\n" + " 1: use improved bicubic scaling for luma.\n" + " 2: use cubic in X, linear in Y direction scaling for luma.\n" + " 3: as 1 but without using a lookup texture.\n" + " 4: experimental unsharp masking (sharpening).\n" + " 5: experimental unsharp masking (sharpening) with larger radius.\n" + " cscale=\n" + " as lscale but for chroma (2x slower with little visible effect).\n" + " filter-strength=\n" + " set the effect strength for some lscale/cscale filters\n" + " customprog=\n" + " use a custom YUV conversion program\n" + " customtex=\n" + " use a custom YUV conversion lookup texture\n" + " nocustomtlin\n" + " use GL_NEAREST scaling for customtex texture\n" + " customtrect\n" + " use texture_rectangle for customtex texture\n" + " mipmapgen\n" + " generate mipmaps for the video image (use with TXB in customprog)\n" + " osdcolor=<0xAARRGGBB>\n" + " use the given color for the OSD\n" + " stereo=\n" + " 0: normal display\n" + " 1: side-by-side to red-cyan stereo\n" + " 2: side-by-side to green-magenta stereo\n" + " 3: side-by-side to quadbuffer stereo\n" + "\n"); + return -1; + } + if (user_colorspace != 0 || levelconv != -1) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] \"colorspace\" and \"levelconv\" " + "suboptions have been removed. Use options --colormatrix and" + " --colormatrix-input-range/--colormatrix-output-range instead.\n"); + return -1; + } + p->glctx = init_mpglcontext(gltype, vo); + if (!p->glctx) + goto err_out; + p->gl = p->glctx->gl; + if (p->use_yuv == -1 || !allow_sw) { + if (create_window(vo, 320, 200, VOFLAG_HIDDEN, NULL) < 0) + goto err_out; + if (p->glctx->setGlWindow(p->glctx) == SET_WINDOW_FAILED) + goto err_out; + if (!allow_sw && isSoftwareGl(vo)) + goto err_out; + autodetectGlExtensions(vo); + // We created a window to test whether the GL context supports hardware + // acceleration and so on. Destroy that window to make sure all state + // associated with it is lost. + uninit(vo); + p->glctx = init_mpglcontext(gltype, vo); + if (!p->glctx) + goto err_out; + p->gl = p->glctx->gl; + } + if (p->many_fmts) + mp_msg(MSGT_VO, MSGL_INFO, "[gl] using extended formats. " + "Use -vo gl:nomanyfmts if playback fails.\n"); + mp_msg(MSGT_VO, MSGL_V, "[gl] Using %d as slice height " + "(0 means image height).\n", p->slice_height); + + return 0; + +err_out: + uninit(vo); + return -1; +} + +static int preinit(struct vo *vo, const char *arg) +{ + return preinit_internal(vo, arg, 1); +} + +static int preinit_nosw(struct vo *vo, const char *arg) +{ + return preinit_internal(vo, arg, 0); +} + +static int control(struct vo *vo, uint32_t request, void *data) +{ + struct gl_priv *p = vo->priv; + + switch (request) { + case VOCTRL_PAUSE: + case VOCTRL_RESUME: + p->int_pause = (request == VOCTRL_PAUSE); + return VO_TRUE; + case VOCTRL_QUERY_FORMAT: + return query_format(vo, *(uint32_t *)data); + case VOCTRL_GET_IMAGE: + return get_image(vo, data); + case VOCTRL_DRAW_IMAGE: + return draw_image(vo, data); + case VOCTRL_DRAW_EOSD: + if (!data) + return VO_FALSE; + genEOSD(vo, data); + if (vo_doublebuffering) + do_render_osd(vo, RENDER_EOSD); + return VO_TRUE; + case VOCTRL_GET_EOSD_RES: { + mp_eosd_res_t *r = data; + r->w = vo->dwidth; + r->h = vo->dheight; + r->mt = r->mb = r->ml = r->mr = 0; + if (p->scaled_osd) { + r->w = p->image_width; + r->h = p->image_height; + } else if (aspect_scaling()) { + r->ml = r->mr = p->ass_border_x; + r->mt = r->mb = p->ass_border_y; + } + return VO_TRUE; + } + case VOCTRL_ONTOP: + p->glctx->ontop(vo); + return VO_TRUE; + case VOCTRL_FULLSCREEN: + p->glctx->fullscreen(vo); + resize(vo, vo->dwidth, vo->dheight); + return VO_TRUE; + case VOCTRL_BORDER: + p->glctx->border(vo); + resize(vo, vo->dwidth, vo->dheight); + return VO_TRUE; + case VOCTRL_GET_PANSCAN: + if (!p->use_aspect) + return VO_NOTIMPL; + return VO_TRUE; + case VOCTRL_SET_PANSCAN: + if (!p->use_aspect) + return VO_NOTIMPL; + resize(vo, vo->dwidth, vo->dheight); + return VO_TRUE; + case VOCTRL_GET_EQUALIZER: + if (p->is_yuv) { + struct voctrl_get_equalizer_args *args = data; + return mp_csp_equalizer_get(&p->video_eq, args->name, args->valueptr) + >= 0 ? VO_TRUE : VO_NOTIMPL; + } + break; + case VOCTRL_SET_EQUALIZER: + if (p->is_yuv) { + struct voctrl_set_equalizer_args *args = data; + if (mp_csp_equalizer_set(&p->video_eq, args->name, args->value) < 0) + return VO_NOTIMPL; + update_yuvconv(vo); + return VO_TRUE; + } + break; + case VOCTRL_SET_YUV_COLORSPACE: { + bool supports_csp = (1 << p->use_yuv) & MASK_NOT_COMBINERS; + if (vo->config_count && supports_csp) { + p->colorspace = *(struct mp_csp_details *)data; + update_yuvconv(vo); + } + return VO_TRUE; + } + case VOCTRL_GET_YUV_COLORSPACE: + *(struct mp_csp_details *)data = p->colorspace; + return VO_TRUE; + case VOCTRL_UPDATE_SCREENINFO: + p->glctx->update_xinerama_info(vo); + return VO_TRUE; + case VOCTRL_REDRAW_OSD: + if (vo_doublebuffering) + do_render(vo); + draw_osd(vo, data); + if (vo_doublebuffering) + do_render_osd(vo, 2); + flip_page(vo); + return VO_TRUE; + } + return VO_NOTIMPL; +} + +const struct vo_driver video_out_gl = { + .is_new = true, + .info = &(const vo_info_t) { + "OpenGL", + "gl", + "Reimar Doeffinger ", + "" + }, + .preinit = preinit, + .config = config, + .control = control, + .draw_slice = draw_slice, + .draw_osd = draw_osd, + .flip_page = flip_page, + .check_events = check_events, + .uninit = uninit, +}; + +const struct vo_driver video_out_gl_nosw = +{ + .is_new = true, + .info = &(const vo_info_t) { + "OpenGL no software rendering", + "gl_nosw", + "Reimar Doeffinger ", + "" + }, + .preinit = preinit_nosw, + .config = config, + .control = control, + .draw_slice = draw_slice, + .draw_osd = draw_osd, + .flip_page = flip_page, + .check_events = check_events, + .uninit = uninit, +}; -- 2.11.4.GIT