diff options
Diffstat (limited to 'src/graphics/GLContext.cpp')
-rw-r--r-- | src/graphics/GLContext.cpp | 656 |
1 files changed, 656 insertions, 0 deletions
diff --git a/src/graphics/GLContext.cpp b/src/graphics/GLContext.cpp new file mode 100644 index 0000000..e5ed1ce --- /dev/null +++ b/src/graphics/GLContext.cpp @@ -0,0 +1,656 @@ +// +// libavg - Media Playback Engine. +// Copyright (C) 2003-2014 Ulrich von Zadow +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// Current versions can be found at www.libavg.de +// + +#include "GLContext.h" + +#include "ShaderRegistry.h" +#include "StandardShader.h" + +#ifdef __APPLE__ + #include "CGLContext.h" +#elif defined linux + #ifdef AVG_ENABLE_EGL + #include "EGLContext.h" + #else + #include "GLXContext.h" + #endif +#elif defined _WIN32 + #include "WGLContext.h" +#endif + +#include "../base/Backtrace.h" +#include "../base/Exception.h" +#include "../base/Logger.h" +#include "../base/MathHelper.h" + +#include <iostream> +#include <stdio.h> + + +namespace avg { + +using namespace std; +using namespace boost; + +thread_specific_ptr<GLContext*> GLContext::s_pCurrentContext; +GLContext* GLContext::s_pMainContext = 0; // Optimized access to main context. +bool GLContext::s_bErrorCheckEnabled = false; +bool GLContext::s_bErrorLogEnabled = true; + + +GLContext* GLContext::create(const GLConfig& glConfig, const IntPoint& windowSize, + const SDL_SysWMinfo* pSDLWMInfo) +{ + if (glConfig.m_bGLES) { + AVG_ASSERT(isGLESSupported()); + } +#ifdef __APPLE__ + return new CGLContext(glConfig, windowSize, pSDLWMInfo); +#elif defined linux + #ifdef AVG_ENABLE_EGL + GLConfig tempConfig = glConfig; + tempConfig.m_bGLES = true; + return new EGLContext(tempConfig, windowSize, pSDLWMInfo); + #else + return new GLXContext(glConfig, windowSize, pSDLWMInfo); + #endif +#elif defined _WIN32 + return new WGLContext(glConfig, windowSize, pSDLWMInfo); +#else + AVG_ASSERT(false); + return GLContextPtr(); +#endif +} + +GLContext::GLContext(const IntPoint& windowSize, const SDL_SysWMinfo* pSDLWMInfo) + : m_MaxTexSize(0), + m_bCheckedGPUMemInfoExtension(false), + m_bCheckedMemoryMode(false), + m_BlendColor(0.f, 0.f, 0.f, 0.f), + m_BlendMode(BLEND_ADD), + m_MajorGLVersion(-1) +{ + if (s_pCurrentContext.get() == 0) { + s_pCurrentContext.reset(new (GLContext*)); + } +} + +GLContext::~GLContext() +{ +} + +void GLContext::init(const GLConfig& glConfig, bool bOwnsContext) +{ + m_GLConfig = glConfig; + m_bOwnsContext = bOwnsContext; + activate(); + glproc::init(); + if (m_GLConfig.m_bGLES) { + m_MajorGLVersion = 2; + m_MinorGLVersion = 0; + } else { + const char* pVersion = (const char*)glGetString(GL_VERSION); + sscanf(pVersion, "%d.%d", &m_MajorGLVersion, &m_MinorGLVersion); + } + + if (m_GLConfig.m_bUseDebugContext) { + if (isDebugContextSupported()) { + glproc::DebugMessageCallback(GLContext::debugLogCallback, 0); + } else { + m_GLConfig.m_bUseDebugContext = false; + } + } +#ifndef AVG_ENABLE_EGL + if (m_GLConfig.m_MultiSampleSamples > 1) { + glEnable(GL_MULTISAMPLE); + checkError("init: glEnable(GL_MULTISAMPLE)"); + } +#endif + m_pShaderRegistry = ShaderRegistryPtr(new ShaderRegistry()); + if (useGPUYUVConversion()) { + m_pShaderRegistry->setPreprocessorDefine("ENABLE_YUV_CONVERSION", ""); + } + setBlendMode(BLEND_BLEND, false); + if (!m_GLConfig.m_bUsePOTTextures) { + m_GLConfig.m_bUsePOTTextures = + !queryOGLExtension("GL_ARB_texture_non_power_of_two") && !isGLES(); + } + if (m_GLConfig.m_ShaderUsage == GLConfig::AUTO) { + if (isGLES()) { + m_GLConfig.m_ShaderUsage = GLConfig::MINIMAL; + } else { + m_GLConfig.m_ShaderUsage = GLConfig::FULL; + } + } +#ifdef __APPLE__ + if (GLContext::isVendor("Intel")) { + // Bug #434: Some shaders cause hard lockups on Mac Book Air. + m_GLConfig.m_ShaderUsage = GLConfig::MINIMAL; + } +#endif + for (int i=0; i<16; ++i) { + m_BoundTextures[i] = 0xFFFFFFFF; + } + if (!m_GLConfig.m_bGLES && !queryOGLExtension("GL_ARB_vertex_buffer_object")) { + throw Exception(AVG_ERR_UNSUPPORTED, + "Graphics driver lacks vertex buffer support, unable to initialize graphics."); + } + glEnable(GL_BLEND); + checkError("init: glEnable(GL_BLEND)"); + glDisable(GL_DEPTH_TEST); + checkError("init: glDisable(GL_DEPTH_TEST)"); + glEnable(GL_STENCIL_TEST); + checkError("init: glEnable(GL_STENCIL_TEST)"); +} + +void GLContext::deleteObjects() +{ + m_pStandardShader = StandardShaderPtr(); + for (unsigned i=0; i<m_FBOIDs.size(); ++i) { + glproc::DeleteFramebuffers(1, &(m_FBOIDs[i])); + } + m_FBOIDs.clear(); + if (*s_pCurrentContext == this) { + *s_pCurrentContext = 0; + } +} + +void GLContext::getVersion(int& major, int& minor) const +{ + major = m_MajorGLVersion; + minor = m_MinorGLVersion; +} + +bool GLContext::ownsContext() const +{ + return m_bOwnsContext; +} + +void GLContext::setCurrent() +{ + *s_pCurrentContext = this; +} + +ShaderRegistryPtr GLContext::getShaderRegistry() const +{ + return m_pShaderRegistry; +} + +StandardShaderPtr GLContext::getStandardShader() +{ + if (m_pStandardShader == StandardShaderPtr()) { + m_pStandardShader = StandardShaderPtr(new StandardShader()); + } + return m_pStandardShader; +} + +bool GLContext::useGPUYUVConversion() const +{ + return (m_MajorGLVersion > 1) || isGLES(); +} + +GLConfig::ShaderUsage GLContext::getShaderUsage() const +{ + return m_GLConfig.m_ShaderUsage; +} + +GLBufferCache& GLContext::getVertexBufferCache() +{ + return m_VertexBufferCache; +} + +GLBufferCache& GLContext::getIndexBufferCache() +{ + return m_IndexBufferCache; +} + +GLBufferCache& GLContext::getPBOCache() +{ + return m_PBOCache; +} + +unsigned GLContext::genFBO() +{ + unsigned fboID; + if (m_FBOIDs.empty()) { + glproc::GenFramebuffers(1, &fboID); + } else { + fboID = m_FBOIDs.back(); + m_FBOIDs.pop_back(); + } + return fboID; +} + +void GLContext::returnFBOToCache(unsigned fboID) +{ + m_FBOIDs.push_back(fboID); +} + +void GLContext::setBlendColor(const glm::vec4& color) +{ + if (m_BlendColor != color) { + glproc::BlendColor(color[0], color[1], color[2], color[3]); + m_BlendColor = color; + } +} + +void GLContext::setBlendMode(BlendMode mode, bool bPremultipliedAlpha) +{ + AVG_ASSERT(isBlendModeSupported(mode)); + GLenum srcFunc; + if (bPremultipliedAlpha) { + srcFunc = GL_CONSTANT_ALPHA; + } else { + srcFunc = GL_SRC_ALPHA; + } + if (mode != m_BlendMode || m_bPremultipliedAlpha != bPremultipliedAlpha) { + switch (mode) { + case BLEND_BLEND: + glproc::BlendEquation(GL_FUNC_ADD); + glproc::BlendFuncSeparate(srcFunc, GL_ONE_MINUS_SRC_ALPHA, + GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + checkError("setBlendMode: blend"); + break; + case BLEND_ADD: + glproc::BlendEquation(GL_FUNC_ADD); + glproc::BlendFuncSeparate(srcFunc, GL_ONE, GL_ONE, GL_ONE); + checkError("setBlendMode: add"); + break; + case BLEND_MIN: + glproc::BlendEquation(GL_MIN_EXT); + glproc::BlendFuncSeparate(srcFunc, GL_ONE_MINUS_SRC_ALPHA, + GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + checkError("setBlendMode: min"); + break; + case BLEND_MAX: + glproc::BlendEquation(GL_MAX_EXT); + glproc::BlendFuncSeparate(srcFunc, GL_ONE_MINUS_SRC_ALPHA, + GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + checkError("setBlendMode: max"); + break; + case BLEND_COPY: + glproc::BlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_ONE, GL_ZERO); + checkError("setBlendMode: copy"); + break; + default: + AVG_ASSERT(false); + } + + m_BlendMode = mode; + m_bPremultipliedAlpha = bPremultipliedAlpha; + } +} + +bool GLContext::isBlendModeSupported(BlendMode mode) const +{ + if (isGLES() && (mode == BLEND_MIN || mode == BLEND_MAX)) { + return queryOGLExtension("GL_EXT_blend_minmax"); + } else { + return true; + } +} + +void GLContext::bindTexture(unsigned unit, unsigned texID) +{ + if (m_BoundTextures[unit-GL_TEXTURE0] != texID) { + glproc::ActiveTexture(unit); + checkError("GLContext::bindTexture ActiveTexture()"); + glBindTexture(GL_TEXTURE_2D, texID); + checkError("GLContext::bindTexture BindTexture()"); + m_BoundTextures[unit-GL_TEXTURE0] = texID; + } +} + +const GLConfig& GLContext::getConfig() +{ + return m_GLConfig; +} + +void GLContext::logConfig() +{ + AVG_TRACE(Logger::category::CONFIG, Logger::severity::INFO, "OpenGL configuration: "); + AVG_TRACE(Logger::category::CONFIG, Logger::severity::INFO, + " OpenGL version: " << glGetString(GL_VERSION)); + AVG_TRACE(Logger::category::CONFIG, Logger::severity::INFO, + " OpenGL vendor: " << glGetString(GL_VENDOR)); + AVG_TRACE(Logger::category::CONFIG, Logger::severity::INFO, + " OpenGL renderer: " << glGetString(GL_RENDERER)); + m_GLConfig.log(); + switch (getMemoryMode()) { + case MM_PBO: + AVG_TRACE(Logger::category::CONFIG, Logger::severity::INFO, + " Using pixel buffer objects"); + break; + case MM_OGL: + AVG_TRACE(Logger::category::CONFIG, Logger::severity::INFO, + " Not using GL memory extensions"); + break; + } + AVG_TRACE(Logger::category::CONFIG, Logger::severity::INFO, + " Max. texture size: " << getMaxTexSize()); + string s; + if (useGPUYUVConversion()) { + s = "yes"; + } else { + s = "no"; + } + AVG_TRACE(Logger::category::CONFIG, Logger::severity::INFO, + string(" GPU-based YUV-RGB conversion: ")+s+"."); + try { + AVG_TRACE(Logger::category::CONFIG, Logger::severity::INFO, + " Dedicated video memory: " << getVideoMemInstalled()/(1024*1024) + << " MB"); + AVG_TRACE(Logger::category::CONFIG, Logger::severity::INFO, + " Video memory used at start: " << getVideoMemUsed()/(1024*1024) + << " MB"); + } catch (Exception) { + AVG_TRACE(Logger::category::CONFIG, Logger::severity::ERROR, + " Dedicated video memory: Unknown"); + AVG_TRACE(Logger::category::CONFIG, Logger::severity::ERROR, + " Video memory used at start: Unknown"); + } +} + +size_t GLContext::getVideoMemInstalled() +{ + checkGPUMemInfoSupport(); + int kbMemInstalled; + glGetIntegerv(GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &kbMemInstalled); + return (size_t)kbMemInstalled*1024; +} + +size_t GLContext::getVideoMemUsed() +{ + checkGPUMemInfoSupport(); + int kbMemAvailable; + glGetIntegerv(GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &kbMemAvailable); + return getVideoMemInstalled()-(size_t)kbMemAvailable*1024; +} + +bool GLContext::usePOTTextures() +{ + return m_GLConfig.m_bUsePOTTextures; +} + +bool GLContext::arePBOsSupported() +{ + if (isGLES()) { + return false; + } else { + return (queryOGLExtension("GL_ARB_pixel_buffer_object") || + queryOGLExtension("GL_EXT_pixel_buffer_object")); + } +} + +OGLMemoryMode GLContext::getMemoryMode() +{ + if (!m_bCheckedMemoryMode) { + if (arePBOsSupported() && m_GLConfig.m_bUsePixelBuffers) { + m_MemoryMode = MM_PBO; + } else { + m_MemoryMode = MM_OGL; + } + m_bCheckedMemoryMode = true; + } + return m_MemoryMode; +} + +bool GLContext::isGLES() const +{ + return m_GLConfig.m_bGLES; +} + +bool GLContext::isVendor(const string& sWantedVendor) const +{ + string sVendor((const char *)glGetString(GL_VENDOR)); + return (sVendor.find(sWantedVendor) != string::npos); +} + +bool GLContext::useDepthBuffer() const +{ + return !isGLES(); +} + +int GLContext::getMaxTexSize() +{ + if (m_MaxTexSize == 0) { + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_MaxTexSize); + } + return m_MaxTexSize; +} + + +void GLContext::swapBuffers() +{ + AVG_ASSERT(false); +} + +void GLContext::enableErrorChecks(bool bEnable) +{ + s_bErrorCheckEnabled = bEnable; +} + +void GLContext::checkError(const char* pszWhere) +{ + if (s_bErrorCheckEnabled) { + mandatoryCheckError(pszWhere); + } +} + +void GLContext::mandatoryCheckError(const char* pszWhere) +{ + GLenum err = glGetError(); + if (err != GL_NO_ERROR) { + stringstream s; +#ifndef AVG_ENABLE_EGL + s << "OpenGL error in " << pszWhere <<": " << gluErrorString(err) + << " (#" << err << ") "; +#else + s << "OpenGL error in " << pszWhere <<": (#" << err << ") "; +#endif + AVG_LOG_ERROR(s.str()); + if (err != GL_INVALID_OPERATION) { + checkError(" --"); + } + AVG_ASSERT(false); + } +} + +void GLContext::ensureFullShaders(const string& sContext) const +{ + if (getShaderUsage() != GLConfig::FULL) { + throw Exception(AVG_ERR_UNSUPPORTED, + sContext + " not supported if ShaderUsage==MINIMAL"); + } + +} + +GLContext::BlendMode GLContext::stringToBlendMode(const string& s) +{ + if (s == "blend") { + return GLContext::BLEND_BLEND; + } else if (s == "add") { + return GLContext::BLEND_ADD; + } else if (s == "min") { + return GLContext::BLEND_MIN; + } else if (s == "max") { + return GLContext::BLEND_MAX; + } else { + throw(Exception(AVG_ERR_UNSUPPORTED, "Blend mode "+s+" not supported.")); + } +} + +GLContext* GLContext::getCurrent() +{ + return *s_pCurrentContext; +} + +GLContext* GLContext::getMain() +{ + return s_pMainContext; +} + +void GLContext::setMain(GLContext * pMainContext) +{ + s_pMainContext = pMainContext; +} + +int GLContext::nextMultiSampleValue(int curSamples) +{ + switch (curSamples) { + case 1: + return 0; + case 2: + return 1; + case 4: + return 2; + case 8: + return 4; + default: + return 8; + } +} + +bool GLContext::isGLESSupported() +{ +#if defined linux + #ifdef AVG_ENABLE_EGL + return true; + #else + return GLXContext::haveARBCreateContext(); + #endif +#else + return false; +#endif +} + +void GLContext::enableErrorLog(bool bEnable) +{ + s_bErrorLogEnabled = bEnable; +} + +void GLContext::checkGPUMemInfoSupport() +{ + if (!m_bCheckedGPUMemInfoExtension) { + m_bGPUMemInfoSupported = queryOGLExtension("GL_NVX_gpu_memory_info"); + m_bCheckedGPUMemInfoExtension = true; + } + if (!m_bGPUMemInfoSupported) { + throw Exception(AVG_ERR_UNSUPPORTED, + "Video memory query not supported on this system."); + } +} + +bool GLContext::isDebugContextSupported() const +{ + if (queryOGLExtension("GL_ARB_debug_output") || queryOGLExtension("GL_KHR_debug")) { + return true; + } + if (isGLES() && isVendor("NVIDIA")) { + // There is no extension for debug output in gles 2.0, but Linux NVidia + // supports the functionality anyway. So we activate it :-). + return true; + } + return false; +} + +void GLContext::debugLogCallback(GLenum source, GLenum type, GLuint id, + GLenum severity, GLsizei length, const GLchar* message, void* userParam) +{ +/* + string sSource; + switch (source) { + case GL_DEBUG_SOURCE_API_ARB: + sSource = "API"; + break; + case GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB: + sSource = "Window System"; + break; + case GL_DEBUG_SOURCE_SHADER_COMPILER_ARB: + sSource = "Shader Compiler"; + break; + case GL_DEBUG_SOURCE_THIRD_PARTY_ARB: + sSource = "Third Party"; + break; + case GL_DEBUG_SOURCE_APPLICATION_ARB: + sSource = "Application"; + break; + case GL_DEBUG_SOURCE_OTHER_ARB: + sSource = "Other"; + break; + default: + AVG_ASSERT(false); + } + + string sSeverity; + switch (severity) { + case GL_DEBUG_SEVERITY_HIGH_ARB: + sSeverity = "High"; + break; + case GL_DEBUG_SEVERITY_MEDIUM_ARB: + sSeverity = "Medium"; + break; + case GL_DEBUG_SEVERITY_LOW_ARB: + sSeverity = "Low"; + break; + default: + AVG_ASSERT(false); + } + + string sType; + switch (type) { + case GL_DEBUG_TYPE_ERROR_ARB: + sType = "Error"; + break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB: + sType = "Deprecated Behaviour"; + break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB: + sType = "Undefined Behaviour"; + break; + case GL_DEBUG_TYPE_PORTABILITY_ARB: + sType = "Portability Issue"; + break; + case GL_DEBUG_TYPE_PERFORMANCE_ARB: + sType = "Performance Issue"; + break; + case GL_DEBUG_TYPE_OTHER_ARB: + sType = "Other"; + break; + default: + AVG_ASSERT(false); + } +*/ + + // XXX Temporary to clean up NVidia message spam. +#ifndef AVG_ENABLE_EGL + if (type != GL_DEBUG_TYPE_PERFORMANCE_ARB && s_bErrorLogEnabled) { +#endif + AVG_LOG_WARNING(message); + // dumpBacktrace(); + // AVG_ASSERT(false); +#ifndef AVG_ENABLE_EGL + } +#endif +} + +} |