diff --git a/DOCS/man/en/vo.rst b/DOCS/man/en/vo.rst index 0ba7fe8b9f..34b547c4bc 100644 --- a/DOCS/man/en/vo.rst +++ b/DOCS/man/en/vo.rst @@ -413,6 +413,9 @@ gl enabled). This option is for testing; to disable the OSD use ``--osdlevel=0`` instead. + sw + Continue even if a software renderer is detected. + backend= auto auto-select (default) @@ -424,9 +427,7 @@ gl X11/GLX gl3 - OpenGL video output driver, extended version. The requires an OpenGL 3 - capable graphics driver. (Note: this is only because of developer pedantry. - The dependency on actual OpenGL 3 features is rather low.) + OpenGL video output driver, extended version. It supports extended scaling methods, dithering and color management. It tries to use sane defaults for good quality output. @@ -434,6 +435,8 @@ gl3 Note that some cheaper LCDs do dithering that gravely interferes with vo_gl3's dithering. Disabling dithering with ``dither-depth=-1`` helps. + Some features are available with OpenGL 3 capable graphics drivers only. + lscale= Set the scaling filter. Possible choices: bilinear @@ -579,6 +582,9 @@ gl3 glfinish Call glFinish() before swapping buffers + sw + Continue even if a software renderer is detected. + backend= auto auto-select (default) @@ -600,16 +606,12 @@ gl3 fbo-format= Selects the internal format of any FBO textures used. - fmt can be one of: rgb, rgba, rgb8, rgb16, rgb16f, rgb32f + fmt can be one of: rgb, rgba, rgb8, rgb10, rgb16, rgb16f, rgb32f Default: rgb16. gamma Always enable gamma control. (Disables delayed enabling.) - force-gl2 - Create a legacy GL context. This will randomly malfunction - if the proper extensions are not supported. - icc-profile= Load an ICC profile and use it to transform linear RGB to screen output. Needs LittleCMS2 support compiled in. diff --git a/libvo/gl_common.c b/libvo/gl_common.c index 1ad7017e10..1593f07c50 100644 --- a/libvo/gl_common.c +++ b/libvo/gl_common.c @@ -39,6 +39,7 @@ #include #include #include +#include #include "talloc.h" #include "gl_common.h" #include "csputils.h" @@ -238,6 +239,48 @@ int glFindFormat(uint32_t fmt, int have_texture_rg, int *bpp, GLint *gl_texfmt, 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 #endif @@ -259,234 +302,421 @@ static void *getdladdr(const char *s) return ret; } -typedef struct { - ptrdiff_t offset; // offset to the function pointer in struct GL - const char *extstr; - const char *funcnames[7]; +#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; - bool is_gl3; -} extfunc_desc_t; - -#define DEF_FUNC_DESC(name) \ - {offsetof(GL, name), NULL, {"gl" # name}, gl ## name} -#define DEF_EXT_FUNCS(...) __VA_ARGS__ -#define DEF_EXT_DESC(name, ext, funcnames) \ - {offsetof(GL, name), ext, {DEF_EXT_FUNCS funcnames}} -// These are mostly handled the same, but needed because at least the MESA -// headers don't define any function prototypes for these. -#define DEF_GL3_DESC(name) \ - {offsetof(GL, name), NULL, {"gl" # name}, NULL, .is_gl3 = true} - -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(Viewport), - DEF_FUNC_DESC(Clear), - DEF_FUNC_DESC(GenTextures), - DEF_FUNC_DESC(DeleteTextures), - DEF_FUNC_DESC(TexEnvi), - DEF_FUNC_DESC(ClearColor), - 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(GetIntegerv), - DEF_FUNC_DESC(GetBooleanv), - DEF_FUNC_DESC(ColorMask), - DEF_FUNC_DESC(ReadPixels), - DEF_FUNC_DESC(ReadBuffer), - DEF_FUNC_DESC(DrawArrays), - DEF_FUNC_DESC(GetString), - DEF_FUNC_DESC(GetError), - - // legacy GL functions (1.x - 2.x) - DEF_FUNC_DESC(Begin), - DEF_FUNC_DESC(End), - DEF_FUNC_DESC(MatrixMode), - DEF_FUNC_DESC(LoadIdentity), - DEF_FUNC_DESC(Translated), - DEF_FUNC_DESC(Scaled), - DEF_FUNC_DESC(Ortho), - DEF_FUNC_DESC(PushMatrix), - DEF_FUNC_DESC(PopMatrix), - 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(Color4ub), - DEF_FUNC_DESC(Color4f), - DEF_FUNC_DESC(TexCoord2f), - DEF_FUNC_DESC(TexCoord2fv), - DEF_FUNC_DESC(Vertex2f), - DEF_FUNC_DESC(VertexPointer), - DEF_FUNC_DESC(ColorPointer), - DEF_FUNC_DESC(TexCoordPointer), - DEF_FUNC_DESC(EnableClientState), - DEF_FUNC_DESC(DisableClientState), - - // OpenGL extension functions - 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(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(GetProgramivARB, "_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")), - - // ancient ATI extensions - 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")), - - // GL 3, possibly in GL 2.x as well in form of extensions - DEF_GL3_DESC(GenBuffers), - DEF_GL3_DESC(DeleteBuffers), - DEF_GL3_DESC(BindBuffer), - DEF_GL3_DESC(MapBuffer), - DEF_GL3_DESC(UnmapBuffer), - DEF_GL3_DESC(BufferData), - DEF_GL3_DESC(ActiveTexture), - DEF_GL3_DESC(BindTexture), - DEF_GL3_DESC(GenVertexArrays), - DEF_GL3_DESC(BindVertexArray), - DEF_GL3_DESC(GetAttribLocation), - DEF_GL3_DESC(EnableVertexAttribArray), - DEF_GL3_DESC(DisableVertexAttribArray), - DEF_GL3_DESC(VertexAttribPointer), - DEF_GL3_DESC(DeleteVertexArrays), - DEF_GL3_DESC(UseProgram), - DEF_GL3_DESC(GetUniformLocation), - DEF_GL3_DESC(CompileShader), - DEF_GL3_DESC(CreateProgram), - DEF_GL3_DESC(CreateShader), - DEF_GL3_DESC(ShaderSource), - DEF_GL3_DESC(LinkProgram), - DEF_GL3_DESC(AttachShader), - DEF_GL3_DESC(DeleteShader), - DEF_GL3_DESC(DeleteProgram), - DEF_GL3_DESC(GetShaderInfoLog), - DEF_GL3_DESC(GetShaderiv), - DEF_GL3_DESC(GetProgramInfoLog), - DEF_GL3_DESC(GetProgramiv), - DEF_GL3_DESC(GetStringi), - DEF_GL3_DESC(BindAttribLocation), - DEF_GL3_DESC(BindFramebuffer), - DEF_GL3_DESC(GenFramebuffers), - DEF_GL3_DESC(DeleteFramebuffers), - DEF_GL3_DESC(CheckFramebufferStatus), - DEF_GL3_DESC(FramebufferTexture2D), - DEF_GL3_DESC(Uniform1f), - DEF_GL3_DESC(Uniform3f), - DEF_GL3_DESC(Uniform1i), - DEF_GL3_DESC(UniformMatrix3fv), - DEF_GL3_DESC(UniformMatrix4x3fv), - - {-1} }; +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 is_gl3) + const char *ext2, bool gl3) { - const extfunc_desc_t *dsc; - char *allexts = talloc_strdup(NULL, ext2 ? ext2 : ""); - - *gl = (GL) {0}; + talloc_free_children(gl); + *gl = (GL) { + .extensions = talloc_strdup(gl, ext2 ? ext2 : ""), + }; if (!getProcAddress) getProcAddress = (void *)getdladdr; - if (is_gl3) { + 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++) { - allexts = talloc_asprintf_append(allexts, " %s", - gl->GetStringi(GL_EXTENSIONS, 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); - allexts = talloc_asprintf_append(allexts, " %s", ext); + 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]; + } + } } - mp_msg(MSGT_VO, MSGL_DBG2, "OpenGL extensions string:\n%s\n", allexts); - for (dsc = extfuncs; dsc->offset >= 0; dsc++) { - void *ptr = NULL; - if (!dsc->extstr || strstr(allexts, dsc->extstr)) { - for (int i = 0; !ptr && dsc->funcnames[i]; i++) - ptr = getProcAddress((const GLubyte *)dsc->funcnames[i]); - } - if (!ptr) - ptr = dsc->fallback; - if (!ptr && !dsc->extstr && (!dsc->is_gl3 || is_gl3)) - mp_msg(MSGT_VO, MSGL_WARN, "[gl] OpenGL function not found: %s\n", - dsc->funcnames[0]); - void **funcptr = (void**)(((char*)gl) + dsc->offset); - *funcptr = ptr; - } - talloc_free(allexts); + 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); } /** @@ -1710,35 +1940,38 @@ void glDrawTex(GL *gl, GLfloat x, GLfloat y, GLfloat w, GLfloat h, #ifdef CONFIG_GL_COCOA #include "cocoa_common.h" -static int create_window_cocoa(struct MPGLContext *ctx, uint32_t d_width, - uint32_t d_height, uint32_t flags) + +static bool create_window_cocoa(struct MPGLContext *ctx, uint32_t d_width, + uint32_t d_height, uint32_t flags, bool gl3) { - if (vo_cocoa_create_window(ctx->vo, d_width, d_height, flags, 0) == 0) { - return SET_WINDOW_OK; - } else { - return SET_WINDOW_FAILED; + int rv = vo_cocoa_create_window(ctx->vo, d_width, d_height, flags, gl3); + if (rv != 0) + return false; + + getFunctions(ctx->gl, (void *)vo_cocoa_glgetaddr, NULL, gl3); + + if (gl3) { + ctx->depth_r = vo_cocoa_cgl_color_size(); + ctx->depth_g = vo_cocoa_cgl_color_size(); + ctx->depth_b = vo_cocoa_cgl_color_size(); } -} -static int create_window_cocoa_gl3(struct MPGLContext *ctx, int gl_flags, - int gl_version, uint32_t d_width, - uint32_t d_height, uint32_t flags) -{ - int rv = vo_cocoa_create_window(ctx->vo, d_width, d_height, flags, 1); - getFunctions(ctx->gl, (void *)vo_cocoa_glgetaddr, NULL, true); - ctx->depth_r = vo_cocoa_cgl_color_size(); - ctx->depth_g = vo_cocoa_cgl_color_size(); - ctx->depth_b = vo_cocoa_cgl_color_size(); - return rv; -} - -static int setGlWindow_cocoa(MPGLContext *ctx) -{ - vo_cocoa_change_attributes(ctx->vo); - getFunctions(ctx->gl, (void *)vo_cocoa_glgetaddr, NULL, false); if (!ctx->gl->SwapInterval) ctx->gl->SwapInterval = vo_cocoa_swap_interval; - return SET_WINDOW_OK; + + return true; +} + +static bool create_window_cocoa_old(struct MPGLContext *ctx, uint32_t d_width, + uint32_t d_height, uint32_t flags) +{ + return create_window_cocoa(ctx, d_width, d_height, flags, false); +} + +static bool create_window_cocoa_gl3(struct MPGLContext *ctx, uint32_t d_width, + uint32_t d_height, uint32_t flags) +{ + return create_window_cocoa(ctx, d_width, d_height, flags, true); } static void releaseGlContext_cocoa(MPGLContext *ctx) @@ -1771,24 +2004,9 @@ static void cocoa_fullscreen(struct vo *vo) #include "w32_common.h" struct w32_context { - int vinfo; HGLRC context; }; -static int create_window_w32(struct MPGLContext *ctx, uint32_t d_width, - uint32_t d_height, uint32_t flags) -{ - if (!vo_w32_config(ctx->vo, d_width, d_height, flags)) - return -1; - return 0; -} - -/** - * \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; @@ -1799,17 +2017,59 @@ static void *w32gpa(const GLubyte *procName) return GetProcAddress(oglmod, procName); } -static int create_window_w32_gl3(struct MPGLContext *ctx, int gl_flags, - int gl_version, uint32_t d_width, - uint32_t d_height, uint32_t flags) { +static bool create_window_w32_old(struct MPGLContext *ctx, uint32_t d_width, + uint32_t d_height, uint32_t flags) +{ + GL *gl = ctx->gl; + if (!vo_w32_config(ctx->vo, d_width, d_height, flags)) - return -1; + return false; + + struct w32_context *w32_ctx = ctx->priv; + HGLRC *context = &w32_ctx->context; + + if (*context) { + gl->Finish(); // supposedly to prevent flickering + return true; + } + + HWND win = ctx->vo->w32->window; + HDC windc = vo_w32_get_dc(ctx->vo, win); + bool res = false; + + HGLRC new_context = wglCreateContext(windc); + if (!new_context) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GL context!\n"); + goto out; + } + + if (!wglMakeCurrent(windc, new_context)) { + mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL context!\n"); + wglDeleteContext(new_context); + goto out; + } + + *context = new_context; + + getFunctions(ctx->gl, w32gpa, NULL, false); + res = true; + +out: + vo_w32_release_dc(ctx->vo, win, windc); + return res; +} + +static bool create_window_w32_gl3(struct MPGLContext *ctx, uint32_t d_width, + uint32_t d_height, uint32_t flags) +{ + if (!vo_w32_config(ctx->vo, d_width, d_height, flags)) + return false; struct w32_context *w32_ctx = ctx->priv; HGLRC *context = &w32_ctx->context; if (*context) // reuse existing context - return 0; // not reusing it breaks gl3! + return true; // not reusing it breaks gl3! HWND win = ctx->vo->w32->window; HDC windc = vo_w32_get_dc(ctx->vo, win); @@ -1818,7 +2078,7 @@ static int create_window_w32_gl3(struct MPGLContext *ctx, int gl_flags, new_context = wglCreateContext(windc); if (!new_context) { mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GL context!\n"); - return -1; + return false; } // set context @@ -1844,6 +2104,7 @@ static int create_window_w32_gl3(struct MPGLContext *ctx, int gl_flags, if (!wglCreateContextAttribsARB) goto unsupported; + int gl_version = ctx->requested_gl_version; int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, MPGL_VER_GET_MAJOR(gl_version), WGL_CONTEXT_MINOR_VERSION_ARB, MPGL_VER_GET_MINOR(gl_version), @@ -1873,7 +2134,7 @@ static int create_window_w32_gl3(struct MPGLContext *ctx, int gl_flags, if (!wglMakeCurrent(windc, *context)) { mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GL3 context!\n"); wglDeleteContext(*context); - return -1; + return false; } /* update function pointers */ @@ -1887,86 +2148,20 @@ static int create_window_w32_gl3(struct MPGLContext *ctx, int gl_flags, ctx->depth_b = pfd.cBlueBits; } - return 0; + return true; unsupported: mp_msg(MSGT_VO, MSGL_ERR, "[gl] The current OpenGL implementation does" " not support OpenGL 3.x \n"); out: wglDeleteContext(new_context); - return -1; -} - -static int setGlWindow_w32(MPGLContext *ctx) -{ - HWND win = ctx->vo->w32->window; - struct w32_context *w32_ctx = ctx->priv; - int *vinfo = &w32_ctx->vinfo; - HGLRC *context = &w32_ctx->context; - int new_vinfo; - HDC windc = vo_w32_get_dc(ctx->vo, 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 - { - 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(ctx->gl, w32gpa, NULL, false); - - // and inform that reinit is neccessary - res = SET_WINDOW_REINIT; - } else - res = SET_WINDOW_OK; - -out: - vo_w32_release_dc(ctx->vo, win, windc); - return res; + return false; } static void releaseGlContext_w32(MPGLContext *ctx) { struct w32_context *w32_ctx = ctx->priv; - int *vinfo = &w32_ctx->vinfo; HGLRC *context = &w32_ctx->context; - *vinfo = 0; if (*context) { wglMakeCurrent(0, 0); wglDeleteContext(*context); @@ -1992,10 +2187,35 @@ struct glx_context { GLXContext context; }; -static int create_window_x11(struct MPGLContext *ctx, uint32_t d_width, - uint32_t d_height, uint32_t flags) +// Returns the XVisualInfo associated with Window win. +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 bool create_window_x11_old(struct MPGLContext *ctx, uint32_t d_width, + uint32_t d_height, uint32_t flags) +{ + struct glx_context *glx_ctx = ctx->priv; + Display *display = ctx->vo->x11->display; struct vo *vo = ctx->vo; + GL *gl = ctx->gl; + + if (glx_ctx->context) { + // GL context and window already exist. + // Only update window geometry etc. + Colormap colormap = XCreateColormap(display, vo->x11->rootwin, + glx_ctx->vinfo->visual, AllocNone); + vo_x11_create_vo_window(vo, glx_ctx->vinfo, vo->dx, vo->dy, d_width, + d_height, flags, colormap, "gl"); + XFreeColormap(display, colormap); + return true; + } static int default_glx_attribs[] = { GLX_RGBA, GLX_RED_SIZE, 1, GLX_GREEN_SIZE, 1, GLX_BLUE_SIZE, 1, @@ -2007,135 +2227,69 @@ static int create_window_x11(struct MPGLContext *ctx, uint32_t d_width, }; XVisualInfo *vinfo = NULL; if (flags & VOFLAG_STEREO) { - vinfo = glXChooseVisual(vo->x11->display, vo->x11->screen, - stereo_glx_attribs); + vinfo = glXChooseVisual(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); + vinfo = glXChooseVisual(display, vo->x11->screen, default_glx_attribs); if (!vinfo) { mp_msg(MSGT_VO, MSGL_ERR, "[gl] no GLX support present\n"); - return -1; + return false; } 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, + Colormap colormap = XCreateColormap(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"); - return 0; -} - -/** - * \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); -} - -/** - * \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) -{ - struct glx_context *glx_context = ctx->priv; - XVisualInfo **vinfo = &glx_context->vinfo; - GLXContext *context = &glx_context->context; - 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; + XVisualInfo *new_vinfo = getWindowVisualInfo(ctx, ctx->vo->x11->window); + GLXContext 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); - 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; - } + return false; } - // 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; + glXDestroyContext(display, new_context); + XFree(new_vinfo); + return false; } - // set new values - ctx->vo->x11->window = win; - vo_x11_update_geometry(ctx->vo, 1); - if (!keep_context) { - void *(*getProcAddress)(const GLubyte *); - if (*context) - glXDestroyContext(display, *context); - *context = new_context; - if (*vinfo) - XFree(*vinfo); - *vinfo = new_vinfo; - getProcAddress = getdladdr("glXGetProcAddress"); - if (!getProcAddress) - getProcAddress = getdladdr("glXGetProcAddressARB"); + void *(*getProcAddress)(const GLubyte *); + getProcAddress = getdladdr("glXGetProcAddress"); + if (!getProcAddress) + getProcAddress = getdladdr("glXGetProcAddressARB"); - const char *glxstr = ""; - const char *(*glXExtStr)(Display *, int) - = getdladdr("glXQueryExtensionsString"); - if (glXExtStr) - glxstr = glXExtStr(display, ctx->vo->x11->screen); + const char *glxstr = ""; + const char *(*glXExtStr)(Display *, int) + = getdladdr("glXQueryExtensionsString"); + if (glXExtStr) + glxstr = glXExtStr(display, ctx->vo->x11->screen); - getFunctions(gl, getProcAddress, glxstr, false); - 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, false); - } - - // and inform that reinit is neccessary - return SET_WINDOW_REINIT; + getFunctions(gl, getProcAddress, glxstr, false); + if (!gl->GenPrograms && gl->GetString && + gl->version < MPGL_VER(3, 0) && + 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, false); } - return SET_WINDOW_OK; + + glx_ctx->context = new_context; + glx_ctx->vinfo = new_vinfo; + + if (!glXIsDirect(vo->x11->display, new_context)) + ctx->gl->mpgl_caps &= ~MPGL_CAP_NO_SW; + + return true; } // The GL3 initialization code roughly follows/copies from: @@ -2161,9 +2315,8 @@ static GLXFBConfig select_fb_config(struct vo *vo, const int *attribs) typedef GLXContext (*glXCreateContextAttribsARBProc) (Display*, GLXFBConfig, GLXContext, Bool, const int*); -static int create_window_x11_gl3(struct MPGLContext *ctx, int gl_flags, - int gl_version, uint32_t d_width, - uint32_t d_height, uint32_t flags) +static bool create_window_x11_gl3(struct MPGLContext *ctx, uint32_t d_width, + uint32_t d_height, uint32_t flags) { struct vo *vo = ctx->vo; struct glx_context *glx_ctx = ctx->priv; @@ -2176,7 +2329,7 @@ static int create_window_x11_gl3(struct MPGLContext *ctx, int gl_flags, vo_x11_create_vo_window(vo, glx_ctx->vinfo, vo->dx, vo->dy, d_width, d_height, flags, colormap, "gl"); XFreeColormap(vo->x11->display, colormap); - return SET_WINDOW_OK; + return true; } int glx_major, glx_minor; @@ -2186,7 +2339,7 @@ static int create_window_x11_gl3(struct MPGLContext *ctx, int gl_flags, (MPGL_VER(glx_major, glx_minor) < MPGL_VER(1, 3))) { mp_msg(MSGT_VO, MSGL_ERR, "[gl] GLX version older than 1.3.\n"); - return SET_WINDOW_FAILED; + return false; } const int glx_attribs_stereo_value_idx = 1; // index of GLX_STEREO + 1 @@ -2213,7 +2366,7 @@ static int create_window_x11_gl3(struct MPGLContext *ctx, int gl_flags, fbc = select_fb_config(vo, glx_attribs); if (!fbc) { mp_msg(MSGT_VO, MSGL_ERR, "[gl] no GLX support present\n"); - return SET_WINDOW_FAILED; + return false; } glXGetFBConfigAttrib(vo->x11->display, fbc, GLX_RED_SIZE, &ctx->depth_r); @@ -2242,15 +2395,16 @@ static int create_window_x11_gl3(struct MPGLContext *ctx, int gl_flags, if (!(have_ctx_ext && glXCreateContextAttribsARB)) { XFree(vinfo); - return SET_WINDOW_FAILED; + return false; } + int gl_version = ctx->requested_gl_version; int context_attribs[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, MPGL_VER_GET_MAJOR(gl_version), GLX_CONTEXT_MINOR_VERSION_ARB, MPGL_VER_GET_MINOR(gl_version), GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB - | (gl_flags & MPGLFLAG_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), + | (flags & VOFLAG_GL_DEBUG ? GLX_CONTEXT_DEBUG_BIT_ARB : 0), None }; GLXContext context = glXCreateContextAttribsARB(vo->x11->display, fbc, 0, @@ -2258,7 +2412,7 @@ static int create_window_x11_gl3(struct MPGLContext *ctx, int gl_flags, if (!context) { mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not create GLX context!\n"); XFree(vinfo); - return SET_WINDOW_FAILED; + return false; } // set context @@ -2266,7 +2420,7 @@ static int create_window_x11_gl3(struct MPGLContext *ctx, int gl_flags, mp_msg(MSGT_VO, MSGL_FATAL, "[gl] Could not set GLX context!\n"); glXDestroyContext(vo->x11->display, context); XFree(vinfo); - return SET_WINDOW_FAILED; + return false; } glx_ctx->vinfo = vinfo; @@ -2274,7 +2428,10 @@ static int create_window_x11_gl3(struct MPGLContext *ctx, int gl_flags, getFunctions(ctx->gl, (void *)glXGetProcAddress, glxstr, true); - return SET_WINDOW_REINIT; + if (!glXIsDirect(vo->x11->display, context)) + ctx->gl->mpgl_caps &= ~MPGL_CAP_NO_SW; + + return true; } /** @@ -2292,7 +2449,8 @@ static void releaseGlContext_x11(MPGLContext *ctx) XFree(*vinfo); *vinfo = NULL; if (*context) { - gl->Finish(); + if (gl->Finish) + gl->Finish(); glXMakeCurrent(display, None, NULL); glXDestroyContext(display, *context); } @@ -2334,45 +2492,46 @@ int mpgl_find_backend(const char *name) return -1; } -MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) +MPGLContext *mpgl_init(enum MPGLType type, struct vo *vo) { MPGLContext *ctx; if (type == GLTYPE_AUTO) { - ctx = init_mpglcontext(GLTYPE_COCOA, vo); + ctx = mpgl_init(GLTYPE_COCOA, vo); if (ctx) return ctx; - ctx = init_mpglcontext(GLTYPE_W32, vo); + ctx = mpgl_init(GLTYPE_W32, vo); if (ctx) return ctx; - return init_mpglcontext(GLTYPE_X11, vo); + return mpgl_init(GLTYPE_X11, vo); } ctx = talloc_zero(NULL, MPGLContext); - ctx->gl = talloc_zero(ctx, GL); - ctx->type = type; - ctx->vo = vo; + *ctx = (MPGLContext) { + .gl = talloc_zero(ctx, GL), + .type = type, + .vo = vo, + .requested_gl_version = MPGL_VER(3, 0), + .vo_init_ok = true, + }; switch (ctx->type) { #ifdef CONFIG_GL_COCOA case GLTYPE_COCOA: - ctx->create_window = create_window_cocoa; + ctx->create_window_old = create_window_cocoa_old; ctx->create_window_gl3 = create_window_cocoa_gl3; - ctx->setGlWindow = setGlWindow_cocoa; ctx->releaseGlContext = releaseGlContext_cocoa; ctx->swapGlBuffers = swapGlBuffers_cocoa; ctx->check_events = cocoa_check_events; ctx->update_xinerama_info = cocoa_update_xinerama_info; ctx->fullscreen = cocoa_fullscreen; ctx->ontop = vo_cocoa_ontop; + ctx->vo_init = vo_cocoa_init; ctx->vo_uninit = vo_cocoa_uninit; - if (vo_cocoa_init(vo)) - return ctx; break; #endif #ifdef CONFIG_GL_WIN32 case GLTYPE_W32: ctx->priv = talloc_zero(ctx, struct w32_context); - ctx->create_window = create_window_w32; + ctx->create_window_old = create_window_w32_old; ctx->create_window_gl3 = create_window_w32_gl3; - ctx->setGlWindow = setGlWindow_w32; ctx->releaseGlContext = releaseGlContext_w32; ctx->swapGlBuffers = swapGlBuffers_w32; ctx->update_xinerama_info = w32_update_xinerama_info; @@ -2380,16 +2539,14 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) ctx->check_events = vo_w32_check_events; ctx->fullscreen = vo_w32_fullscreen; ctx->ontop = vo_w32_ontop; + ctx->vo_init = vo_w32_init; ctx->vo_uninit = vo_w32_uninit; - if (vo_w32_init(vo)) - return ctx; break; #endif #ifdef CONFIG_GL_X11 case GLTYPE_X11: ctx->priv = talloc_zero(ctx, struct glx_context); - ctx->create_window = create_window_x11; - ctx->setGlWindow = setGlWindow_x11; + ctx->create_window_old = create_window_x11_old; ctx->create_window_gl3 = create_window_x11_gl3; ctx->releaseGlContext = releaseGlContext_x11; ctx->swapGlBuffers = swapGlBuffers_x11; @@ -2398,40 +2555,85 @@ MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo) ctx->check_events = vo_x11_check_events; ctx->fullscreen = vo_x11_fullscreen; ctx->ontop = vo_x11_ontop; + ctx->vo_init = vo_init; ctx->vo_uninit = vo_x11_uninit; - if (vo_init(vo)) - return ctx; break; #endif } + if (ctx->vo_init && ctx->vo_init(vo)) + return ctx; talloc_free(ctx); return NULL; } -int create_mpglcontext(struct MPGLContext *ctx, int gl_flags, int gl_version, - uint32_t d_width, uint32_t d_height, uint32_t flags) +bool mpgl_destroy_window(struct MPGLContext *ctx) { - if (gl_version < MPGL_VER(3, 0)) { - if (ctx->create_window(ctx, d_width, d_height, flags) < 0) - return SET_WINDOW_FAILED; - return ctx->setGlWindow(ctx); - } else { - if (!ctx->create_window_gl3) { - mp_msg(MSGT_VO, MSGL_ERR, "[gl] OpenGL 3.x context creation not " - "implemented.\n"); - return SET_WINDOW_FAILED; - } - return ctx->create_window_gl3(ctx, gl_flags, gl_version, d_width, - d_height, flags); - } + ctx->releaseGlContext(ctx); + *ctx->gl = (GL) {0}; + // This is a caveat. At least on X11, this will recreate the X display + // connection. Also, if vo_init() fails, unspecified things will happen. + ctx->vo_uninit(ctx->vo); + ctx->vo_init_ok = ctx->vo_init(ctx->vo); + return ctx->vo_init_ok; } -void uninit_mpglcontext(MPGLContext *ctx) +static bool create_window(struct MPGLContext *ctx, int gl_caps, + bool (*create)(struct MPGLContext *, uint32_t, + uint32_t, uint32_t), + uint32_t d_width, uint32_t d_height, uint32_t flags) +{ + if (!create || !ctx->vo_init_ok) + return false; + if (create(ctx, d_width, d_height, flags)) { + int missing = (ctx->gl->mpgl_caps & gl_caps) ^ gl_caps; + if (!missing) { + ctx->selected_create_window = create; + return true; + } + mp_msg(MSGT_VO, MSGL_WARN, "[gl] Missing OpenGL features:"); + list_features(missing, MSGL_WARN, false); + if (missing & MPGL_CAP_NO_SW) { + mp_msg(MSGT_VO, MSGL_WARN, "[gl] Rejecting suspected software " + "OpenGL renderer.\n"); + } + } + // If we tried to create a GL 3 context, and we're going to create a legacy + // context after this, the window should be recreated at least on X11. + mpgl_destroy_window(ctx); + return false; +} + +bool mpgl_create_window(struct MPGLContext *ctx, int gl_caps, uint32_t d_width, + uint32_t d_height, uint32_t flags) +{ + assert(ctx->vo_init_ok); + if (ctx->selected_create_window) + return ctx->selected_create_window(ctx, d_width, d_height, flags); + + bool allow_gl3 = !(gl_caps & MPGL_CAP_GL_LEGACY); + bool allow_legacy = !(gl_caps & MPGL_CAP_GL3); + gl_caps |= MPGL_CAP_GL; + + if (allow_gl3 && create_window(ctx, gl_caps, ctx->create_window_gl3, + d_width, d_height, flags)) + return true; + + if (allow_legacy && create_window(ctx, gl_caps, ctx->create_window_old, + d_width, d_height, flags)) + return true; + + mp_msg(MSGT_VO, MSGL_ERR, "[gl] OpenGL context creation failed!\n"); + return false; +} + +void mpgl_uninit(MPGLContext *ctx) { if (!ctx) return; - ctx->releaseGlContext(ctx); - ctx->vo_uninit(ctx->vo); + if (ctx->vo_init_ok) { + ctx->releaseGlContext(ctx); + ctx->vo_uninit(ctx->vo); + } talloc_free(ctx); } diff --git a/libvo/gl_common.h b/libvo/gl_common.h index f42caa8fd1..d2592b2f4f 100644 --- a/libvo/gl_common.h +++ b/libvo/gl_common.h @@ -153,16 +153,6 @@ 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. -#define SET_WINDOW_OK 0 -//! new window is set, but the OpenGL context needs to be reinitialized. -#define SET_WINDOW_REINIT 1 -/** \} */ - enum MPGLType { GLTYPE_AUTO, GLTYPE_COCOA, @@ -171,49 +161,82 @@ enum MPGLType { }; enum { - MPGLFLAG_DEBUG = 1, + MPGL_CAP_GL = (1 << 0), // GL was successfully loaded + MPGL_CAP_GL_LEGACY = (1 << 1), // GL 1.1 (but not 3.x) + MPGL_CAP_GL2 = (1 << 2), // GL 2.0 (3.x core subset) + MPGL_CAP_GL21 = (1 << 3), // GL 2.1 (3.x core subset) + MPGL_CAP_GL3 = (1 << 4), // GL 3.x core + MPGL_CAP_FB = (1 << 5), + MPGL_CAP_VAO = (1 << 6), + MPGL_CAP_SRGB_TEX = (1 << 7), + MPGL_CAP_SRGB_FB = (1 << 8), + MPGL_CAP_FLOAT_TEX = (1 << 9), + MPGL_CAP_TEX_RG = (1 << 10), // GL_ARB_texture_rg / GL 3.x + MPGL_CAP_NO_SW = (1 << 30), // used to block sw. renderers }; #define MPGL_VER(major, minor) (((major) << 16) | (minor)) #define MPGL_VER_GET_MAJOR(ver) ((ver) >> 16) #define MPGL_VER_GET_MINOR(ver) ((ver) & ((1 << 16) - 1)) +#define MPGL_VER_P(ver) MPGL_VER_GET_MAJOR(ver), MPGL_VER_GET_MINOR(ver) + typedef struct MPGLContext { GL *gl; enum MPGLType type; struct vo *vo; - void *priv; + // Bit size of each component in the created framebuffer. 0 if unknown. int depth_r, depth_g, depth_b; - int (*create_window)(struct MPGLContext *ctx, uint32_t d_width, - uint32_t d_height, uint32_t flags); - int (*setGlWindow)(struct MPGLContext *); - void (*releaseGlContext)(struct MPGLContext *); + + // GL version requested from create_window_gl3 backend. + // (Might be different from the actual version in gl->version.) + int requested_gl_version; + void (*swapGlBuffers)(struct MPGLContext *); int (*check_events)(struct vo *vo); void (*fullscreen)(struct vo *vo); + int (*vo_init)(struct vo *vo); void (*vo_uninit)(struct vo *vo); - // only available if GL3 context creation is supported - // gl_flags: bitfield of MPGLFLAG_* constants - // gl_version: requested OpenGL version number (use MPGL_VER()) - // return value is one of the SET_WINDOW_* constants - int (*create_window_gl3)(struct MPGLContext *ctx, int gl_flags, - int gl_version, uint32_t d_width, - uint32_t d_height, uint32_t flags); + void (*releaseGlContext)(struct MPGLContext *); + + // Creates GL 1.x/2.x legacy context. + bool (*create_window_old)(struct MPGLContext *ctx, uint32_t d_width, + uint32_t d_height, uint32_t flags); + + // Creates GL 3.x core context. + bool (*create_window_gl3)(struct MPGLContext *ctx, uint32_t d_width, + uint32_t d_height, uint32_t flags); + // optional void (*ontop)(struct vo *vo); void (*border)(struct vo *vo); void (*update_xinerama_info)(struct vo *vo); + + // For free use by the backend. + void *priv; + // Internal to gl_common.c. + bool (*selected_create_window)(struct MPGLContext *ctx, uint32_t d_width, + uint32_t d_height, uint32_t flags); + bool vo_init_ok; } MPGLContext; int mpgl_find_backend(const char *name); -MPGLContext *init_mpglcontext(enum MPGLType type, struct vo *vo); -void uninit_mpglcontext(MPGLContext *ctx); +MPGLContext *mpgl_init(enum MPGLType type, struct vo *vo); +void mpgl_uninit(MPGLContext *ctx); -// calls create_window_gl3 or create_window+setGlWindow -int create_mpglcontext(struct MPGLContext *ctx, int gl_flags, int gl_version, - uint32_t d_width, uint32_t d_height, uint32_t flags); +// Create a VO window and create a GL context on it. +// (Calls create_window_gl3 or create_window+setGlWindow.) +// gl_caps: bitfield of MPGL_CAP_* (required GL version and feature set) +// flags: passed to the backend's create window function +// Returns success. +bool mpgl_create_window(struct MPGLContext *ctx, int gl_caps, uint32_t d_width, + uint32_t d_height, uint32_t flags); + +// Destroy the window, without resetting GL3 vs. GL2 context choice. +// If this fails (false), mpgl_uninit(ctx) must be called. +bool mpgl_destroy_window(struct MPGLContext *ctx); // print a multi line string with line numbers (e.g. for shader sources) // mod, lev: module and log level, as in mp_msg() @@ -221,6 +244,11 @@ void mp_log_source(int mod, int lev, const char *src); //function pointers loaded from the OpenGL library struct GL { + int version; // MPGL_VER() mangled + int glsl_version; // e.g. 130 for GLSL 1.30 + char *extensions; // Equivalent to GL_EXTENSIONS + int mpgl_caps; // Bitfield of MPGL_CAP_* constants + void (GLAPIENTRY *Begin)(GLenum); void (GLAPIENTRY *End)(void); void (GLAPIENTRY *Viewport)(GLint, GLint, GLsizei, GLsizei); @@ -281,8 +309,6 @@ struct GL { void (GLAPIENTRY *DisableClientState)(GLenum); GLenum (GLAPIENTRY *GetError)(void); - - // OpenGL extension functions void (GLAPIENTRY *GenBuffers)(GLsizei, GLuint *); void (GLAPIENTRY *DeleteBuffers)(GLsizei, const GLuint *); void (GLAPIENTRY *BindBuffer)(GLenum, GLuint); @@ -304,7 +330,6 @@ struct GL { GLsizei, GLint, GLenum, GLenum, const GLvoid *); - // ancient ATI extensions void (GLAPIENTRY *BeginFragmentShader)(void); void (GLAPIENTRY *EndFragmentShader)(void); void (GLAPIENTRY *SampleMap)(GLuint, GLuint, GLenum); @@ -315,8 +340,6 @@ struct GL { GLuint, GLuint, GLuint); void (GLAPIENTRY *SetFragmentShaderConstant)(GLuint, const GLfloat *); - - // GL 3, possibly in GL 2.x as well in form of extensions void (GLAPIENTRY *GenVertexArrays)(GLsizei, GLuint *); void (GLAPIENTRY *BindVertexArray)(GLuint); GLint (GLAPIENTRY *GetAttribLocation)(GLuint, const GLchar *); @@ -350,6 +373,7 @@ struct GL { GLint); void (GLAPIENTRY *Uniform1f)(GLint, GLfloat); + void (GLAPIENTRY *Uniform2f)(GLint, GLfloat, GLfloat); void (GLAPIENTRY *Uniform3f)(GLint, GLfloat, GLfloat, GLfloat); void (GLAPIENTRY *Uniform1i)(GLint, GLint); void (GLAPIENTRY *UniformMatrix3fv)(GLint, GLsizei, GLboolean, diff --git a/libvo/video_out.h b/libvo/video_out.h index 2cd314f281..5ab5f96301 100644 --- a/libvo/video_out.h +++ b/libvo/video_out.h @@ -126,6 +126,7 @@ typedef struct { #define VOFLAG_FLIPPING 0x08 #define VOFLAG_HIDDEN 0x10 //< Use to create a hidden window #define VOFLAG_STEREO 0x20 //< Use to create a stereo-capable window +#define VOFLAG_GL_DEBUG 0x40 // Hint to request debug OpenGL context typedef struct vo_info_s { diff --git a/libvo/vo_corevideo.m b/libvo/vo_corevideo.m index 1d10330bcb..d92e11ca1c 100644 --- a/libvo/vo_corevideo.m +++ b/libvo/vo_corevideo.m @@ -154,9 +154,8 @@ static int config(struct vo *vo, uint32_t width, uint32_t height, p->image_width = width; p->image_height = height; - if (p->mpglctx->create_window(p->mpglctx, d_width, d_height, flags) < 0) - return -1; - if (p->mpglctx->setGlWindow(p->mpglctx) == SET_WINDOW_FAILED) + int mpgl_caps = MPGL_CAP_GL_LEGACY; + if (!mpgl_create_window(p->mpglctx, mpgl_caps, d_width, d_height, flags)) return -1; init_gl(vo, vo->dwidth, vo->dheight); @@ -367,7 +366,7 @@ static int query_format(struct vo *vo, uint32_t format) static void uninit(struct vo *vo) { struct priv *p = vo->priv; - uninit_mpglcontext(p->mpglctx); + mpgl_uninit(p->mpglctx); release_cv_entities(vo); } @@ -377,7 +376,7 @@ static int preinit(struct vo *vo, const char *arg) struct priv *p = vo->priv; *p = (struct priv) { - .mpglctx = init_mpglcontext(GLTYPE_COCOA, vo), + .mpglctx = mpgl_init(GLTYPE_COCOA, vo), .colorspace = MP_CSP_DETAILS_DEFAULTS, .quad = talloc_ptrtype(p, p->quad), .osd = talloc_ptrtype(p, p->osd), diff --git a/libvo/vo_gl.c b/libvo/vo_gl.c index 5453943fe9..f24448ac6c 100644 --- a/libvo/vo_gl.c +++ b/libvo/vo_gl.c @@ -62,6 +62,8 @@ struct gl_priv { MPGLContext *glctx; GL *gl; + int allow_sw; + int use_osd; int scaled_osd; //! Textures for OSD @@ -388,6 +390,9 @@ static void uninitGl(struct vo *vo) struct gl_priv *p = vo->priv; GL *gl = p->gl; + if (!gl) + return; + int i = 0; if (gl->DeletePrograms && p->fragprog) gl->DeletePrograms(1, &p->fragprog); @@ -415,16 +420,6 @@ static void uninitGl(struct vo *vo) p->err_shown = 0; } -static int isSoftwareGl(struct vo *vo) -{ - struct gl_priv *p = vo->priv; - const char *renderer = p->gl->GetString(GL_RENDERER); - const char *vendor = p->gl->GetString(GL_VENDOR); - return !renderer || strcmp(renderer, "Software Rasterizer") == 0 || - strstr(renderer, "llvmpipe") || - strcmp(vendor, "Microsoft Corporation") == 0; -} - static void autodetectGlExtensions(struct vo *vo) { struct gl_priv *p = vo->priv; @@ -596,15 +591,18 @@ static int initGl(struct vo *vo, uint32_t d_width, uint32_t d_height) return 1; } -static int create_window(struct vo *vo, uint32_t d_width, uint32_t d_height, - uint32_t flags) +static bool create_window(struct vo *vo, uint32_t d_width, uint32_t d_height, + uint32_t flags) { struct gl_priv *p = vo->priv; if (p->stereo_mode == GL_3D_QUADBUFFER) flags |= VOFLAG_STEREO; - return p->glctx->create_window(p->glctx, d_width, d_height, flags); + int mpgl_caps = MPGL_CAP_GL_LEGACY; + if (!p->allow_sw) + mpgl_caps |= MPGL_CAP_NO_SW; + return mpgl_create_window(p->glctx, mpgl_caps, d_width, d_height, flags); } static int config(struct vo *vo, uint32_t width, uint32_t height, @@ -626,13 +624,12 @@ static int config(struct vo *vo, uint32_t width, uint32_t height, p->vo_flipped = !!(flags & VOFLAG_FLIPPING); - if (create_window(vo, d_width, d_height, flags) < 0) - return -1; - if (vo->config_count) uninitGl(vo); - if (p->glctx->setGlWindow(p->glctx) == SET_WINDOW_FAILED) + + if (!create_window(vo, d_width, d_height, flags)) return -1; + initGl(vo, vo->dwidth, vo->dheight); return 0; @@ -1153,13 +1150,12 @@ static void uninit(struct vo *vo) { struct gl_priv *p = vo->priv; - if (p->glctx) - uninitGl(vo); + uninitGl(vo); free(p->custom_prog); p->custom_prog = NULL; free(p->custom_tex); p->custom_tex = NULL; - uninit_mpglcontext(p->glctx); + mpgl_uninit(p->glctx); p->glctx = NULL; p->gl = NULL; } @@ -1192,7 +1188,6 @@ static int preinit(struct vo *vo, const char *arg) p->eosd = eosd_packer_create(vo); - int allow_sw = 0; char *backend_arg = NULL; //essentially unused; for legacy warnings only @@ -1223,7 +1218,7 @@ static int preinit(struct vo *vo, const char *arg) {"mipmapgen", OPT_ARG_BOOL, &p->mipmap_gen, NULL}, {"osdcolor", OPT_ARG_INT, &p->osd_color, NULL}, {"stereo", OPT_ARG_INT, &p->stereo_mode, NULL}, - {"sw", OPT_ARG_BOOL, &allow_sw, NULL}, + {"sw", OPT_ARG_BOOL, &p->allow_sw, NULL}, {"backend", OPT_ARG_MSTRZ,&backend_arg, backend_valid}, // Removed options. // They are only parsed to notify the user about the replacements. @@ -1330,27 +1325,21 @@ static int preinit(struct vo *vo, const char *arg) int backend = backend_arg ? mpgl_find_backend(backend_arg) : GLTYPE_AUTO; free(backend_arg); - p->glctx = init_mpglcontext(backend, vo); + p->glctx = mpgl_init(backend, 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) < 0) - goto err_out; - if (p->glctx->setGlWindow(p->glctx) == SET_WINDOW_FAILED) - goto err_out; - if (!allow_sw && isSoftwareGl(vo)) + if (p->use_yuv == -1) { + if (!create_window(vo, 320, 200, VOFLAG_HIDDEN)) 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(backend, vo); - if (!p->glctx) + uninitGl(vo); + if (!mpgl_destroy_window(p->glctx)) goto err_out; - p->gl = p->glctx->gl; } if (p->many_fmts) mp_msg(MSGT_VO, MSGL_INFO, "[gl] using extended formats. " diff --git a/libvo/vo_gl3.c b/libvo/vo_gl3.c index dea0c6637a..329d19370f 100644 --- a/libvo/vo_gl3.c +++ b/libvo/vo_gl3.c @@ -148,7 +148,6 @@ struct gl_priv { struct vo *vo; MPGLContext *glctx; GL *gl; - const char *shader_version; int use_indirect; int use_gamma; @@ -160,7 +159,7 @@ struct gl_priv { int use_pbo; int use_glFinish; int use_gl_debug; - int use_gl2; + int allow_sw; int dither_depth; int swap_interval; @@ -194,6 +193,7 @@ struct gl_priv { GLuint dither_texture; float dither_quantization; float dither_multiply; + int dither_size; uint32_t image_width; uint32_t image_height; @@ -305,9 +305,13 @@ static void draw_triangles(struct gl_priv *p, struct vertex *vb, int vert_count) GL_DYNAMIC_DRAW); gl->BindBuffer(GL_ARRAY_BUFFER, 0); - gl->BindVertexArray(p->vao); + if (gl->BindVertexArray) + gl->BindVertexArray(p->vao); + gl->DrawArrays(GL_TRIANGLES, 0, vert_count); - gl->BindVertexArray(0); + + if (gl->BindVertexArray) + gl->BindVertexArray(0); debug_check_gl(p, "after rendering"); } @@ -351,10 +355,12 @@ static void write_quad(struct vertex *va, #undef COLOR_INIT } -static void fbotex_init(struct gl_priv *p, struct fbotex *fbo, int w, int h) +static bool fbotex_init(struct gl_priv *p, struct fbotex *fbo, int w, int h) { GL *gl = p->gl; + bool res = true; + assert(gl->mpgl_caps & MPGL_CAP_FB); assert(!fbo->fbo); assert(!fbo->texture); @@ -375,25 +381,28 @@ static void fbotex_init(struct gl_priv *p, struct fbotex *fbo, int w, int h) gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fbo->texture, 0); - if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER) - != GL_FRAMEBUFFER_COMPLETE) - { + if (gl->CheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { mp_msg(MSGT_VO, MSGL_ERR, "[gl] Error: framebuffer completeness " "check failed!\n"); + res = false; } gl->BindFramebuffer(GL_FRAMEBUFFER, 0); debug_check_gl(p, "after creating framebuffer & associated texture"); + + return res; } static void fbotex_uninit(struct gl_priv *p, struct fbotex *fbo) { GL *gl = p->gl; - gl->DeleteFramebuffers(1, &fbo->fbo); - gl->DeleteTextures(1, &fbo->texture); - *fbo = (struct fbotex) {0}; + if (gl->mpgl_caps & MPGL_CAP_FB) { + gl->DeleteFramebuffers(1, &fbo->fbo); + gl->DeleteTextures(1, &fbo->texture); + *fbo = (struct fbotex) {0}; + } } static void matrix_ortho2d(float m[3][3], float x0, float x1, @@ -444,9 +453,20 @@ static void update_uniforms(struct gl_priv *p, GLuint program) 1.0 / cparams.ggamma, 1.0 / cparams.bgamma); - gl->Uniform1i(gl->GetUniformLocation(program, "texture1"), 0); - gl->Uniform1i(gl->GetUniformLocation(program, "texture2"), 1); - gl->Uniform1i(gl->GetUniformLocation(program, "texture3"), 2); + for (int n = 0; n < p->plane_count; n++) { + char textures_n[32]; + char textures_size_n[32]; + snprintf(textures_n, sizeof(textures_n), "textures[%d]", n); + snprintf(textures_size_n, sizeof(textures_size_n), "textures_size[%d]", n); + + gl->Uniform1i(gl->GetUniformLocation(program, textures_n), n); + gl->Uniform2f(gl->GetUniformLocation(program, textures_size_n), + p->texture_width >> p->planes[n].shift_x, + p->texture_height >> p->planes[n].shift_y); + } + + gl->Uniform2f(gl->GetUniformLocation(program, "dither_size"), + p->dither_size, p->dither_size); gl->Uniform1i(gl->GetUniformLocation(program, "lut_3d"), TEXUNIT_3DLUT); @@ -607,12 +627,12 @@ static void shader_setup_scaler(char **shader, struct scaler *scaler, int pass) // The direction/pass assignment is rather arbitrary, but fixed in // other parts of the code (like FBO setup). const char *direction = pass == 0 ? "0, 1" : "1, 0"; - *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1) " - "sample_convolution_sep%d(vec2(%s), %s, p0, p1)\n", + *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1, p2) " + "sample_convolution_sep%d(vec2(%s), %s, p0, p1, p2)\n", target, size, direction, scaler->lut_name); } else { - *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1) " - "sample_convolution%d(%s, p0, p1)\n", + *shader = talloc_asprintf_append(*shader, "#define %s(p0, p1, p2) " + "sample_convolution%d(%s, p0, p1, p2)\n", target, size, scaler->lut_name); } } @@ -642,7 +662,7 @@ static void compile_shaders(struct gl_priv *p) char *s_eosd = get_section(tmp, src, "frag_eosd"); char *s_osd = get_section(tmp, src, "frag_osd"); - char *header = talloc_asprintf(tmp, "#version %s\n%s", p->shader_version, + char *header = talloc_asprintf(tmp, "#version %d\n%s", gl->glsl_version, shader_prelude); char *header_eosd = talloc_strdup(tmp, header); @@ -881,6 +901,8 @@ static void init_dither(struct gl_priv *p) unsigned char dither[256]; make_dither_matrix(dither, size); + p->dither_size = size; + gl->ActiveTexture(GL_TEXTURE0 + TEXUNIT_DITHER); gl->GenTextures(1, &p->dither_texture); gl->BindTexture(GL_TEXTURE_2D, p->dither_texture); @@ -1081,7 +1103,9 @@ static void do_render(struct gl_priv *p) float final_texw = p->image_width * source->tex_w / (float)source->vp_w; float final_texh = p->image_height * source->tex_h / (float)source->vp_h; - if (p->use_srgb && !p->use_lut_3d) + bool use_srgb_fb = p->use_srgb && !p->use_lut_3d; + + if (use_srgb_fb) gl->Enable(GL_FRAMEBUFFER_SRGB); if (p->stereo_mode) { @@ -1122,7 +1146,8 @@ static void do_render(struct gl_priv *p) draw_triangles(p, vb, VERTICES_PER_QUAD); } - gl->Disable(GL_FRAMEBUFFER_SRGB); + if (use_srgb_fb) + gl->Disable(GL_FRAMEBUFFER_SRGB); gl->UseProgram(0); @@ -1583,6 +1608,70 @@ static void draw_eosd(struct gl_priv *p, mp_eosd_images_t *imgs) gl->Disable(GL_BLEND); } +// Disable features that are not supported with the current OpenGL version. +static void check_gl_features(struct gl_priv *p) +{ + GL *gl = p->gl; + bool have_float_tex = gl->mpgl_caps & MPGL_CAP_FLOAT_TEX; + bool have_fbo = gl->mpgl_caps & MPGL_CAP_FB; + bool have_srgb = (gl->mpgl_caps & MPGL_CAP_SRGB_TEX) && + (gl->mpgl_caps & MPGL_CAP_SRGB_FB); + + char *disabled[10]; + int n_disabled = 0; + + if (have_fbo) { + struct fbotex fbo = {0}; + have_fbo = fbotex_init(p, &fbo, 16, 16); + fbotex_uninit(p, &fbo); + } + + // Disable these only if the user didn't disable scale-sep on the command + // line, so convolution filter can still be forced to be run. + // Normally, we want to disable them by default if FBOs are unavailable, + // because they will be slow (not critically slow, but still slower). + // Without FP textures, we must always disable them. + if (!have_float_tex || (!have_fbo && p->use_scale_sep)) { + for (int n = 0; n < 2; n++) { + struct scaler *scaler = &p->scalers[n]; + if (mp_find_filter_kernel(scaler->name)) { + scaler->name = "bilinear"; + disabled[n_disabled++] + = have_float_tex ? "scaler (FBO)" : "scaler (float tex.)"; + } + } + } + + if (!have_srgb && p->use_srgb) { + p->use_srgb = false; + disabled[n_disabled++] = "sRGB"; + } + if (!have_fbo && p->use_lut_3d) { + p->use_lut_3d = false; + disabled[n_disabled++] = "color management (FBO)"; + } + if (!have_srgb && p->use_lut_3d) { + p->use_lut_3d = false; + disabled[n_disabled++] = "color management (sRGB)"; + } + + if (!have_fbo) { + p->use_scale_sep = false; + p->use_indirect = false; + } + + if (n_disabled) { + mp_msg(MSGT_VO, MSGL_ERR, "[gl] Some OpenGL extensions not detected, " + "disabling: "); + for (int n = 0; n < n_disabled; n++) { + if (n) + mp_msg(MSGT_VO, MSGL_ERR, ", "); + mp_msg(MSGT_VO, MSGL_ERR, "%s", disabled[n]); + } + mp_msg(MSGT_VO, MSGL_ERR, ".\n"); + } +} + static void setup_vertex_array(GL *gl) { size_t stride = sizeof(struct vertex); @@ -1616,15 +1705,7 @@ static int init_gl(struct gl_priv *p) mp_msg(MSGT_VO, MSGL_V, "[gl] Display depth: R=%d, G=%d, B=%d\n", p->glctx->depth_r, p->glctx->depth_g, p->glctx->depth_b); - GLint major, minor; - gl->GetIntegerv(GL_MAJOR_VERSION, &major); - gl->GetIntegerv(GL_MINOR_VERSION, &minor); - - p->shader_version = "130"; - - // Hack for OSX: it only creates 3.2 contexts. - if (MPGL_VER(major, minor) >= MPGL_VER(3, 2)) - p->shader_version = "150"; + check_gl_features(p); gl->Disable(GL_DITHER); gl->Disable(GL_BLEND); @@ -1634,13 +1715,18 @@ static int init_gl(struct gl_priv *p) gl->DrawBuffer(GL_BACK); gl->GenBuffers(1, &p->vertex_buffer); - gl->GenVertexArrays(1, &p->vao); - gl->BindBuffer(GL_ARRAY_BUFFER, p->vertex_buffer); - gl->BindVertexArray(p->vao); - setup_vertex_array(gl); + + if (gl->BindVertexArray) { + gl->GenVertexArrays(1, &p->vao); + gl->BindVertexArray(p->vao); + setup_vertex_array(gl); + gl->BindVertexArray(0); + } else { + setup_vertex_array(gl); + } + gl->BindBuffer(GL_ARRAY_BUFFER, 0); - gl->BindVertexArray(0); GLint max_texture_size; gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size); @@ -1664,7 +1750,8 @@ static void uninit_gl(struct gl_priv *p) uninit_video(p); - gl->DeleteVertexArrays(1, &p->vao); + if (gl->DeleteVertexArrays) + gl->DeleteVertexArrays(1, &p->vao); p->vao = 0; gl->DeleteBuffers(1, &p->vertex_buffer); p->vertex_buffer = 0; @@ -1762,26 +1849,19 @@ static int query_format(uint32_t format) return caps; } -static bool config_window(struct gl_priv *p, uint32_t d_width, +static bool create_window(struct gl_priv *p, uint32_t d_width, uint32_t d_height, uint32_t flags) { if (p->stereo_mode == GL_3D_QUADBUFFER) flags |= VOFLAG_STEREO; - int mpgl_version = MPGL_VER(3, 0); - int mpgl_flags = p->use_gl_debug ? MPGLFLAG_DEBUG : 0; + if (p->use_gl_debug) + flags |= VOFLAG_GL_DEBUG; - if (p->use_gl2) - mpgl_version = MPGL_VER(2, 1); - - if (create_mpglcontext(p->glctx, mpgl_flags, mpgl_version, d_width, - d_height, flags) == SET_WINDOW_FAILED) - return false; - - if (!p->vertex_buffer) - init_gl(p); - - return true; + int mpgl_caps = MPGL_CAP_GL21 | MPGL_CAP_TEX_RG; + if (!p->allow_sw) + mpgl_caps |= MPGL_CAP_NO_SW; + return mpgl_create_window(p->glctx, mpgl_caps, d_width, d_height, flags); } static int config(struct vo *vo, uint32_t width, uint32_t height, @@ -1790,9 +1870,12 @@ static int config(struct vo *vo, uint32_t width, uint32_t height, { struct gl_priv *p = vo->priv; - if (!config_window(p, d_width, d_height, flags)) + if (!create_window(p, d_width, d_height, flags)) return -1; + if (!p->vertex_buffer) + init_gl(p); + p->vo_flipped = !!(flags & VOFLAG_FLIPPING); if (p->image_format != format || p->image_width != width @@ -1918,6 +2001,7 @@ static int control(struct vo *vo, uint32_t request, void *data) char *arg = data; if (!reparse_cmdline(p, arg)) return false; + check_gl_features(p); reinit_rendering(p); resize(p); vo->want_redraw = true; @@ -1932,7 +2016,7 @@ static void uninit(struct vo *vo) struct gl_priv *p = vo->priv; uninit_gl(p); - uninit_mpglcontext(p->glctx); + mpgl_uninit(p->glctx); p->glctx = NULL; p->gl = NULL; } @@ -2105,6 +2189,7 @@ const struct fbo_format fbo_formats[] = { {"rgb", GL_RGB}, {"rgba", GL_RGBA}, {"rgb8", GL_RGB8}, + {"rgb10", GL_RGB10}, {"rgb16", GL_RGB16}, {"rgb16f", GL_RGB16F}, {"rgb32f", GL_RGB32F}, @@ -2224,6 +2309,8 @@ static bool reparse_cmdline(struct gl_priv *p, char *arg) p->use_scale_sep = opt->use_scale_sep; p->dither_depth = opt->dither_depth; + check_gl_features(p); + return true; } @@ -2275,11 +2362,11 @@ static int preinit(struct vo *vo, const char *arg) {"lparam2", OPT_ARG_FLOAT, &p->scaler_params[1]}, {"fancy-downscaling", OPT_ARG_BOOL, &p->use_fancy_downscaling}, {"debug", OPT_ARG_BOOL, &p->use_gl_debug}, - {"force-gl2", OPT_ARG_BOOL, &p->use_gl2}, {"indirect", OPT_ARG_BOOL, &p->use_indirect}, {"scale-sep", OPT_ARG_BOOL, &p->use_scale_sep}, {"fbo-format", OPT_ARG_MSTRZ, &fbo_format, fbo_format_valid}, {"backend", OPT_ARG_MSTRZ, &backend_arg, backend_valid}, + {"sw", OPT_ARG_BOOL, &p->allow_sw}, {"icc-profile", OPT_ARG_MSTRZ, &icc_profile}, {"icc-cache", OPT_ARG_MSTRZ, &icc_cache}, {"icc-intent", OPT_ARG_INT, &icc_intent}, @@ -2328,23 +2415,20 @@ static int preinit(struct vo *vo, const char *arg) p->eosd = eosd_packer_create(vo); - p->glctx = init_mpglcontext(backend, vo); + p->glctx = mpgl_init(backend, vo); if (!p->glctx) goto err_out; p->gl = p->glctx->gl; - if (true) { - if (!config_window(p, 320, 200, VOFLAG_HIDDEN)) - goto err_out; - // We created a window to test whether the GL context could be - // created and so on. Destroy that window to make sure all state - // associated with it is lost. - uninit(vo); - p->glctx = init_mpglcontext(backend, vo); - if (!p->glctx) - goto err_out; - p->gl = p->glctx->gl; - } + if (!create_window(p, 320, 200, VOFLAG_HIDDEN)) + goto err_out; + check_gl_features(p); + // We created a window to test whether the GL context could be + // created and so on. Destroy that window to make sure all state + // associated with it is lost. + uninit_gl(p); + if (!mpgl_destroy_window(p->glctx)) + goto err_out; return 0; @@ -2464,13 +2548,10 @@ static const char help_text[] = " This mechanism is disabled on RGB input.\n" " fbo-format=\n" " Selects the internal format of any FBO textures used.\n" -" fmt can be one of: rgb, rgba, rgb8, rgb16, rgb16f, rgb32f\n" +" fmt can be one of: rgb, rgba, rgb8, rgb10, rgb16, rgb16f, rgb32f\n" " Default: rgb16.\n" " gamma\n" " Always enable gamma control. (Disables delayed enabling.)\n" -" force-gl2\n" -" Create a legacy GL context. This will randomly malfunction\n" -" if the proper extensions are not supported.\n" "Color management:\n" " icc-profile=\n" " Load an ICC profile and use it to transform linear RGB to\n" diff --git a/libvo/vo_gl3_shaders.glsl b/libvo/vo_gl3_shaders.glsl index f67e55e6f5..207b44b4ba 100644 --- a/libvo/vo_gl3_shaders.glsl +++ b/libvo/vo_gl3_shaders.glsl @@ -22,7 +22,29 @@ // inserted at the beginning of all shaders #!section prelude + +// GLSL 1.20 compatibility layer +// texture() should be assumed to always map to texture2D() +#if __VERSION__ >= 130 +# define texture1D texture +# define texture3D texture +# define DECLARE_FRAGPARMS \ + out vec4 out_color; +#else +# define texture texture2D +# define DECLARE_FRAGPARMS +# define out_color gl_FragColor +# define in varying +#endif + #!section vertex_all + +#if __VERSION__ < 130 +# undef in +# define in attribute +# define out varying +#endif + uniform mat3 transform; uniform sampler3D lut_3d; @@ -40,37 +62,36 @@ void main() { gl_Position = vec4(position, 1); color = vertex_color; #ifdef USE_3DLUT - color = vec4(texture(lut_3d, color.rgb).rgb, color.a); + color = vec4(texture3D(lut_3d, color.rgb).rgb, color.a); #endif texcoord = vertex_texcoord; } #!section frag_eosd -uniform sampler2D texture1; +uniform sampler2D textures[3]; in vec2 texcoord; in vec4 color; -out vec4 out_color; +DECLARE_FRAGPARMS void main() { - out_color = vec4(color.rgb, color.a * texture(texture1, texcoord).r); + out_color = vec4(color.rgb, color.a * texture(textures[0], texcoord).r); } #!section frag_osd -uniform sampler2D texture1; +uniform sampler2D textures[3]; in vec2 texcoord; in vec4 color; -out vec4 out_color; +DECLARE_FRAGPARMS void main() { - out_color = texture(texture1, texcoord).rrrg * color; + out_color = texture(textures[0], texcoord).rrrg * color; } #!section frag_video -uniform sampler2D texture1; -uniform sampler2D texture2; -uniform sampler2D texture3; +uniform sampler2D textures[3]; +uniform vec2 textures_size[3]; uniform sampler1D lut_c_1d; uniform sampler1D lut_l_1d; uniform sampler2D lut_c_2d; @@ -83,11 +104,12 @@ uniform float conv_gamma; uniform float dither_quantization; uniform float dither_multiply; uniform float filter_param1; +uniform vec2 dither_size; in vec2 texcoord; -out vec4 out_color; +DECLARE_FRAGPARMS -vec4 sample_bilinear(sampler2D tex, vec2 texcoord) { +vec4 sample_bilinear(sampler2D tex, vec2 texsize, vec2 texcoord) { return texture(tex, texcoord); } @@ -108,8 +130,7 @@ vec4 calcweights(float s) { return t; } -vec4 sample_bicubic_fast(sampler2D tex, vec2 texcoord) { - vec2 texsize = textureSize(tex, 0); +vec4 sample_bicubic_fast(sampler2D tex, vec2 texsize, vec2 texcoord) { vec2 pt = 1 / texsize; vec2 fcoord = fract(texcoord * texsize + vec2(0.5, 0.5)); vec4 parmx = calcweights(fcoord.x); @@ -130,12 +151,12 @@ vec4 sample_bicubic_fast(sampler2D tex, vec2 texcoord) { } float[2] weights2(sampler1D lookup, float f) { - vec4 c = texture(lookup, f); + vec4 c = texture1D(lookup, f); return float[2](c.r, c.g); } float[4] weights4(sampler1D lookup, float f) { - vec4 c = texture(lookup, f); + vec4 c = texture1D(lookup, f); return float[4](c.r, c.g, c.b, c.a); } @@ -169,13 +190,13 @@ float[16] weights16(sampler2D lookup, float f) { c3.r, c3.g, c3.b, c3.a, c4.r, c4.g, c4.b, c4.a); } -#define CONVOLUTION_SEP_N(NAME, N) \ - vec4 NAME(sampler2D tex, vec2 texcoord, vec2 pt, float weights[N]) { \ - vec4 res = vec4(0); \ - for (int n = 0; n < N; n++) { \ - res += weights[n] * texture(tex, texcoord + pt * n); \ - } \ - return res; \ +#define CONVOLUTION_SEP_N(NAME, N) \ + vec4 NAME(sampler2D tex, vec2 texcoord, vec2 pt, float weights[N]) { \ + vec4 res = vec4(0); \ + for (int n = 0; n < N; n++) { \ + res += weights[n] * texture(tex, texcoord + pt * n); \ + } \ + return res; \ } CONVOLUTION_SEP_N(convolution_sep2, 2) @@ -188,8 +209,8 @@ CONVOLUTION_SEP_N(convolution_sep16, 16) // The dir parameter is (0, 1) or (1, 0), and we expect the shader compiler to // remove all the redundant multiplications and additions. #define SAMPLE_CONVOLUTION_SEP_N(NAME, N, SAMPLERT, CONV_FUNC, WEIGHTS_FUNC)\ - vec4 NAME(vec2 dir, SAMPLERT lookup, sampler2D tex, vec2 texcoord) { \ - vec2 texsize = textureSize(tex, 0); \ + vec4 NAME(vec2 dir, SAMPLERT lookup, sampler2D tex, vec2 texsize, \ + vec2 texcoord) { \ vec2 pt = (1 / texsize) * dir; \ float fcoord = dot(fract(texcoord * texsize - 0.5), dir); \ vec2 base = texcoord - fcoord * pt; \ @@ -226,8 +247,7 @@ CONVOLUTION_N(convolution12, 12) CONVOLUTION_N(convolution16, 16) #define SAMPLE_CONVOLUTION_N(NAME, N, SAMPLERT, CONV_FUNC, WEIGHTS_FUNC) \ - vec4 NAME(SAMPLERT lookup, sampler2D tex, vec2 texcoord) { \ - vec2 texsize = textureSize(tex, 0); \ + vec4 NAME(SAMPLERT lookup, sampler2D tex, vec2 texsize, vec2 texcoord) {\ vec2 pt = 1 / texsize; \ vec2 fcoord = fract(texcoord * texsize - 0.5); \ vec2 base = texcoord - fcoord * pt; \ @@ -245,8 +265,7 @@ SAMPLE_CONVOLUTION_N(sample_convolution16, 16, sampler2D, convolution16, weights // Unsharp masking -vec4 sample_sharpen3(sampler2D tex, vec2 texcoord) { - vec2 texsize = textureSize(tex, 0); +vec4 sample_sharpen3(sampler2D tex, vec2 texsize, vec2 texcoord) { vec2 pt = 1 / texsize; vec2 st = pt * 0.5; vec4 p = texture(tex, texcoord); @@ -257,8 +276,7 @@ vec4 sample_sharpen3(sampler2D tex, vec2 texcoord) { return p + (p - 0.25 * sum) * filter_param1; } -vec4 sample_sharpen5(sampler2D tex, vec2 texcoord) { - vec2 texsize = textureSize(tex, 0); +vec4 sample_sharpen5(sampler2D tex, vec2 texsize, vec2 texcoord) { vec2 pt = 1 / texsize; vec2 st1 = pt * 1.2; vec4 p = texture(tex, texcoord); @@ -277,11 +295,11 @@ vec4 sample_sharpen5(sampler2D tex, vec2 texcoord) { void main() { #ifdef USE_PLANAR - vec3 color = vec3(SAMPLE_L(texture1, texcoord).r, - SAMPLE_C(texture2, texcoord).r, - SAMPLE_C(texture3, texcoord).r); + vec3 color = vec3(SAMPLE_L(textures[0], textures_size[0], texcoord).r, + SAMPLE_C(textures[1], textures_size[1], texcoord).r, + SAMPLE_C(textures[2], textures_size[2], texcoord).r); #else - vec3 color = SAMPLE_L(texture1, texcoord).rgb; + vec3 color = SAMPLE_L(textures[0], textures_size[0], texcoord).rgb; #endif #ifdef USE_GBRP color.gbr = color; @@ -306,11 +324,11 @@ void main() { color = pow(color, inv_gamma); #endif #ifdef USE_3DLUT - color = texture(lut_3d, color).rgb; + color = texture3D(lut_3d, color).rgb; #endif #ifdef USE_DITHER - float dither = texture(dither, gl_FragCoord.xy / textureSize(dither, 0)).r; - color = floor(color * dither_multiply + dither ) / dither_quantization; + float dither_value = texture(dither, gl_FragCoord.xy / dither_size).r; + color = floor(color * dither_multiply + dither_value ) / dither_quantization; #endif out_color = vec4(color, 1); }