Refactored cvf::OpenGLContext and cvf::OpenGLContextGroup, and added first cut impl of cvfqt::GLWidget and cvfqt::OpenGLWidget

This commit is contained in:
Sigurd Pettersen 2024-01-12 10:07:02 +01:00
parent 4bd1be3763
commit 9b553d6963
19 changed files with 1192 additions and 139 deletions

View File

@ -65,15 +65,18 @@ OpenGLWidget::OpenGLWidget( cvf::OpenGLContextGroup* contextGroup,
cvf::ref<cvf::OpenGLContext> myContext = cvfOpenGLContext();
if ( myContext.notNull() )
{
cvf::OpenGLContextGroup* myOwnerContextGroup = myContext->group();
if ( shareWidget )
{
// We need to check if we actually got a context that shares resources with shareWidget.
cvf::ref<cvf::OpenGLContext> shareContext = shareWidget->cvfOpenGLContext();
CVF_ASSERT(myOwnerContextGroup == shareContext->group());
if ( isSharing() )
{
CVF_ASSERT( myContext->group() == shareContext->group() );
myContext->initializeContext();
makeCurrent();
myOwnerContextGroup->initializeContextGroup(myContext.p());
}
else
{
@ -81,13 +84,14 @@ OpenGLWidget::OpenGLWidget( cvf::OpenGLContextGroup* contextGroup,
// since the construction process above has already optimistically added the new context to the
// existing group. In this case, the newly context is basically defunct so we just shut it down
// (which will also remove it from the group)
myContext->shutdownContext();
myOwnerContextGroup->contextAboutToBeShutdown(myContext.p());
CVF_ASSERT( myContext->group() == nullptr );
}
}
else
{
myContext->initializeContext();
makeCurrent();
contextGroup->initializeContextGroup(myContext.p());
}
}
}
@ -118,13 +122,16 @@ OpenGLWidget::OpenGLWidget( OpenGLWidget* shareWidget, QWidget* parent, Qt::Wind
cvf::ref<cvf::OpenGLContext> myContext = cvfOpenGLContext();
if ( myContext.notNull() )
{
cvf::OpenGLContextGroup* myOwnerContextGroup = myContext->group();
// We need to check if we actually got a context that shares resources with shareWidget.
if ( isSharing() )
{
if ( isValid() )
{
CVF_ASSERT( myContext->group() == shareContext->group() );
myContext->initializeContext();
makeCurrent();
myOwnerContextGroup->initializeContextGroup(myContext.p());
}
}
else
@ -133,7 +140,7 @@ OpenGLWidget::OpenGLWidget( OpenGLWidget* shareWidget, QWidget* parent, Qt::Wind
// the construction process above has already optimistically added the new context to the existing group.
// In this case, the newly context is basically defunct so we just shut it down (which will also remove it
// from the group)
myContext->shutdownContext();
myOwnerContextGroup->contextAboutToBeShutdown(myContext.p());
CVF_ASSERT( myContext->group() == nullptr );
}
}
@ -161,7 +168,14 @@ void OpenGLWidget::cvfShutdownOpenGLContext()
cvf::ref<cvf::OpenGLContext> myContext = cvfOpenGLContext();
if ( myContext.notNull() )
{
myContext->shutdownContext();
cvf::OpenGLContextGroup* myOwnerContextGroup = myContext->group();
// If shutdown has already been called, the context is no longer member of any group
if (myOwnerContextGroup)
{
makeCurrent();
myOwnerContextGroup->contextAboutToBeShutdown(myContext.p());
}
}
}

View File

@ -20,7 +20,9 @@ set(QT_LIBRARIES Qt5::Core Qt5::Gui Qt5::Widgets Qt5::OpenGL)
set(CEE_HEADER_FILES
cvfqtBasicAboutDialog.h
cvfqtGLWidget.h
cvfqtMouseState.h
cvfqtOpenGLWidget.h
cvfqtPerformanceInfoHud.h
cvfqtUtils.h
cvfqtCvfBoundQGLContext_deprecated.h
@ -29,13 +31,16 @@ cvfqtGLWidget_deprecated.h
set(CEE_SOURCE_FILES
cvfqtBasicAboutDialog.cpp
cvfqtGLWidget.cpp
cvfqtMouseState.cpp
cvfqtOpenGLWidget.cpp
cvfqtPerformanceInfoHud.cpp
cvfqtUtils.cpp
cvfqtCvfBoundQGLContext_deprecated.cpp
cvfqtGLWidget_deprecated.cpp
)
add_library(${PROJECT_NAME} ${CEE_HEADER_FILES} ${CEE_SOURCE_FILES})
target_include_directories(${PROJECT_NAME}
@ -57,5 +62,4 @@ source_group("" FILES ${PROJECT_FILES})
# Unity Build
if (CMAKE_UNITY_BUILD)
set_source_files_properties (cvfqtGLWidget_deprecated.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE)
set_source_files_properties (cvfqtOpenGLContext.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION TRUE)
endif()

View File

@ -56,18 +56,9 @@ namespace cvfqt {
///
//--------------------------------------------------------------------------------------------------
OpenGLContext_QGLContextAdapter_deprecated::OpenGLContext_QGLContextAdapter_deprecated(cvf::OpenGLContextGroup* contextGroup, QGLContext* backingQGLContext)
: cvf::OpenGLContext(contextGroup),
m_isCoreOpenGLProfile(false),
m_majorVersion(0),
m_minorVersion(0)
: cvf::OpenGLContext(contextGroup)
{
m_qtGLContext = backingQGLContext;
CVF_ASSERT(m_qtGLContext);
QGLFormat glFormat = m_qtGLContext->format();
m_majorVersion = glFormat.majorVersion();
m_minorVersion = glFormat.minorVersion();
m_isCoreOpenGLProfile = (glFormat.profile() == QGLFormat::CoreProfile) ? true : false;
}
@ -80,26 +71,6 @@ OpenGLContext_QGLContextAdapter_deprecated::~OpenGLContext_QGLContextAdapter_dep
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool OpenGLContext_QGLContextAdapter_deprecated::initializeContext()
{
if (!cvf::OpenGLContext::initializeContext())
{
return false;
}
// Possibly override setting for fixed function support
if (m_isCoreOpenGLProfile)
{
group()->capabilities()->setSupportsFixedFunction(false);
}
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -55,16 +55,11 @@ public:
OpenGLContext_QGLContextAdapter_deprecated(cvf::OpenGLContextGroup* contextGroup, QGLContext* backingQGLContext);
virtual ~OpenGLContext_QGLContextAdapter_deprecated();
virtual bool initializeContext();
virtual void makeCurrent();
virtual bool isCurrent() const;
private:
QGLContext* m_qtGLContext;
bool m_isCoreOpenGLProfile; // This is a Core OpenGL profile. Implies OpenGL version of 3.2 or more
int m_majorVersion; // OpenGL version as reported by Qt
int m_minorVersion;
};

View File

@ -0,0 +1,519 @@
//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2011-2013 Ceetron AS
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:
//
// GNU General Public License Usage
// This library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 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 General Public License at <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
// GNU Lesser General Public License Usage
// 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.1 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 at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
// for more details.
//
//##################################################################################################
#include "cvfqtGLWidget.h"
#include "cvfOpenGLContextGroup.h"
#include "cvfOpenGLContext.h"
#include "cvfLogManager.h"
#include "cvfTrace.h"
#include <QPointer>
#include <QEvent>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QPlatformSurfaceEvent>
namespace cvfqt {
//==================================================================================================
//
//
//
//==================================================================================================
class ForwardingOpenGLContext_GLWidget : public cvf::OpenGLContext
{
public:
ForwardingOpenGLContext_GLWidget(cvf::OpenGLContextGroup* contextGroup, QGLWidget* ownerQtGLWidget)
: cvf::OpenGLContext(contextGroup),
m_ownerQtGLWidget(ownerQtGLWidget)
{
CVF_ASSERT(contextGroup);
// In our current usage pattern the owner widget (and its contained Qt OpenGL context) must already be initialized/created
CVF_ASSERT(m_ownerQtGLWidget);
CVF_ASSERT(m_ownerQtGLWidget->isValid());
CVF_ASSERT(m_ownerQtGLWidget->context());
CVF_ASSERT(m_ownerQtGLWidget->context()->isValid());
}
virtual void makeCurrent()
{
if (m_ownerQtGLWidget)
{
m_ownerQtGLWidget->makeCurrent();
}
}
virtual bool isCurrent() const
{
const QGLContext* ownersQGLContext = m_ownerQtGLWidget ? m_ownerQtGLWidget->context() : NULL;
if (ownersQGLContext && QGLContext::currentContext() == ownersQGLContext)
{
return true;
}
return false;
}
virtual cvf::OglId defaultFramebufferObject() const
{
if (m_ownerQtGLWidget)
{
const QGLContext* ownersQGLContext = m_ownerQtGLWidget->context();
const QOpenGLContext* ownersQOpenGLContext = ownersQGLContext ? ownersQGLContext->contextHandle() : NULL;
return ownersQOpenGLContext ? ownersQOpenGLContext->defaultFramebufferObject() : 0;
}
return 0;
}
private:
QPointer<QGLWidget> m_ownerQtGLWidget;
};
//==================================================================================================
///
/// \class cvfqt::GLWidget
/// \ingroup GuiQt
///
///
///
//==================================================================================================
//--------------------------------------------------------------------------------------------------
/// Constructor, use this for the first or only widget in your application.
//--------------------------------------------------------------------------------------------------
GLWidget::GLWidget(cvf::OpenGLContextGroup* contextGroup, const QGLFormat& format, QWidget* parent, Qt::WindowFlags f)
: QGLWidget(format, parent, NULL, f),
m_cvfOpenGLContextGroup(contextGroup),
m_logger(CVF_GET_LOGGER("cee.cvf.qt"))
{
// Must pass a context group
CVF_ASSERT(m_cvfOpenGLContextGroup.notNull());
// Only the first widget in a context group can be created using this constructor
// All following widgets must be created using the constructor overload that takes a shareWidget
CVF_ASSERT(m_cvfOpenGLContextGroup->contextCount() == 0);
// The isValid() call will return true if the widget has a valid GL rendering context; otherwise returns false.
// The widget will be invalid if the system has no OpenGL support.
if (!isValid())
{
CVF_LOG_ERROR(m_logger, "Widget creation failed, the system has no OpenGL support");
return;
}
// The Qt docs for QGLWidget and all previous experience indicates that it is OK to do OpenGL related
// initialization in QGLWidget's constructor (contrary to QOpenGLWidget).
// Note that the Qt docs still hint that initialization should be deferred to initializeGL(). If we ever
// experience problems related to doing initialization here, we should probably move the initialization.
// Since we're in the constructor we must ensure this widget's context is current before initialization
makeCurrent();
const QGLContext* myQtGLContext = context();
CVF_ASSERT(myQtGLContext);
CVF_ASSERT(myQtGLContext->isValid());
CVF_ASSERT(QGLContext::currentContext() == myQtGLContext);
m_cvfForwardingOpenGLContext = new ForwardingOpenGLContext_GLWidget(m_cvfOpenGLContextGroup.p(), this);
if (!m_cvfOpenGLContextGroup->initializeContextGroup(m_cvfForwardingOpenGLContext.p()))
{
CVF_LOG_ERROR(m_logger, "Error initializing context group");
}
// Install our event filter
installEventFilter(this);
// If we're using Qt5 or above, we can get hold of a QOpenGLContext from our QGLContext
// Connect to QOpenGLContext's aboutToBeDestroyed signal so we get notified when Qt's OpenGL context is about to be destroyed
connect(myQtGLContext->contextHandle(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::qtOpenGLContextAboutToBeDestroyed);
}
//--------------------------------------------------------------------------------------------------
/// Constructor, creates a widget sharing the OpenGL resources with the specified widget.
//--------------------------------------------------------------------------------------------------
GLWidget::GLWidget(GLWidget* shareWidget, QWidget* parent , Qt::WindowFlags f)
: QGLWidget(shareWidget->context()->requestedFormat(), parent, shareWidget, f),
m_logger(CVF_GET_LOGGER("cee.cvf.qt"))
{
// Note that when calling QGLWidget's constructor in the initializer list above, we *must* use the
// constructor overload that takes a format as its first parameter. If we do not do this, the default QGLFormat
// will be used, and it may not be compatible with the format of the shareWidget.
// We grab hold of the format from the shareWidget, but note that we do that by calling requestedFormat()
// instead of just format() to ensure that the format being requested here is the same as the one that was actually used
// in the "first widget constructor" and not whatever format the first widget ended up with after construction.
// Requires that a share widget is passed in as parameter
CVF_ASSERT(shareWidget);
// If the share widget doesn't have a CVF OpenGL context something went wrong when it was created and we cannot continue
cvf::ref<cvf::OpenGLContext> shareContext = shareWidget->cvfOpenGLContext();
CVF_ASSERT(shareContext.notNull());
if (!isValid())
{
CVF_LOG_ERROR(m_logger, "Widget creation failed, the system has no OpenGL support");
return;
}
// We need to check if we actually got a context that shares resources with the passed shareWidget.
if (!isSharing())
{
CVF_LOG_ERROR(m_logger, "Widget creation failed, unable to create a shared OpenGL context");
return;
}
// Since we're in the constructor we must ensure this widget's context is current before initialization
makeCurrent();
const QGLContext* myQtGLContext = context();
CVF_ASSERT(myQtGLContext);
CVF_ASSERT(myQtGLContext->isValid());
CVF_ASSERT(QGLContext::currentContext() == myQtGLContext);
m_cvfOpenGLContextGroup = shareContext->group();
CVF_ASSERT(m_cvfOpenGLContextGroup.notNull());
m_cvfForwardingOpenGLContext = new ForwardingOpenGLContext_GLWidget(m_cvfOpenGLContextGroup.p(), this);
// Normally, the context group should already be initialized when the first widget in the group was created.
// Still, for good measure do an initialization here. It will amount to a no-op if context group is already initialized
if (!m_cvfOpenGLContextGroup->initializeContextGroup(m_cvfForwardingOpenGLContext.p()))
{
CVF_LOG_ERROR(m_logger, "Error initializing context group using sharing widget");
}
// Install our event filter
installEventFilter(this);
// If we're using Qt5 or above, we can get hold of a QOpenGLContext from our QGLContext
// Connect to QOpenGLContext's aboutToBeDestroyed signal so we get notified when Qt's OpenGL context is about to be destroyed
connect(myQtGLContext->contextHandle(), &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::qtOpenGLContextAboutToBeDestroyed);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
GLWidget::~GLWidget()
{
cvf::Trace::show("GLWidget::~GLWidget()");
// Make sure we disconnect from the aboutToBeDestroyed signal since after this destructor has been
// called, out object is dangling and the call to the slot will cause a crash
const QGLContext* myQtGLContext = context();
const QOpenGLContext* myQtOpenGLContext = myQtGLContext ? myQtGLContext->contextHandle() : NULL;
if (myQtOpenGLContext)
{
disconnect(myQtOpenGLContext, &QOpenGLContext::aboutToBeDestroyed, this, &GLWidget::qtOpenGLContextAboutToBeDestroyed);
}
// For Qt5, our testing indicates that once we hit the widget's destructor it may be too late
// to try and do any cleanup of OpenGL resources. Most of the time it works, but typically it will fail when
// we're shutting down the application by closing the main window. In such cases it seems that the OpenGL
// context cannot be made current anymore.
//
// One solution to this is to do an explicit shutdown of the context while before the system
// start shutting down. One traditional way of doing this is to iterate over all GLWidgets and call
// the cvfShutdownOpenGLContext() explicitly, eg from QMainWindow::closeEvent()
//
// In our quest to get a notification just before the QGLContext dies we have also tried to go via
// QGLContext's QOpenGLContext and connect to the aboutToBeDestroyed signal. This signal does indeed trigger
// before the QGLContext dies, but it seems that at this point we are no longer able to make the context
// current which is a requirement before we can do our OpenGL related cleanup.
//
// Another promising solution that seems to work reliably is to install an event filter and respond to
// the QEvent::PlatformSurface event. See our eventFilter() override
// For Qt4 it seems that doing OpenGL related cleanup in the destructor is OK
// We're able to make the context current and delete any OpenGL resources necessary through the cvfShutdownOpenGLContext();
// Note that calling this function is safe even if the context has already been shut down.
// It may however fail/assert if the context hasn't already been shut down and if we're unable to make the OpenGL context current
cvfShutdownOpenGLContext();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::OpenGLContext* GLWidget::cvfOpenGLContext()
{
return m_cvfForwardingOpenGLContext.p();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void GLWidget::cvfShutdownOpenGLContext()
{
if (m_cvfForwardingOpenGLContext.notNull())
{
makeCurrent();
// If we're not able to make the context current, the eventual un-initialize that will happen
// in the context group when we remove the last context will fail.
const QGLContext* myContext = context();
const QGLContext* currContext = QGLContext::currentContext();
if (myContext != currContext)
{
CVF_LOG_WARNING(m_logger, "Could not make the widget's OpenGL context current for context shutdown");
}
m_cvfOpenGLContextGroup->contextAboutToBeShutdown(m_cvfForwardingOpenGLContext.p());
m_cvfForwardingOpenGLContext = NULL;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void GLWidget::initializeGL()
{
//cvf::Trace::show("GLWidget::initializeGL()");
// According to Qt doc this function is called once before the first call to paintGL() or resizeGL(),
// and then once whenever the widget has been assigned a new QGLContext. There is no need to call makeCurrent() because
// this has already been done when this function is called.
if (m_cvfForwardingOpenGLContext.isNull())
{
CVF_LOG_ERROR(m_logger, "Unexpected error in GLWidget::initializeGL(), no forwarding OpenGL context present");
return;
}
// Initialization of context group should already be done, but for good measure
CVF_ASSERT(m_cvfOpenGLContextGroup.notNull());
if (!m_cvfOpenGLContextGroup->isContextGroupInitialized())
{
CVF_LOG_DEBUG(m_logger, "Doing late initialization of context group in GLWidget::initializeGL()");
if (!m_cvfOpenGLContextGroup->initializeContextGroup(m_cvfForwardingOpenGLContext.p()))
{
CVF_LOG_ERROR(m_logger, "Initialization of context group in GLWidget::initializeGL() failed");
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void GLWidget::resizeGL(int /*width*/, int /*height*/)
{
// Intentionally empty, and no implementation in QGLWidget::resizeGL() either.
// Should normally be implemented in derived class
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void GLWidget::paintGL()
{
// No implementation here and no significant implementation in QGLWidget either.
// In Qt4 QGLWidget::paintGL() does nothing. In Qt5 QGLWidget::paintGL() merely clears the depth and color buffer.
// Derived classes must reimplement this function in order to do painting.
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void GLWidget::qtOpenGLContextAboutToBeDestroyed()
{
//cvf::Trace::show("GLWidget::qtOpenGLContextAboutToBeDestroyed()");
cvfShutdownOpenGLContext();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool GLWidget::eventFilter(QObject* watched, QEvent* event)
{
// The most reliable way we have found of detecting when an OpenGL context is about to be destroyed is
// hooking into the QEvent::PlatformSurface event and checking for the SurfaceAboutToBeDestroyed event type.
// From the Qt doc:
// The underlying native surface will be destroyed immediately after this event.
// The SurfaceAboutToBeDestroyed event type is useful as a means of stopping rendering to a platform window before it is destroyed.
if (event->type() == QEvent::PlatformSurface)
{
QPlatformSurfaceEvent* platformSurfaceEvent = static_cast<QPlatformSurfaceEvent*>(event);
if (platformSurfaceEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed)
{
CVF_LOG_DEBUG(m_logger, "Shutting down OpenGL context in response to platform surface about to be destroyed");
cvfShutdownOpenGLContext();
}
}
// Reading the Qt docs it can seem like reparenting of the QGLWidget might pose a problem for us.
// According to the doc, a change of parent will cause the widget's QGLContext to be deleted and a new one
// to be created. In Qt4, this does indeed seem to happen, whereas for Qt5 it does not. Still, it appears that Qt4 makes
// an effort to make the new QGLContext compatible with the old one, and does some tricks to set up temporary OpenGL
// resource sharing so that we may not actually be affected by this since we're actually not storing any references to
// the QGLContext, but accessing it indirectly through the widget in our ForwardingOpenGLContext.
// May also want to look out for
if (event->type() == QEvent::ParentChange)
{
CVF_LOG_DEBUG(m_logger, "cvfqt::GLWidget has been reparented. This may cause OpenGL issues");
}
else if (event->type() == QEvent::ParentAboutToChange)
{
CVF_LOG_DEBUG(m_logger, "cvfqt::GLWidget is about to change parent. This may cause OpenGL issues");
}
return QGLWidget::eventFilter(watched, event);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void GLWidget::logOpenGLInfo()
{
CVF_LOG_INFO(m_logger, "Starting logging of OpenGL info (cvfqt::GLWidget)...");
cvf::String sQtVerInfo = cvf::String("Qt version: %1 (run-time=%2)").arg(QT_VERSION_STR).arg(qVersion());
CVF_LOG_INFO(m_logger, sQtVerInfo);
if (!context() || !isValid())
{
CVF_LOG_WARNING(m_logger, "QGLWidget does not have a valid GL rendering context, reported info will not be correct!");
}
// Log output from querying Qt
CVF_LOG_INFO(m_logger, "Qt OpenGL format info:");
const QGLFormat qglFormat = format();
const QGLFormat::OpenGLContextProfile profile = qglFormat.profile();
const QGLFormat::OpenGLVersionFlags qglVersionFlags = QGLFormat::openGLVersionFlags();
cvf::String sInfo = cvf::String(" info: version %1.%2, depthSize: %3, software: %4, doubleBuffer: %5, sampleBuffers: %6 (size: %7)")
.arg(qglFormat.majorVersion()).arg(qglFormat.minorVersion())
.arg(qglFormat.depthBufferSize())
.arg(qglFormat.directRendering() ? "no" : "yes")
.arg(qglFormat.doubleBuffer() ? "yes" : "no")
.arg(qglFormat.sampleBuffers() ? "yes" : "no")
.arg(qglFormat.samples());
CVF_LOG_INFO(m_logger, sInfo);
cvf::String sProfile = "UNKNOWN";
if (profile == QGLFormat::NoProfile) sProfile = "NoProfile";
else if (profile == QGLFormat::CoreProfile) sProfile = "CoreProfile";
else if (profile == QGLFormat::CompatibilityProfile) sProfile = "CompatibilityProfile";
CVF_LOG_INFO(m_logger, " context profile: " + sProfile);
cvf::String sVersionsPresent = cvf::String(" versions present: 1.1: %1, 2.0: %2, 2.1: %3, 3.0: %4, 3.3: %5, 4.0: %6, ES2: %7")
.arg(qglVersionFlags & QGLFormat::OpenGL_Version_1_1 ? "yes" : "no")
.arg(qglVersionFlags & QGLFormat::OpenGL_Version_2_0 ? "yes" : "no")
.arg(qglVersionFlags & QGLFormat::OpenGL_Version_2_1 ? "yes" : "no")
.arg(qglVersionFlags & QGLFormat::OpenGL_Version_3_0 ? "yes" : "no")
.arg(qglVersionFlags & QGLFormat::OpenGL_Version_3_3 ? "yes" : "no")
.arg(qglVersionFlags & QGLFormat::OpenGL_Version_4_0 ? "yes" : "no")
.arg(qglVersionFlags & QGLFormat::OpenGL_ES_Version_2_0 ? "yes" : "no");
CVF_LOG_INFO(m_logger, sVersionsPresent);
CVF_LOG_INFO(m_logger, " is sharing: " + cvf::String(isSharing() ? "yes" : "no"));
// Log the information we have gathered when initializing the context group
const cvf::OpenGLInfo oglInfo = m_cvfOpenGLContextGroup->info();
CVF_LOG_INFO(m_logger, "OpenGL info:");
CVF_LOG_INFO(m_logger, " version: " + oglInfo.version());
CVF_LOG_INFO(m_logger, " vendor: " + oglInfo.vendor());
CVF_LOG_INFO(m_logger, " renderer: " + oglInfo.renderer());
// Lastly, query OpenGL implementation directly if this context is current
GLint smoothLineWidthRange[2] = { -1, -1 };
GLint smoothPointSizeRange[2] = { -1, -1 };
GLint aliasedLineWidthRange[2] = { -1, -1 };
GLint aliasedPointSizeRange[2] = { -1, -1 };
// Note that GL_LINE_WIDTH_RANGE and GL_SMOOTH_LINE_WIDTH_RANGE are synonyms (0x0B22)
// Likewise for GL_POINT_SIZE_RANGE and GL_SMOOTH_POINT_SIZE_RANGE are synonyms (0x0B12)
#ifndef GL_ALIASED_LINE_WIDTH_RANGE
#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E
#endif
#ifndef GL_ALIASED_POINT_SIZE_RANGE
#define GL_ALIASED_POINT_SIZE_RANGE 0x846D
#endif
const bool thisContextIsCurrent = context() == QGLContext::currentContext();
if (thisContextIsCurrent)
{
glGetIntegerv(GL_LINE_WIDTH_RANGE, smoothLineWidthRange);
glGetIntegerv(GL_POINT_SIZE_RANGE, smoothPointSizeRange);
if (qglVersionFlags & QGLFormat::OpenGL_Version_1_2)
{
glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, aliasedLineWidthRange);
glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, aliasedPointSizeRange);
}
}
else
{
CVF_LOG_WARNING(m_logger, "Cannot query OpenGL directly, QGLWidget's context is not current");
}
cvf::String sLineInfo = cvf::String("OpenGL line width range: aliased lines: %1 to %2, smooth lines: %3 to %4").arg(aliasedLineWidthRange[0]).arg(aliasedLineWidthRange[1]).arg(smoothLineWidthRange[0]).arg(smoothLineWidthRange[1]);
cvf::String sPointInfo = cvf::String("OpenGL point size range: aliased points: %1 to %2, smooth points: %3 to %4").arg(aliasedPointSizeRange[0]).arg(aliasedPointSizeRange[1]).arg(smoothPointSizeRange[0]).arg(smoothPointSizeRange[1]);
CVF_LOG_INFO(m_logger, sLineInfo);
CVF_LOG_INFO(m_logger, sPointInfo);
CVF_LOG_INFO(m_logger, "Finished logging of OpenGL info");
}
} // namespace cvfqt

View File

@ -0,0 +1,86 @@
//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2011-2013 Ceetron AS
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:
//
// GNU General Public License Usage
// This library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 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 General Public License at <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
// GNU Lesser General Public License Usage
// 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.1 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 at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
// for more details.
//
//##################################################################################################
#pragma once
#include "cvfBase.h"
#include "cvfObject.h"
#include <QGLWidget>
namespace cvf {
class OpenGLContext;
class OpenGLContextGroup;
class Logger;
}
namespace cvfqt {
//==================================================================================================
//
//
//
//==================================================================================================
class GLWidget : public QGLWidget
{
public:
GLWidget(cvf::OpenGLContextGroup* contextGroup, const QGLFormat& format, QWidget* parent, Qt::WindowFlags f = Qt::WindowFlags());
GLWidget(GLWidget* shareWidget, QWidget* parent , Qt::WindowFlags f = Qt::WindowFlags());
~GLWidget();
cvf::OpenGLContext* cvfOpenGLContext();
void cvfShutdownOpenGLContext();
void logOpenGLInfo();
protected:
virtual void initializeGL();
virtual void resizeGL(int width, int height);
virtual void paintGL();
virtual bool eventFilter(QObject* watched, QEvent* event);
private:
void qtOpenGLContextAboutToBeDestroyed();
private:
cvf::ref<cvf::OpenGLContextGroup> m_cvfOpenGLContextGroup;
cvf::ref<cvf::OpenGLContext> m_cvfForwardingOpenGLContext;
cvf::ref<cvf::Logger> m_logger;
};
}

View File

@ -68,7 +68,9 @@ GLWidget_deprecated::GLWidget_deprecated(cvf::OpenGLContextGroup* contextGroup,
cvf::ref<cvf::OpenGLContext> myContext = cvfOpenGLContext();
if (myContext.notNull())
{
myContext->initializeContext();
// We must ensure the context is current before initialization
makeCurrent();
contextGroup->initializeContextGroup(myContext.p());
}
}
}
@ -96,13 +98,16 @@ GLWidget_deprecated::GLWidget_deprecated(GLWidget_deprecated* shareWidget, QWidg
cvf::ref<cvf::OpenGLContext> myContext = cvfOpenGLContext();
if (myContext.notNull())
{
cvf::OpenGLContextGroup* myOwnerContextGroup = myContext->group();
CVF_ASSERT(myOwnerContextGroup == shareContext->group());
// We need to check if we actually got a context that shares resources with shareWidget.
if (isSharing())
{
if (isValid())
{
CVF_ASSERT(myContext->group() == shareContext->group());
myContext->initializeContext();
makeCurrent();
myOwnerContextGroup->initializeContextGroup(myContext.p());
}
}
else
@ -110,7 +115,9 @@ GLWidget_deprecated::GLWidget_deprecated(GLWidget_deprecated* shareWidget, QWidg
// If we didn't, we need to remove the newly created context from the group it has been added to since
// the construction process above has already optimistically added the new context to the existing group.
// In this case, the newly context is basically defunct so we just shut it down (which will also remove it from the group)
myContext->shutdownContext();
// Normally, we would need to ensure the context is current before calling shutdown, but in this case we are not the last
// context in the group so there should be no need for cleaning up resources.
myOwnerContextGroup->contextAboutToBeShutdown(myContext.p());
CVF_ASSERT(myContext->group() == NULL);
}
}
@ -140,7 +147,14 @@ void GLWidget_deprecated::cvfShutdownOpenGLContext()
cvf::ref<cvf::OpenGLContext> myContext = cvfOpenGLContext();
if (myContext.notNull())
{
myContext->shutdownContext();
cvf::OpenGLContextGroup* myOwnerContextGroup = myContext->group();
// If shutdown has already been called, the context is no longer member of any group
if (myOwnerContextGroup)
{
makeCurrent();
myOwnerContextGroup->contextAboutToBeShutdown(myContext.p());
}
}
}

View File

@ -0,0 +1,334 @@
//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2011-2013 Ceetron AS
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:
//
// GNU General Public License Usage
// This library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 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 General Public License at <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
// GNU Lesser General Public License Usage
// 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.1 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 at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
// for more details.
//
//##################################################################################################
#include "cvfqtOpenGLWidget.h"
#include "cvfTrace.h"
#include "cvfString.h"
#include "cvfOpenGLContext.h"
#include "cvfLogManager.h"
#include <QOpenGLContext>
#include <QPointer>
#include <QEvent>
#include <QPlatformSurfaceEvent>
namespace cvfqt {
//==================================================================================================
//
//
//
//==================================================================================================
class ForwardingOpenGLContext_OpenGLWidget : public cvf::OpenGLContext
{
public:
ForwardingOpenGLContext_OpenGLWidget(cvf::OpenGLContextGroup* contextGroup, QOpenGLWidget* ownerQtOpenGLWidget)
: cvf::OpenGLContext(contextGroup),
m_ownerQtOpenGLWidget(ownerQtOpenGLWidget)
{
CVF_ASSERT(contextGroup);
// In our current usage pattern the owner widget (and its contained Qt OpenGL context)
// must already be initialized/created
CVF_ASSERT(m_ownerQtOpenGLWidget);
CVF_ASSERT(m_ownerQtOpenGLWidget->isValid());
CVF_ASSERT(m_ownerQtOpenGLWidget->context());
CVF_ASSERT(m_ownerQtOpenGLWidget->context()->isValid());
}
virtual void makeCurrent()
{
if (m_ownerQtOpenGLWidget)
{
m_ownerQtOpenGLWidget->makeCurrent();
}
}
virtual bool isCurrent() const
{
QOpenGLContext* ownersContext = m_ownerQtOpenGLWidget ? m_ownerQtOpenGLWidget->context() : NULL;
if (ownersContext && QOpenGLContext::currentContext() == ownersContext)
{
return true;
}
return false;
}
virtual cvf::OglId defaultFramebufferObject() const
{
if (m_ownerQtOpenGLWidget)
{
return m_ownerQtOpenGLWidget->defaultFramebufferObject();
}
return 0;
}
QOpenGLWidget* ownerQtOpenGLWidget()
{
return m_ownerQtOpenGLWidget;
}
private:
QPointer<QOpenGLWidget> m_ownerQtOpenGLWidget;
};
//==================================================================================================
//
//
//
//==================================================================================================
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
OpenGLWidget::OpenGLWidget(cvf::OpenGLContextGroup* contextGroup, QWidget* parent, Qt::WindowFlags f)
: QOpenGLWidget(parent, f),
m_instanceNumber(-1),
m_initializationState(UNINITIALIZED),
m_cvfOpenGlContextGroup(contextGroup),
m_logger(CVF_GET_LOGGER("cee.cvf.qt"))
{
static int sl_nextInstanceNumber = 0;
m_instanceNumber = sl_nextInstanceNumber++;
CVF_LOG_DEBUG(m_logger, cvf::String("OpenGLWidget[%1]::OpenGLWidget()").arg(m_instanceNumber));
CVF_ASSERT(m_cvfOpenGlContextGroup.notNull());
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
OpenGLWidget::~OpenGLWidget()
{
CVF_LOG_DEBUG(m_logger, cvf::String("OpenGLWidget[%1]::~OpenGLWidget()").arg(m_instanceNumber));
if (m_initializationState == IS_INITIALIZED)
{
// Make sure we disconnect from the aboutToBeDestroyed signal since after this destructor has been
// called, our object is dangling and the call to the slot will cause a crash
QOpenGLContext* myQtOpenGLContext = context();
if (myQtOpenGLContext)
{
disconnect(myQtOpenGLContext, &QOpenGLContext::aboutToBeDestroyed, this, &OpenGLWidget::qtOpenGLContextAboutToBeDestroyed);
}
}
shutdownCvfOpenGLContext();
}
//--------------------------------------------------------------------------------------------------
/// This is where we do the custom initialization needed for the Qt binding to work
///
/// Note that if you re-implement this function in your subclass, you must make
/// sure to call the base class.
//--------------------------------------------------------------------------------------------------
void OpenGLWidget::initializeGL()
{
CVF_LOG_DEBUG(m_logger, cvf::String("OpenGLWidget[%1]::initializeGL()").arg(m_instanceNumber));
if (!isValid())
{
CVF_LOG_WARNING(m_logger, cvf::String("OpenGLWidget[%1]: Widget is not valid for initialization").arg(m_instanceNumber));
}
// According to Qt doc this function is called once before the first call to paintGL() or resizeGL().
// Further it is stated that this widget's QOpenGLContext is already current.
// We should be able to assume that we will only be called with a create, valid and current QOpenGLContext
//
// Note that in some scenarios, such as when a widget get reparented, initializeGL() ends up being called
// multiple times in the lifetime of the widget. In those cases, the widget's associated context is first destroyed
// and then a new one is created. This is then followed by a new call to initializeGL() where all OpenGL resources must get reinitialized.
QOpenGLContext* myQtOpenGLContext = context();
CVF_ASSERT(myQtOpenGLContext);
CVF_ASSERT(myQtOpenGLContext->isValid());
CVF_ASSERT(QOpenGLContext::currentContext() == myQtOpenGLContext);
if (m_initializationState != IS_INITIALIZED)
{
CVF_LOG_DEBUG(m_logger, cvf::String("OpenGLWidget[%1]: Starting internal initialization").arg(m_instanceNumber));
// This should either be the first call or the result of a destroy/recreate cycle
CVF_ASSERT(m_cvfForwardingOpenGlContext.isNull());
// Try and detect the situation where the user is trying to associate incompatible widgets/contexts in the same cvf OpenGLContextGroup
// The assert below will fail if two incompatible OpenGL contexts end up in the same context group
if (m_cvfOpenGlContextGroup->contextCount() > 0)
{
for (size_t i = 0; i < m_cvfOpenGlContextGroup->contextCount(); i++)
{
ForwardingOpenGLContext_OpenGLWidget* existingFwdContext = dynamic_cast<ForwardingOpenGLContext_OpenGLWidget*>(m_cvfOpenGlContextGroup->context(i));
QOpenGLWidget* existingWidget = existingFwdContext ? existingFwdContext->ownerQtOpenGLWidget() : NULL;
QOpenGLContext* existingQtContext = existingWidget ? existingWidget->context() : NULL;
if (existingQtContext)
{
// Assert that these two contexts are actually sharing OpenGL resources
CVF_ASSERT(QOpenGLContext::areSharing(existingQtContext, myQtOpenGLContext));
}
}
}
// Create the actual wrapper/forwarding OpenGL context that implements the cvf::OpenGLContext that we need
cvf::ref<ForwardingOpenGLContext_OpenGLWidget> myCvfContext = new ForwardingOpenGLContext_OpenGLWidget(m_cvfOpenGlContextGroup.p(), this);
// Possibly initialize the context group
if (!m_cvfOpenGlContextGroup->isContextGroupInitialized())
{
if (!m_cvfOpenGlContextGroup->initializeContextGroup(myCvfContext.p()))
{
CVF_LOG_ERROR(m_logger, cvf::String("OpenGLWidget[%1]: OpenGL context creation failed, could not initialize context group").arg(m_instanceNumber));
return;
}
}
// All is well, so store pointer to the cvf OpenGL context
m_cvfForwardingOpenGlContext = myCvfContext;
const InitializationState prevInitState = m_initializationState;
m_initializationState = IS_INITIALIZED;
// Connect to signal so we get notified when Qt's OpenGL context is about to be destroyed
connect(myQtOpenGLContext, &QOpenGLContext::aboutToBeDestroyed, this, &OpenGLWidget::qtOpenGLContextAboutToBeDestroyed);
if (prevInitState == UNINITIALIZED)
{
// Call overridable notification function to indicate that OpenGL has been initialized
// Don't do this if we're being re-initialized
onWidgetOpenGLReady();
}
// Trigger a repaint if we're being re-initialized
if (prevInitState == PENDING_REINITIALIZATION)
{
update();
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void OpenGLWidget::resizeGL(int /*w*/, int /*h*/)
{
// Empty, should normally be implemented in derived class
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void OpenGLWidget::paintGL()
{
// No implementation here (nor in QOpenGLWidget::paintGL())
// Derived classes must re-implement this function in order to do painting.
//
// Typical code would go something like this:
// cvf::OpenGLContext* currentOglContext = cvfOpenGLContext();
// myRenderSequence->render(currentOglContext);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::OpenGLContext* OpenGLWidget::cvfOpenGLContext()
{
return m_cvfForwardingOpenGlContext.p();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
int OpenGLWidget::instanceNumber() const
{
return m_instanceNumber;
}
//--------------------------------------------------------------------------------------------------
/// Notification function that will be called after internal CVF OpenGL initialization has
/// successfully completed. This includes both the creation of the CVF OpenGLContext and
/// successful initialization of the OpenGL context group
///
/// Can be re-implemented in derived classes to take any needed actions.
//--------------------------------------------------------------------------------------------------
void OpenGLWidget::onWidgetOpenGLReady()
{
// Base implementation does nothing
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void OpenGLWidget::qtOpenGLContextAboutToBeDestroyed()
{
CVF_LOG_DEBUG(m_logger, cvf::String("OpenGLWidget[%1]::qtOpenGLContextAboutToBeDestroyed()").arg(m_instanceNumber));
if (m_cvfForwardingOpenGlContext.notNull())
{
CVF_LOG_DEBUG(m_logger, cvf::String("OpenGLWidget[%1]: Shutting down CVF OpenGL context since Qt context is about to be destroyed").arg(m_instanceNumber));
shutdownCvfOpenGLContext();
}
if (m_initializationState == IS_INITIALIZED)
{
m_initializationState = PENDING_REINITIALIZATION;
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void OpenGLWidget::shutdownCvfOpenGLContext()
{
if (m_cvfForwardingOpenGlContext.notNull())
{
makeCurrent();
m_cvfOpenGlContextGroup->contextAboutToBeShutdown(m_cvfForwardingOpenGlContext.p());
m_cvfForwardingOpenGlContext = NULL;
}
}
} // namespace cvfqt

View File

@ -0,0 +1,96 @@
//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2011-2013 Ceetron AS
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:
//
// GNU General Public License Usage
// This library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 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 General Public License at <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
// GNU Lesser General Public License Usage
// 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.1 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 at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
// for more details.
//
//##################################################################################################
#pragma once
#include "cvfBase.h"
#include "cvfObject.h"
#include <QOpenGLWidget>
namespace cvf {
class OpenGLContext;
class OpenGLContextGroup;
class Logger;
}
namespace cvfqt {
//==================================================================================================
//
//
//
//==================================================================================================
class OpenGLWidget : public QOpenGLWidget
{
public:
OpenGLWidget(cvf::OpenGLContextGroup* contextGroup, QWidget* parent, Qt::WindowFlags f = Qt::WindowFlags());
~OpenGLWidget();
cvf::OpenGLContext* cvfOpenGLContext();
protected:
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
int instanceNumber() const;
virtual void onWidgetOpenGLReady();
private:
void qtOpenGLContextAboutToBeDestroyed();
void shutdownCvfOpenGLContext();
private:
enum InitializationState
{
UNINITIALIZED,
PENDING_REINITIALIZATION,
IS_INITIALIZED
};
int m_instanceNumber;
InitializationState m_initializationState;
cvf::ref<cvf::OpenGLContextGroup> m_cvfOpenGlContextGroup;
cvf::ref<cvf::OpenGLContext> m_cvfForwardingOpenGlContext;
cvf::ref<cvf::Logger> m_logger;
};
}

View File

@ -485,8 +485,13 @@ void FramebufferObject::useDefaultWindowFramebuffer(OpenGLContext* oglContext)
{
CVF_CALLSITE_OPENGL(oglContext);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDrawBuffer(GL_BACK);
const OglId defaultFBO = oglContext->defaultFramebufferObject();
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
if (defaultFBO == 0)
{
glDrawBuffer(GL_BACK);
}
CVF_CHECK_OGL(oglContext);
}

View File

@ -59,8 +59,7 @@ namespace cvf {
/// The context will be added unconditionally to the \a contextGroup group
//--------------------------------------------------------------------------------------------------
OpenGLContext::OpenGLContext(OpenGLContextGroup* contextGroup)
: m_contextGroup(contextGroup),
m_isValid(false)
: m_contextGroup(contextGroup)
{
CVF_ASSERT(m_contextGroup);
m_contextGroup->addContext(this);
@ -74,8 +73,6 @@ OpenGLContext::~OpenGLContext()
{
//Trace::show("OpenGLContext destructor");
m_isValid = false;
// Context group is holding references to contexts, so by the time we get to this
// destructor the link to the context group must already be broken
CVF_ASSERT(m_contextGroup == NULL);
@ -87,8 +84,7 @@ OpenGLContext::~OpenGLContext()
//--------------------------------------------------------------------------------------------------
bool OpenGLContext::isContextValid() const
{
// Also check on context group, since it may have been set to NULL after valid flag was set
if (m_contextGroup && m_isValid)
if (m_contextGroup && m_contextGroup->isContextGroupInitialized())
{
return true;
}
@ -102,59 +98,9 @@ bool OpenGLContext::isContextValid() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool OpenGLContext::initializeContext()
OglId OpenGLContext::defaultFramebufferObject() const
{
makeCurrent();
if (!m_contextGroup)
{
return false;
}
if (!m_contextGroup->isContextGroupInitialized())
{
if (!m_contextGroup->initializeContextGroup(this))
{
return false;
}
}
m_isValid = true;
return true;
}
//--------------------------------------------------------------------------------------------------
/// Prepare the context for deletion
///
/// Prepares the context for deletion by removing the context from its context group. If this is the
/// last context in the context group, this function will also try and delete the context group's
/// OpenGL resources.
///
/// \warning After calling this function the context is no longer usable and should be deleted.
/// \warning May try and make the context current in order to free resources if this context
/// is the last context in the context group.
//--------------------------------------------------------------------------------------------------
void OpenGLContext::shutdownContext()
{
if (m_contextGroup)
{
CVF_ASSERT(m_contextGroup->containsContext(this));
// If our ref count is down to 1, there is probably something strange going on.
// Since one reference to us is being held by the context group, this means that the
// caller ISN'T holding a reference which may give him a nast surprise when this function returns!
CVF_ASSERT(refCount() > 1);
// This call will remove the context from the group AND clean up resources in the
// group if we are the last context in the group
m_contextGroup->contextAboutToBeShutdown(this);
}
CVF_ASSERT(m_contextGroup == NULL);
m_isValid = false;
return 0;
}

View File

@ -39,6 +39,7 @@
#include "cvfObject.h"
#include "cvfOpenGLContextGroup.h"
#include "cvfOpenGLTypes.h"
namespace cvf {
@ -58,11 +59,10 @@ public:
virtual ~OpenGLContext();
bool isContextValid() const;
virtual bool initializeContext();
virtual void shutdownContext();
virtual void makeCurrent() = 0;
virtual bool isCurrent() const = 0;
virtual OglId defaultFramebufferObject() const;
OpenGLContextGroup* group();
const OpenGLContextGroup* group() const;
@ -71,7 +71,6 @@ public:
private:
OpenGLContextGroup* m_contextGroup; // Raw pointer (to avoid circular reference) to the context group that this context belongs to.
bool m_isValid; // Will be set to true after successful initialization
friend class OpenGLContextGroup;
};

View File

@ -42,6 +42,7 @@
#include "cvfOpenGLResourceManager.h"
#include "cvfOpenGLCapabilities.h"
#include "cvfLogManager.h"
#include "cvfTrace.h"
#include <memory.h>
@ -75,6 +76,8 @@ OpenGLContextGroup::OpenGLContextGroup()
m_glewContextStruct(NULL),
m_wglewContextStruct(NULL)
{
Trace::show("OpenGLContextGroup constructor");
m_resourceManager = new OpenGLResourceManager;
m_logger = CVF_GET_LOGGER("cee.cvf.OpenGL");
m_capabilities = new OpenGLCapabilities;
@ -86,7 +89,7 @@ OpenGLContextGroup::OpenGLContextGroup()
//--------------------------------------------------------------------------------------------------
OpenGLContextGroup::~OpenGLContextGroup()
{
//Trace::show("OpenGLContextGroup destructor");
Trace::show("OpenGLContextGroup destructor");
// Our resource manager must already be clean
// There is no way of safely deleting the resources after all the contexts have been removed
@ -129,6 +132,8 @@ bool OpenGLContextGroup::isContextGroupInitialized() const
//--------------------------------------------------------------------------------------------------
bool OpenGLContextGroup::initializeContextGroup(OpenGLContext* currentContext)
{
Trace::show("OpenGLContextGroup::initializeContextGroup()");
CVF_ASSERT(currentContext);
CVF_ASSERT(currentContext->isCurrent());
CVF_ASSERT(containsContext(currentContext));
@ -139,20 +144,27 @@ bool OpenGLContextGroup::initializeContextGroup(OpenGLContext* currentContext)
if (!initializeGLEW(currentContext))
{
CVF_LOG_ERROR(m_logger.p(), "Failed to intitialize GLEW in context group");
return false;
}
configureCapablititesFromGLEW(currentContext);
configureCapabilitiesFromGLEW(currentContext);
#ifdef WIN32
if (!initializeWGLEW(currentContext))
{
CVF_LOG_ERROR(m_logger.p(), "Failed to intitialize WGLEW in context group");
return false;
}
#endif
#endif
CVF_LOG_DEBUG(m_logger.p(), "OpenGL initialized in context group");
CVF_LOG_DEBUG(m_logger.p(), " version: " + m_info.version());
CVF_LOG_DEBUG(m_logger.p(), " vendor: " + m_info.vendor());
CVF_LOG_DEBUG(m_logger.p(), " renderer: " + m_info.renderer());
m_isInitialized = true;
}
@ -165,10 +177,12 @@ bool OpenGLContextGroup::initializeContextGroup(OpenGLContext* currentContext)
//--------------------------------------------------------------------------------------------------
void OpenGLContextGroup::uninitializeContextGroup()
{
Trace::show("OpenGLContextGroup::uninitializeContextGroup()");
CVF_ASSERT(m_contexts.empty());
CVF_ASSERT(!m_resourceManager->hasAnyOpenGLResources());
// Just replace capablities with a new object
// Just replace capabilities with a new object
m_capabilities = new OpenGLCapabilities;
#ifdef CVF_USE_GLEW
@ -188,26 +202,28 @@ void OpenGLContextGroup::uninitializeContextGroup()
//--------------------------------------------------------------------------------------------------
/// Called by OpenGLContext objects when they are being shut down
///
/// This function will remove the context \a contextToShutdown from the context group.
/// If \a contextToShutdown is the last context in the group, all resources in the group's
/// This function will remove the context \a currentContextToShutdown from the context group.
/// If \a currentContextToShutdown is the last context in the group, all resources in the group's
/// resource manager will be deleted and the context group will be reset to uninitialized.
///
/// \warning This function may try and make the context passed in \a contextToShutdown curreent!
/// \warning The passed context must be the current OpenGL context!
/// \warning After calling this function the context is no longer usable and should be deleted.
//--------------------------------------------------------------------------------------------------
void OpenGLContextGroup::contextAboutToBeShutdown(OpenGLContext* contextToShutdown)
void OpenGLContextGroup::contextAboutToBeShutdown(OpenGLContext* currentContextToShutdown)
{
CVF_ASSERT(contextToShutdown);
CVF_ASSERT(containsContext(contextToShutdown));
Trace::show("OpenGLContextGroup::contextAboutToBeShutdown()");
CVF_ASSERT(currentContextToShutdown);
CVF_ASSERT(containsContext(currentContextToShutdown));
// If this is the last context in the group, we'll delete all the OpenGL resources in the s resource manager before we go
bool shouldUninitializeGroup = false;
if (contextCount() == 1)
{
CVF_ASSERT(m_resourceManager.notNull());
if (m_resourceManager->hasAnyOpenGLResources() && contextToShutdown->isContextValid())
if (m_resourceManager->hasAnyOpenGLResources() && currentContextToShutdown->isContextValid())
{
contextToShutdown->makeCurrent();
m_resourceManager->deleteAllOpenGLResources(contextToShutdown);
m_resourceManager->deleteAllOpenGLResources(currentContextToShutdown);
}
shouldUninitializeGroup = true;
@ -217,12 +233,12 @@ void OpenGLContextGroup::contextAboutToBeShutdown(OpenGLContext* contextToShutdo
// Since one reference to the context is being held by this context group, this means that the
// caller isn't holding a reference to the context. In this case the context object will evaporate
// during the call below, and the caller will most likely get a nasty surprise
CVF_ASSERT(contextToShutdown->refCount() > 1);
CVF_ASSERT(currentContextToShutdown->refCount() > 1);
// Make sure we set the back pointer before removing it from our collection
// since the removal from the list may actually trigger destruction of the context (see comment above)
contextToShutdown->m_contextGroup = NULL;
m_contexts.erase(contextToShutdown);
currentContextToShutdown->m_contextGroup = NULL;
m_contexts.erase(currentContextToShutdown);
if (shouldUninitializeGroup)
{
@ -239,11 +255,23 @@ void OpenGLContextGroup::contextAboutToBeShutdown(OpenGLContext* contextToShutdo
//--------------------------------------------------------------------------------------------------
bool OpenGLContextGroup::initializeGLEW(OpenGLContext* currentContext)
{
Trace::show("OpenGLContextGroup::initializeGLEW()");
CVF_ASSERT(currentContext);
CVF_ASSERT(m_glewContextStruct == NULL);
#ifdef CVF_USE_GLEW
// Usage of GLEW requires that we have a standard OpenGL implementation available (not any Qt wrapper etc)
// Supposedly the version string should always contain at least one '.'
// Try and test this by querying the OpenGL version number and assume that there is no OpenGL available if it fails
const String sVersion(reinterpret_cast<const char*>(glGetString(GL_VERSION)));
if (sVersion.find(".") == String::npos)
{
CVF_LOG_ERROR(m_logger, "Error initializing OpenGL functions, probe for OpenGL version failed. No valid OpenGL context is current");
return false;
}
// Since we're sometimes (when using Core OpenGL) seeing some OGL errors from GLEW, check before and after call to help find them
CVF_CHECK_OGL(currentContext);
@ -253,6 +281,7 @@ bool OpenGLContextGroup::initializeGLEW(OpenGLContext* currentContext)
GLenum err = glewContextInit(theContextStruct);
if (err != GLEW_OK)
{
CVF_LOG_ERROR(m_logger, String("Error initializing GLEW, glewContextInit() returned %1").arg(err));
delete theContextStruct;
return false;
}
@ -261,6 +290,10 @@ bool OpenGLContextGroup::initializeGLEW(OpenGLContext* currentContext)
CVF_CHECK_OGL(currentContext);
String sVendor(reinterpret_cast<const char*>(glGetString(GL_VENDOR)));
String sRenderer(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
m_info.setOpenGLStrings(sVersion, sVendor, sRenderer);
#else
CVF_FAIL_MSG("Not implemented");
@ -315,7 +348,7 @@ bool OpenGLContextGroup::initializeWGLEW(OpenGLContext* currentContext)
///
/// \warning The passed context must be current and GLEW must already be initialized!
//--------------------------------------------------------------------------------------------------
void OpenGLContextGroup::configureCapablititesFromGLEW(OpenGLContext* currentContext)
void OpenGLContextGroup::configureCapabilitiesFromGLEW(OpenGLContext* currentContext)
{
#ifdef CVF_USE_GLEW
CVF_CALLSITE_GLEW(currentContext);
@ -362,6 +395,15 @@ size_t OpenGLContextGroup::contextCount() const
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
cvf::OpenGLContext* OpenGLContextGroup::context(size_t index)
{
return m_contexts.at(index);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@ -432,6 +474,15 @@ OpenGLCapabilities* OpenGLContextGroup::capabilities()
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
OpenGLInfo OpenGLContextGroup::info() const
{
return m_info;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------

View File

@ -40,6 +40,8 @@
#include "cvfObject.h"
#include "cvfCollection.h"
#include "cvfLogger.h"
#include "cvfOpenGLCapabilities.h"
#include "cvfOpenGLInfo.h"
struct GLEWContextStruct;
struct WGLEWContextStruct;
@ -48,7 +50,6 @@ namespace cvf {
class OpenGLContext;
class OpenGLResourceManager;
class OpenGLCapabilities;
//==================================================================================================
@ -63,24 +64,27 @@ public:
virtual ~OpenGLContextGroup();
bool isContextGroupInitialized() const;
bool initializeContextGroup(OpenGLContext* currentContext);
void contextAboutToBeShutdown(OpenGLContext* currentContextToShutdown);
size_t contextCount() const;
OpenGLContext* context(size_t index);
bool containsContext(const OpenGLContext* context) const;
OpenGLResourceManager* resourceManager();
Logger* logger();
OpenGLCapabilities* capabilities();
OpenGLInfo info() const;
GLEWContextStruct* glewContextStruct();
WGLEWContextStruct* wglewContextStruct();
private:
bool initializeContextGroup(OpenGLContext* currentContext);
void uninitializeContextGroup();
void contextAboutToBeShutdown(OpenGLContext* contextToShutdown);
bool initializeGLEW(OpenGLContext* currentContext);
bool initializeWGLEW(OpenGLContext* currentContext);
void configureCapablititesFromGLEW(OpenGLContext* currentContext);
void configureCapabilitiesFromGLEW(OpenGLContext* currentContext);
void addContext(OpenGLContext* contextToAdd);
private:
@ -89,6 +93,7 @@ private:
ref<OpenGLResourceManager> m_resourceManager; // Resource manager that is shared between all contexts in this group
ref<Logger> m_logger;
ref<OpenGLCapabilities> m_capabilities; // Capabilities of the contexts in this group context
OpenGLInfo m_info;
GLEWContextStruct* m_glewContextStruct; // Pointer to the GLEW context struct
WGLEWContextStruct* m_wglewContextStruct; // Pointer to the GLEW context struct

View File

@ -1,7 +1,7 @@
//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2018 Ceetron AS
// Copyright (C) 2011-2013 Ceetron AS
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:

View File

@ -1,7 +1,7 @@
//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2018 Ceetron AS
// Copyright (C) 2011-2013 Ceetron AS
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:

View File

@ -119,7 +119,9 @@ bool Win32OpenGLContext::createHardwareContext(HWND hWnd)
m_hDC = hDC;
m_hRC = hRC;
if (initializeContext())
cvf::OpenGLContextGroup* ownerContextGroup = group();
CVF_ASSERT(ownerContextGroup);
if (ownerContextGroup->initializeContextGroup(this))
{
return true;
}
@ -136,7 +138,11 @@ bool Win32OpenGLContext::createHardwareContext(HWND hWnd)
void Win32OpenGLContext::shutdownContext()
{
// Clears up resources and removes us from our group
cvf::OpenGLContext::shutdownContext();
cvf::OpenGLContextGroup* ownerContextGroup = group();
CVF_ASSERT(ownerContextGroup);
makeCurrent();
ownerContextGroup->contextAboutToBeShutdown(this);
if (m_hRC)
{
@ -165,7 +171,14 @@ void Win32OpenGLContext::makeCurrent()
{
if (m_hDC && m_hRC)
{
wglMakeCurrent(m_hDC, m_hRC);
HGLRC currRCBefore = wglGetCurrentContext();
if (currRCBefore != m_hRC)
{
wglMakeCurrent(m_hDC, m_hRC);
HGLRC currRCAfter = wglGetCurrentContext();
CVF_ASSERT(currRCAfter = m_hRC);
CVF_UNUSED(currRCAfter);
}
}
}
@ -177,7 +190,8 @@ bool Win32OpenGLContext::isCurrent() const
{
if (m_hDC && m_hRC)
{
if (wglGetCurrentContext() == m_hRC)
HGLRC currRC = wglGetCurrentContext();
if (currRC == m_hRC)
{
return true;
}

View File

@ -52,7 +52,7 @@ public:
~Win32OpenGLContext();
bool createHardwareContext(HWND hWnd);
virtual void shutdownContext();
void shutdownContext();
virtual void makeCurrent();
virtual bool isCurrent() const;

View File

@ -91,7 +91,7 @@ TEST(OpenGLContextGroupTest, LifeCycle)
EXPECT_EQ(2, ctx2->refCount());
EXPECT_EQ(2, ctx3->refCount());
ctx1->shutdownContext();
grp->contextAboutToBeShutdown(ctx1.p());
EXPECT_EQ(1, ctx1->refCount());
EXPECT_TRUE(ctx1->group() == NULL);
EXPECT_FALSE(grp->containsContext(ctx1.p()));