496 lines
12 KiB
C++
496 lines
12 KiB
C++
/* -*- 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: */
|