diff options
Diffstat (limited to 'video/out/gl_common.c')
-rw-r--r-- | video/out/gl_common.c | 2654 |
1 files changed, 2654 insertions, 0 deletions
diff --git a/video/out/gl_common.c b/video/out/gl_common.c new file mode 100644 index 0000000000..80db2eacc4 --- /dev/null +++ b/video/out/gl_common.c @@ -0,0 +1,2654 @@ +/* + * common OpenGL routines + * + * copyleft (C) 2005-2010 Reimar Döffinger <Reimar.Doeffinger@gmx.de> + * 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 <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <stdbool.h> +#include <math.h> +#include <assert.h> +#include "talloc.h" +#include "gl_common.h" +#include "csputils.h" +#include "aspect.h" +#include "pnm_loader.h" +#include "options.h" +#include "sub/sub.h" +#include "bitmap_packer.h" + +//! \defgroup glgeneral OpenGL general helper functions + +// GLU has this as gluErrorString (we don't use GLU, as it is legacy-OpenGL) +static const char *gl_error_to_string(GLenum error) +{ + switch (error) { + case GL_INVALID_ENUM: return "INVALID_ENUM"; + case GL_INVALID_VALUE: return "INVALID_VALUE"; + case GL_INVALID_OPERATION: return "INVALID_OPERATION"; + case GL_INVALID_FRAMEBUFFER_OPERATION: + return "INVALID_FRAMEBUFFER_OPERATION"; + case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY"; + default: return "unknown"; + } +} + +void glCheckError(GL *gl, const char *info) +{ + for (;;) { + GLenum error = gl->GetError(); + if (error == GL_NO_ERROR) + break; + mp_msg(MSGT_VO, MSGL_ERR, "[gl] %s: OpenGL error %s.\n", info, + gl_error_to_string(error)); + } +} + +//! \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); + gl->PixelStorei(GL_PACK_ALIGNMENT, gl_alignment); +} + +//! 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 have_texture_rg, 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 = have_texture_rg ? GL_R16 : GL_LUMINANCE16; + *bpp = 16; + *gl_format = have_texture_rg ? GL_RED : 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 different 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; +} + +struct feature { + int id; + const char *name; +}; + +static const struct feature features[] = { + {MPGL_CAP_GL, "Basic OpenGL"}, + {MPGL_CAP_GL_LEGACY, "Legacy OpenGL"}, + {MPGL_CAP_GL2, "OpenGL 2.0"}, + {MPGL_CAP_GL21, "OpenGL 2.1"}, + {MPGL_CAP_GL3, "OpenGL 3.0"}, + {MPGL_CAP_FB, "Framebuffers"}, + {MPGL_CAP_VAO, "VAOs"}, + {MPGL_CAP_SRGB_TEX, "sRGB textures"}, + {MPGL_CAP_SRGB_FB, "sRGB framebuffers"}, + {MPGL_CAP_FLOAT_TEX, "Float textures"}, + {MPGL_CAP_TEX_RG, "RG textures"}, + {MPGL_CAP_NO_SW, "NO_SW"}, + {0}, +}; + +static void list_features(int set, int msgl, bool invert) +{ + for (const struct feature *f = &features[0]; f->id; f++) { + if (invert == !(f->id & set)) + mp_msg(MSGT_VO, msgl, " [%s]", f->name); + } + mp_msg(MSGT_VO, msgl, "\n"); +} + +// This guesses if the current GL context is a suspected software renderer. +static bool is_software_gl(GL *gl) +{ + const char *renderer = gl->GetString(GL_RENDERER); + const char *vendor = gl->GetString(GL_VENDOR); + return !(renderer && vendor) || + strcmp(renderer, "Software Rasterizer") == 0 || + strstr(renderer, "llvmpipe") || + strcmp(vendor, "Microsoft Corporation") == 0 || + strcmp(renderer, "Mesa X11") == 0; +} + +#ifdef HAVE_LIBDL +#include <dlfcn.h> +#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; +} + +#define FN_OFFS(name) offsetof(GL, name) + +// Define the function with a "hard" reference to the function as fallback. +// (This requires linking with a compatible OpenGL library.) +#define DEF_FN_HARD(name) {FN_OFFS(name), {"gl" # name}, gl ## name} + +#define DEF_FN(name) {FN_OFFS(name), {"gl" # name}} +#define DEF_FN_NAMES(name, ...) {FN_OFFS(name), {__VA_ARGS__}} + +struct gl_function { + ptrdiff_t offset; + char *funcnames[7]; + void *fallback; +}; + +struct gl_functions { + const char *extension; // introduced with this extension in any version + int provides; // bitfield of MPGL_CAP_* constants + int ver_core; // introduced as required function + int ver_removed; // removed as required function (no replacement) + bool partial_ok; // loading only some functions is ok + struct gl_function *functions; +}; + +#define MAX_FN_COUNT 50 // max functions per gl_functions section + +struct gl_functions gl_functions[] = { + // GL functions which are always available anywhere at least since 1.1 + { + .ver_core = MPGL_VER(1, 1), + .provides = MPGL_CAP_GL, + .functions = (struct gl_function[]) { + DEF_FN_HARD(Viewport), + DEF_FN_HARD(Clear), + DEF_FN_HARD(GenTextures), + DEF_FN_HARD(DeleteTextures), + DEF_FN_HARD(TexEnvi), + DEF_FN_HARD(ClearColor), + DEF_FN_HARD(Enable), + DEF_FN_HARD(Disable), + DEF_FN_HARD(DrawBuffer), + DEF_FN_HARD(DepthMask), + DEF_FN_HARD(BlendFunc), + DEF_FN_HARD(Flush), + DEF_FN_HARD(Finish), + DEF_FN_HARD(PixelStorei), + DEF_FN_HARD(TexImage1D), + DEF_FN_HARD(TexImage2D), + DEF_FN_HARD(TexSubImage2D), + DEF_FN_HARD(GetTexImage), + DEF_FN_HARD(TexParameteri), + DEF_FN_HARD(TexParameterf), + DEF_FN_HARD(TexParameterfv), + DEF_FN_HARD(GetIntegerv), + DEF_FN_HARD(GetBooleanv), + DEF_FN_HARD(ColorMask), + DEF_FN_HARD(ReadPixels), + DEF_FN_HARD(ReadBuffer), + DEF_FN_HARD(DrawArrays), + DEF_FN_HARD(GetString), + DEF_FN_HARD(GetError), + {0} + }, + }, + // GL 2.0-3.x functions + { + .ver_core = MPGL_VER(2, 0), + .provides = MPGL_CAP_GL2, + .functions = (struct gl_function[]) { + DEF_FN(GenBuffers), + DEF_FN(DeleteBuffers), + DEF_FN(BindBuffer), + DEF_FN(MapBuffer), + DEF_FN(UnmapBuffer), + DEF_FN(BufferData), + DEF_FN(ActiveTexture), + DEF_FN(BindTexture), + DEF_FN(GetAttribLocation), + DEF_FN(EnableVertexAttribArray), + DEF_FN(DisableVertexAttribArray), + DEF_FN(VertexAttribPointer), + DEF_FN(UseProgram), + DEF_FN(GetUniformLocation), + DEF_FN(CompileShader), + DEF_FN(CreateProgram), + DEF_FN(CreateShader), + DEF_FN(ShaderSource), + DEF_FN(LinkProgram), + DEF_FN(AttachShader), + DEF_FN(DeleteShader), + DEF_FN(DeleteProgram), + DEF_FN(GetShaderInfoLog), + DEF_FN(GetShaderiv), + DEF_FN(GetProgramInfoLog), + DEF_FN(GetProgramiv), + DEF_FN(BindAttribLocation), + DEF_FN(Uniform1f), + DEF_FN(Uniform2f), + DEF_FN(Uniform3f), + DEF_FN(Uniform1i), + DEF_FN(UniformMatrix3fv), + DEF_FN(TexImage3D), + {0}, + }, + }, + // GL 2.1-3.x functions (also: GLSL 120 shaders) + { + .ver_core = MPGL_VER(2, 1), + .provides = MPGL_CAP_GL21, + .functions = (struct gl_function[]) { + DEF_FN(UniformMatrix4x3fv), + {0} + }, + }, + // GL 3.x core only functions. + { + .ver_core = MPGL_VER(3, 0), + .provides = MPGL_CAP_GL3 | MPGL_CAP_SRGB_TEX | MPGL_CAP_SRGB_FB, + .functions = (struct gl_function[]) { + DEF_FN(GetStringi), + {0} + }, + }, + // Framebuffers, extension in GL 2.x, core in GL 3.x core. + { + .ver_core = MPGL_VER(3, 0), + .extension = "GL_ARB_framebuffer_object", + .provides = MPGL_CAP_FB, + .functions = (struct gl_function[]) { + DEF_FN(BindFramebuffer), + DEF_FN(GenFramebuffers), + DEF_FN(DeleteFramebuffers), + DEF_FN(CheckFramebufferStatus), + DEF_FN(FramebufferTexture2D), + {0} + }, + }, + // Framebuffers, alternative extension name. + { + .ver_removed = MPGL_VER(3, 0), // don't touch these fn names in 3.x + .extension = "GL_EXT_framebuffer_object", + .provides = MPGL_CAP_FB, + .functions = (struct gl_function[]) { + DEF_FN_NAMES(BindFramebuffer, "glBindFramebufferEXT"), + DEF_FN_NAMES(GenFramebuffers, "glGenFramebuffersEXT"), + DEF_FN_NAMES(DeleteFramebuffers, "glDeleteFramebuffersEXT"), + DEF_FN_NAMES(CheckFramebufferStatus, "glCheckFramebufferStatusEXT"), + DEF_FN_NAMES(FramebufferTexture2D, "glFramebufferTexture2DEXT"), + {0} + }, + }, + // VAOs, extension in GL 2.x, core in GL 3.x core. + { + .ver_core = MPGL_VER(3, 0), + .extension = "GL_ARB_vertex_array_object", + .provides = MPGL_CAP_VAO, + .functions = (struct gl_function[]) { + DEF_FN(GenVertexArrays), + DEF_FN(BindVertexArray), + DEF_FN(DeleteVertexArrays), + {0} + } + }, + // sRGB textures, extension in GL 2.x, core in GL 3.x core. + { + .ver_core = MPGL_VER(3, 0), + .extension = "GL_EXT_texture_sRGB", + .provides = MPGL_CAP_SRGB_TEX, + .functions = (struct gl_function[]) {{0}}, + }, + // sRGB framebuffers, extension in GL 2.x, core in GL 3.x core. + { + .ver_core = MPGL_VER(3, 0), + .extension = "GL_EXT_framebuffer_sRGB", + .provides = MPGL_CAP_SRGB_FB, + .functions = (struct gl_function[]) {{0}}, + }, + // Float textures, extension in GL 2.x, core in GL 3.x core. + { + .ver_core = MPGL_VER(3, 0), + .extension = "GL_ARB_texture_float", + .provides = MPGL_CAP_FLOAT_TEX, + .functions = (struct gl_function[]) {{0}}, + }, + // GL_RED / GL_RG textures, extension in GL 2.x, core in GL 3.x core. + { + .ver_core = MPGL_VER(3, 0), + .extension = "GL_ARB_texture_rg", + .provides = MPGL_CAP_TEX_RG, + .functions = (struct gl_function[]) {{0}}, + }, + // Swap control, always an OS specific extension + { + .extension = "_swap_control", + .functions = (struct gl_function[]) { + DEF_FN_NAMES(SwapInterval, "glXSwapIntervalSGI", "glXSwapInterval", + "wglSwapIntervalSGI", "wglSwapInterval", + "wglSwapIntervalEXT"), + {0} + }, + }, + // GL legacy functions in GL 1.x - 2.x, removed from GL 3.x + { + .ver_core = MPGL_VER(1, 1), + .ver_removed = MPGL_VER(3, 0), + .provides = MPGL_CAP_GL_LEGACY, + .functions = (struct gl_function[]) { + DEF_FN_HARD(Begin), + DEF_FN_HARD(End), + DEF_FN_HARD(MatrixMode), + DEF_FN_HARD(LoadIdentity), + DEF_FN_HARD(Translated), + DEF_FN_HARD(Scaled), + DEF_FN_HARD(Ortho), + DEF_FN_HARD(PushMatrix), + DEF_FN_HARD(PopMatrix), + DEF_FN_HARD(GenLists), + DEF_FN_HARD(DeleteLists), + DEF_FN_HARD(NewList), + DEF_FN_HARD(EndList), + DEF_FN_HARD(CallList), + DEF_FN_HARD(CallLists), + DEF_FN_HARD(Color4ub), + DEF_FN_HARD(Color4f), + DEF_FN_HARD(TexCoord2f), + DEF_FN_HARD(TexCoord2fv), + DEF_FN_HARD(Vertex2f), + DEF_FN_HARD(VertexPointer), + DEF_FN_HARD(ColorPointer), + DEF_FN_HARD(TexCoordPointer), + DEF_FN_HARD(EnableClientState), + DEF_FN_HARD(DisableClientState), + {0} + }, + }, + // Loading of old extensions, which are later added to GL 2.0. + // NOTE: actually we should be checking the extension strings: the OpenGL + // library could provide an entry point, but not implement it. + // But the previous code didn't do that, and nobody ever complained. + { + .ver_removed = MPGL_VER(2, 1), + .partial_ok = true, + .functions = (struct gl_function[]) { + DEF_FN_NAMES(GenBuffers, "glGenBuffers", "glGenBuffersARB"), + DEF_FN_NAMES(DeleteBuffers, "glDeleteBuffers", "glDeleteBuffersARB"), + DEF_FN_NAMES(BindBuffer, "glBindBuffer", "glBindBufferARB"), + DEF_FN_NAMES(MapBuffer, "glMapBuffer", "glMapBufferARB"), + DEF_FN_NAMES(UnmapBuffer, "glUnmapBuffer", "glUnmapBufferARB"), + DEF_FN_NAMES(BufferData, "glBufferData", "glBufferDataARB"), + DEF_FN_NAMES(ActiveTexture, "glActiveTexture", "glActiveTextureARB"), + DEF_FN_NAMES(BindTexture, "glBindTexture", "glBindTextureARB", "glBindTextureEXT"), + DEF_FN_NAMES(MultiTexCoord2f, "glMultiTexCoord2f", "glMultiTexCoord2fARB"), + DEF_FN_NAMES(TexImage3D, "glTexImage3D"), + {0} + }, + }, + // Ancient ARB shaders. + { + .extension = "_program", + .ver_removed = MPGL_VER(3, 0), + .functions = (struct gl_function[]) { + DEF_FN_NAMES(GenPrograms, "glGenProgramsARB"), + DEF_FN_NAMES(DeletePrograms, "glDeleteProgramsARB"), + DEF_FN_NAMES(BindProgram, "glBindProgramARB"), + DEF_FN_NAMES(ProgramString, "glProgramStringARB"), + DEF_FN_NAMES(GetProgramivARB, "glGetProgramivARB"), + DEF_FN_NAMES(ProgramEnvParameter4f, "glProgramEnvParameter4fARB"), + {0} + }, + }, + // Ancient ATI extensions. + { + .extension = "ATI_fragment_shader", + .ver_removed = MPGL_VER(3, 0), + .functions = (struct gl_function[]) { + DEF_FN_NAMES(BeginFragmentShader, "glBeginFragmentShaderATI"), + DEF_FN_NAMES(EndFragmentShader, "glEndFragmentShaderATI"), + DEF_FN_NAMES(SampleMap, "glSampleMapATI"), + DEF_FN_NAMES(ColorFragmentOp2, "glColorFragmentOp2ATI"), + DEF_FN_NAMES(ColorFragmentOp3, "glColorFragmentOp3ATI"), + DEF_FN_NAMES(SetFragmentShaderConstant, "glSetFragmentShaderConstantATI"), + {0} + }, + }, +}; + +#undef FN_OFFS +#undef DEF_FN_HARD +#undef DEF_FN +#undef DEF_FN_NAMES + + +/** + * \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, bool gl3) +{ + talloc_free_children(gl); + *gl = (GL) { + .extensions = talloc_strdup(gl, ext2 ? ext2 : ""), + }; + + if (!getProcAddress) + getProcAddress = (void *)getdladdr; + + GLint major = 0, minor = 0; + if (gl3) { + gl->GetStringi = getProcAddress("glGetStringi"); + gl->GetIntegerv = getProcAddress("glGetIntegerv"); + + if (!(gl->GetStringi && gl->GetIntegerv)) + return; + + gl->GetIntegerv(GL_MAJOR_VERSION, &major); + gl->GetIntegerv(GL_MINOR_VERSION, &minor); + + GLint exts; + gl->GetIntegerv(GL_NUM_EXTENSIONS, &exts); + for (int n = 0; n < exts; n++) { + gl->extensions + = talloc_asprintf_append(gl->extensions, " %s", + gl->GetStringi(GL_EXTENSIONS, n)); + } + } else { + gl->GetString = getProcAddress("glGetString"); + if (!gl->GetString) + gl->GetString = glGetString; + + const char *ext = (char*)gl->GetString(GL_EXTENSIONS); + gl->extensions = talloc_asprintf_append(gl->extensions, " %s", ext); + + const char *version = gl->GetString(GL_VERSION); + sscanf(version, "%d.%d", &major, &minor); + } + gl->version = MPGL_VER(major, minor); + + mp_msg(MSGT_VO, MSGL_V, "[gl] Detected OpenGL %d.%d.\n", major, minor); + mp_msg(MSGT_VO, MSGL_DBG2, "[gl] Combined OpenGL extensions string:\n%s\n", + gl->extensions); + + for (int n = 0; n < sizeof(gl_functions) / sizeof(gl_functions[0]); n++) { + struct gl_functions *section = &gl_functions[n]; + + // With gl3=false, we could have a legacy context, where functionality + // is never removed. (E.g. the context could be at version >= 3.0, but + // legacy functions like glBegin still exist and work.) + if (gl3 && section->ver_removed && gl->version >= section->ver_removed) + continue; + + bool must_exist = section->ver_core && gl->version >= section->ver_core + && !section->partial_ok; + + if (!must_exist && section->extension && + !strstr(gl->extensions, section->extension)) + continue; + + void *loaded[MAX_FN_COUNT] = {0}; + bool all_loaded = true; + + for (int i = 0; section->functions[i].funcnames[0]; i++) { + struct gl_function *fn = §ion->functions[i]; + void *ptr = NULL; + for (int x = 0; fn->funcnames[x]; x++) { + ptr = getProcAddress((const GLubyte *)fn->funcnames[x]); + if (ptr) + break; + } + if (!ptr) + ptr = fn->fallback; + if (!ptr) { + all_loaded = false; + if (must_exist) { + // Either we or the driver are not conforming to OpenGL. + mp_msg(MSGT_VO, MSGL_ERR, "[gl] Required function '%s' not " + "found.\n", fn->funcnames[0]); + talloc_free_children(gl); + *gl = (GL) {0}; + return; + } + } + assert(i < MAX_FN_COUNT); + loaded[i] = ptr; + } + + if (all_loaded || section->partial_ok) { + gl->mpgl_caps |= section->provides; + for (int i = 0; section->functions[i].funcnames[0]; i++) { + struct gl_function *fn = §ion->functions[i]; + void **funcptr = (void**)(((char*)gl) + fn->offset); + if (loaded[i]) + *funcptr = loaded[i]; + } + } + } + + gl->glsl_version = 0; + if (gl->version >= MPGL_VER(2, 0)) + gl->glsl_version = 110; + if (gl->version >= MPGL_VER(2, 1)) + gl->glsl_version = 120; + if (gl->version >= MPGL_VER(3, 0)) + gl->glsl_version = 130; + // Specifically needed for OSX (normally we request 3.0 contexts only, but + // OSX always creates 3.2 contexts when requesting a core context). + if (gl->version >= MPGL_VER(3, 2)) + gl->glsl_version = 150; + + if (!is_software_gl(gl)) + gl->mpgl_caps |= MPGL_CAP_NO_SW; + + mp_msg(MSGT_VO, MSGL_V, "[gl] Detected OpenGL features:"); + list_features(gl->mpgl_caps, MSGL_V, false); +} + +/** + * \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; + case GL_RED: + return component_size; + case GL_RG: + case GL_LUMINANCE_ALPHA: + return 2 * component_size; + } + abort(); // 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); +} + +// Like glUploadTex, but upload a byte array with all elements set to val. +// If scratch is not NULL, points to a resizeable talloc memory block than can +// be freely used by the function (for avoiding temporary memory allocations). +void glClearTex(GL *gl, GLenum target, GLenum format, GLenum type, + int x, int y, int w, int h, uint8_t val, void **scratch) +{ + int bpp = glFmt2bpp(format, type); + int stride = w * bpp; + int size = h * stride; + if (size < 1) + return; + void *data = scratch ? *scratch : NULL; + if (talloc_get_size(data) < size) + data = talloc_realloc(NULL, data, char *, size); + memset(data, val, size); + glAdjustAlignment(gl, stride); + gl->PixelStorei(GL_UNPACK_ROW_LENGTH, w); + gl->TexSubImage2D(target, 0, x, y, w, h, format, type, data); + if (scratch) { + *scratch = data; + } else { + talloc_free(data); + } +} + +/** + * \brief download 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 destination memory for download + * \param stride data stride (must be positive) + * \ingroup gltexture + */ +void glDownloadTex(GL *gl, GLenum target, GLenum format, GLenum type, + void *dataptr, int stride) +{ + // this is not always correct, but should work for MPlayer + glAdjustAlignment(gl, stride); + gl->PixelStorei(GL_PACK_ROW_LENGTH, stride / glFmt2bpp(format, type)); + gl->GetTexImage(target, 0, format, type, dataptr); +} + +/** + * \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->SetFragmentShad |