/* -*- 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: */
↑ V1053 Calling the 'isCurrent' virtual function indirectly in the destructor may lead to unexpected result at runtime. Check lines: 'OpenGLContext.cxx:73', 'OpenGLContext.cxx:342', 'OpenGLContext.hxx:68'.