/*
* 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 "aspect.h"
#include "core/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);
}
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),
DEF_FN_HARD(GetTexLevelParameteriv),
{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(
|