summaryrefslogtreecommitdiffstats
path: root/vcl/source/opengl/OpenGLContext.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/source/opengl/OpenGLContext.cxx')
-rw-r--r--vcl/source/opengl/OpenGLContext.cxx496
1 files changed, 496 insertions, 0 deletions
diff --git a/vcl/source/opengl/OpenGLContext.cxx b/vcl/source/opengl/OpenGLContext.cxx
new file mode 100644
index 0000000000..e68e66564d
--- /dev/null
+++ b/vcl/source/opengl/OpenGLContext.cxx
@@ -0,0 +1,496 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <chrono>
+
+#include <thread>
+#include <vcl/opengl/OpenGLContext.hxx>
+#include <vcl/opengl/OpenGLHelper.hxx>
+#include <vcl/opengl/OpenGLWrapper.hxx>
+#include <vcl/syschild.hxx>
+#include <vcl/sysdata.hxx>
+
+#include <osl/thread.hxx>
+#include <sal/log.hxx>
+
+#include <svdata.hxx>
+#include <salgdi.hxx>
+#include <salinst.hxx>
+
+#include <opengl/zone.hxx>
+
+#include <config_features.h>
+
+using namespace com::sun::star;
+
+static sal_Int64 nBufferSwapCounter = 0;
+
+GLWindow::~GLWindow()
+{
+}
+
+bool GLWindow::Synchronize(bool /*bOnoff*/) const
+{
+ return false;
+}
+
+OpenGLContext::OpenGLContext():
+ mpWindow(nullptr),
+ m_pChildWindow(nullptr),
+ mbInitialized(false),
+ mnRefCount(0),
+ mbRequestLegacyContext(false),
+ mpPrevContext(nullptr),
+ mpNextContext(nullptr)
+{
+ VCL_GL_INFO("new context: " << this);
+
+ ImplSVData* pSVData = ImplGetSVData();
+ if( pSVData->maGDIData.mpLastContext )
+ {
+ pSVData->maGDIData.mpLastContext->mpNextContext = this;
+ mpPrevContext = pSVData->maGDIData.mpLastContext;
+ }
+ pSVData->maGDIData.mpLastContext = this;
+
+ // FIXME: better hope we call 'makeCurrent' soon to preserve
+ // the invariant that the last item is the current context.
+}
+
+OpenGLContext::~OpenGLContext()
+{
+ assert (mnRefCount == 0);
+
+ mnRefCount = 1; // guard the shutdown paths.
+ VCL_GL_INFO("delete context: " << this);
+
+ reset();
+
+ ImplSVData* pSVData = ImplGetSVData();
+ if( mpPrevContext )
+ mpPrevContext->mpNextContext = mpNextContext;
+ if( mpNextContext )
+ mpNextContext->mpPrevContext = mpPrevContext;
+ else
+ pSVData->maGDIData.mpLastContext = mpPrevContext;
+
+ m_pChildWindow.disposeAndClear();
+ assert (mnRefCount == 1);
+}
+
+// release associated child-window if we have one
+void OpenGLContext::dispose()
+{
+ reset();
+ m_pChildWindow.disposeAndClear();
+}
+
+rtl::Reference<OpenGLContext> OpenGLContext::Create()
+{
+ return rtl::Reference<OpenGLContext>(ImplGetSVData()->mpDefInst->CreateOpenGLContext());
+}
+
+void OpenGLContext::requestLegacyContext()
+{
+ mbRequestLegacyContext = true;
+}
+
+#ifdef DBG_UTIL
+
+namespace {
+
+const char* getSeverityString(GLenum severity)
+{
+ switch(severity)
+ {
+ case GL_DEBUG_SEVERITY_LOW:
+ return "low";
+ case GL_DEBUG_SEVERITY_MEDIUM:
+ return "medium";
+ case GL_DEBUG_SEVERITY_HIGH:
+ return "high";
+ default:
+ ;
+ }
+
+ return "unknown";
+}
+
+const char* getSourceString(GLenum source)
+{
+ switch(source)
+ {
+ case GL_DEBUG_SOURCE_API:
+ return "API";
+ case GL_DEBUG_SOURCE_SHADER_COMPILER:
+ return "shader compiler";
+ case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
+ return "window system";
+ case GL_DEBUG_SOURCE_THIRD_PARTY:
+ return "third party";
+ case GL_DEBUG_SOURCE_APPLICATION:
+ return "Libreoffice";
+ case GL_DEBUG_SOURCE_OTHER:
+ return "unknown";
+ default:
+ ;
+ }
+
+ return "unknown";
+}
+
+const char* getTypeString(GLenum type)
+{
+ switch(type)
+ {
+ case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
+ return "deprecated behavior";
+ case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
+ return "undefined behavior";
+ case GL_DEBUG_TYPE_PERFORMANCE:
+ return "performance";
+ case GL_DEBUG_TYPE_PORTABILITY:
+ return "portability";
+ case GL_DEBUG_TYPE_MARKER:
+ return "marker";
+ case GL_DEBUG_TYPE_PUSH_GROUP:
+ return "push group";
+ case GL_DEBUG_TYPE_POP_GROUP:
+ return "pop group";
+ case GL_DEBUG_TYPE_OTHER:
+ return "other";
+ case GL_DEBUG_TYPE_ERROR:
+ return "error";
+ default:
+ ;
+ }
+
+ return "unknown";
+}
+
+extern "C" void
+#if defined _WIN32
+APIENTRY
+#endif
+debug_callback(GLenum source, GLenum type, GLuint id,
+ GLenum severity, GLsizei , const GLchar* message,
+ const GLvoid*)
+{
+ // ignore Nvidia's 131218: "Program/shader state performance warning: Fragment Shader is going to be recompiled because the shader key based on GL state mismatches."
+ // the GLSL compiler is a bit too aggressive in optimizing the state based on the current OpenGL state
+
+ // ignore 131185: "Buffer detailed info: Buffer object x (bound to GL_ARRAY_BUFFER_ARB,
+ // usage hint is GL_STATIC_DRAW) will use VIDEO memory as the source for buffer object operations."
+ if (id == 131218 || id == 131185)
+ return;
+
+ SAL_WARN("vcl.opengl", "OpenGL debug message: source: " << getSourceString(source) << ", type: "
+ << getTypeString(type) << ", id: " << id << ", severity: " << getSeverityString(severity) << ", with message: " << message);
+}
+
+}
+
+#endif
+
+bool OpenGLContext::init( vcl::Window* pParent )
+{
+ if(mbInitialized)
+ return true;
+
+ OpenGLZone aZone;
+
+ m_xWindow.reset(pParent ? nullptr : VclPtr<vcl::Window>::Create(nullptr, WB_NOBORDER|WB_NODIALOGCONTROL));
+ mpWindow = pParent ? pParent : m_xWindow.get();
+ if(m_xWindow)
+ m_xWindow->setPosSizePixel(0,0,0,0);
+ //tdf#108069 we may be initted twice, so dispose earlier effort
+ m_pChildWindow.disposeAndClear();
+ initWindow();
+ return ImplInit();
+}
+
+bool OpenGLContext::ImplInit()
+{
+ VCL_GL_INFO("OpenGLContext not implemented for this platform");
+ return false;
+}
+
+static OUString getGLString(GLenum eGlEnum)
+{
+ OUString sString;
+ const GLubyte* pString = glGetString(eGlEnum);
+ if (pString)
+ {
+ sString = OUString::createFromAscii(reinterpret_cast<const char*>(pString));
+ }
+
+ CHECK_GL_ERROR();
+ return sString;
+}
+
+bool OpenGLContext::InitGL()
+{
+ VCL_GL_INFO("OpenGLContext::ImplInit----end");
+ VCL_GL_INFO("Vendor: " << getGLString(GL_VENDOR) << " Renderer: " << getGLString(GL_RENDERER) << " GL version: " << OpenGLHelper::getGLVersion());
+ mbInitialized = true;
+
+ // I think we need at least GL 3.0
+ if (epoxy_gl_version() < 30)
+ {
+ SAL_WARN("vcl.opengl", "We don't have at least OpenGL 3.0");
+ return false;
+ }
+
+ // Check that some "optional" APIs that we use unconditionally are present
+ if (!glBindFramebuffer)
+ {
+ SAL_WARN("vcl.opengl", "We don't have glBindFramebuffer");
+ return false;
+ }
+
+ return true;
+}
+
+void OpenGLContext::InitGLDebugging()
+{
+#ifdef DBG_UTIL
+ // only enable debug output in dbgutil build
+ if (epoxy_has_gl_extension("GL_ARB_debug_output"))
+ {
+ OpenGLZone aZone;
+
+ if (glDebugMessageCallbackARB)
+ {
+ glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB);
+ glDebugMessageCallbackARB(&debug_callback, nullptr);
+
+#ifdef GL_DEBUG_SEVERITY_NOTIFICATION_ARB
+ // Ignore i965’s shader compiler notification flood.
+ glDebugMessageControlARB(GL_DEBUG_SOURCE_SHADER_COMPILER_ARB, GL_DEBUG_TYPE_OTHER_ARB, GL_DEBUG_SEVERITY_NOTIFICATION_ARB, 0, nullptr, true);
+#endif
+ }
+ else if ( glDebugMessageCallback )
+ {
+ glEnable(GL_DEBUG_OUTPUT);
+ glDebugMessageCallback(&debug_callback, nullptr);
+
+ // Ignore i965’s shader compiler notification flood.
+ glDebugMessageControl(GL_DEBUG_SOURCE_SHADER_COMPILER, GL_DEBUG_TYPE_OTHER, GL_DEBUG_SEVERITY_NOTIFICATION, 0, nullptr, true);
+ }
+ }
+
+ // Test hooks for inserting tracing messages into the stream
+ VCL_GL_INFO("LibreOffice GLContext initialized");
+#endif
+}
+
+void OpenGLContext::restoreDefaultFramebuffer()
+{
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+}
+
+void OpenGLContext::setWinPosAndSize(const Point &rPos, const Size& rSize)
+{
+ if (m_xWindow)
+ m_xWindow->SetPosSizePixel(rPos, rSize);
+ if (m_pChildWindow)
+ m_pChildWindow->SetPosSizePixel(rPos, rSize);
+
+ GLWindow& rGLWin = getModifiableOpenGLWindow();
+ rGLWin.Width = rSize.Width();
+ rGLWin.Height = rSize.Height();
+ adjustToNewSize();
+}
+
+void OpenGLContext::adjustToNewSize()
+{
+ const GLWindow& rGLWin = getOpenGLWindow();
+ glViewport(0, 0, rGLWin.Width, rGLWin.Height);
+}
+
+void OpenGLContext::InitChildWindow(SystemChildWindow *pChildWindow)
+{
+ pChildWindow->SetMouseTransparent(true);
+ pChildWindow->SetParentClipMode(ParentClipMode::Clip);
+ pChildWindow->EnableEraseBackground(false);
+ pChildWindow->SetControlForeground();
+ pChildWindow->SetControlBackground();
+}
+
+void OpenGLContext::initWindow()
+{
+}
+
+void OpenGLContext::destroyCurrentContext()
+{
+ //nothing by default
+}
+
+void OpenGLContext::reset()
+{
+ if( !mbInitialized )
+ return;
+
+ OpenGLZone aZone;
+
+ if( isCurrent() )
+ resetCurrent();
+
+ mbInitialized = false;
+
+ // destroy the context itself
+ destroyCurrentContext();
+}
+
+SystemWindowData OpenGLContext::generateWinData(vcl::Window* /*pParent*/, bool /*bRequestLegacyContext*/)
+{
+ return {};
+}
+
+bool OpenGLContext::isCurrent()
+{
+ (void) this; // loplugin:staticmethods
+ return false;
+}
+
+void OpenGLContext::makeCurrent()
+{
+ if (isCurrent())
+ return;
+
+ OpenGLZone aZone;
+
+ clearCurrent();
+
+ // by default nothing else to do
+
+ registerAsCurrent();
+}
+
+bool OpenGLContext::isAnyCurrent()
+{
+ return false;
+}
+
+bool OpenGLContext::hasCurrent()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+ rtl::Reference<OpenGLContext> pCurrentCtx = pSVData->maGDIData.mpLastContext;
+ return pCurrentCtx.is() && pCurrentCtx->isAnyCurrent();
+}
+
+void OpenGLContext::clearCurrent()
+{
+}
+
+void OpenGLContext::prepareForYield()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // release all framebuffers from the old context so we can re-attach the
+ // texture in the new context
+ rtl::Reference<OpenGLContext> pCurrentCtx = pSVData->maGDIData.mpLastContext;
+
+ if ( !pCurrentCtx.is() )
+ return; // Not using OpenGL
+
+ SAL_INFO("vcl.opengl", "Unbinding contexts in preparation for yield");
+
+ // Find the first context that is current and reset it.
+ // Usually the last context is the current, but not in case a new
+ // OpenGLContext is created already but not yet initialized.
+ while (pCurrentCtx.is())
+ {
+ if (pCurrentCtx->isCurrent())
+ {
+ pCurrentCtx->resetCurrent();
+ break;
+ }
+
+ pCurrentCtx = pCurrentCtx->mpPrevContext;
+ }
+
+ assert (!hasCurrent());
+}
+
+void OpenGLContext::registerAsCurrent()
+{
+ ImplSVData* pSVData = ImplGetSVData();
+
+ // move the context to the end of the contexts list
+ static int nSwitch = 0;
+ VCL_GL_INFO("******* CONTEXT SWITCH " << ++nSwitch << " *********");
+ if( mpNextContext )
+ {
+ if( mpPrevContext )
+ mpPrevContext->mpNextContext = mpNextContext;
+ mpNextContext->mpPrevContext = mpPrevContext;
+
+ mpPrevContext = pSVData->maGDIData.mpLastContext;
+ mpNextContext = nullptr;
+ pSVData->maGDIData.mpLastContext->mpNextContext = this;
+ pSVData->maGDIData.mpLastContext = this;
+ }
+}
+
+void OpenGLContext::resetCurrent()
+{
+ clearCurrent();
+ // by default nothing else to do
+}
+
+void OpenGLContext::swapBuffers()
+{
+ // by default nothing else to do
+ BuffersSwapped();
+}
+
+void OpenGLContext::BuffersSwapped()
+{
+ nBufferSwapCounter++;
+
+ static bool bSleep = getenv("SAL_GL_SLEEP_ON_SWAP");
+ if (bSleep)
+ {
+ // half a second.
+ std::this_thread::sleep_for(std::chrono::milliseconds(500) );
+ }
+}
+
+
+sal_Int64 OpenGLWrapper::getBufferSwapCounter()
+{
+ return nBufferSwapCounter;
+}
+
+void OpenGLContext::sync()
+{
+ // default is nothing
+ (void) this; // loplugin:staticmethods
+}
+
+void OpenGLContext::show()
+{
+ if (m_pChildWindow)
+ m_pChildWindow->Show();
+ else if (m_xWindow)
+ m_xWindow->Show();
+}
+
+SystemChildWindow* OpenGLContext::getChildWindow()
+{
+ return m_pChildWindow;
+}
+
+const SystemChildWindow* OpenGLContext::getChildWindow() const
+{
+ return m_pChildWindow;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */