summaryrefslogtreecommitdiff
path: root/src/graphics/GLContext.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/graphics/GLContext.cpp')
-rw-r--r--src/graphics/GLContext.cpp656
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
+}
+
+}