The expiration time for new job artifacts in CI/CD pipelines is now 30 days (GitLab default). Previously generated artifacts in already completed jobs will not be affected by the change. The latest artifacts for all jobs in the latest successful pipelines will be kept. More information: https://gitlab.lrz.de/help/user/admin_area/settings/continuous_integration.html#default-artifacts-expiration

Commit 5b20d90c authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Moved Qt dependency to Qt5:

* Updated all CMake scripts to use Qt5
* CampvisApplication takes care of moving the QGLContext thread affinity to the threads that do the rendering.
* QtCanvas:resize() is called through Qt signalling to ensure being in GUI thread.
* Added init and deinit functions to the Runnable interface.
* minimum required CMake version is now 3.0

refs #249
parent 151a2cf3
......@@ -105,15 +105,9 @@ EXPORT(PACKAGE CAMPVis)
# = Copy Windows DLLs to binary dir for improved development experience =======
IF(WIN32)
# gather Qt4 DLLs - TODO: remove redundant naming of required components
FIND_PACKAGE(Qt4DLLs COMPONENTS QtCore QtGui QtOpenGL QtNetwork QtXmlPatterns)
LIST(APPEND CampvisExternalDllsDebug ${QT_DEBUG_DLLS})
LIST(APPEND CampvisExternalDllsRelease ${QT_RELEASE_DLLS})
LIST(REMOVE_DUPLICATES CampvisExternalDllsDebug)
LIST(REMOVE_DUPLICATES CampvisExternalDllsRelease)
IF(CAMPVIS_COPY_EXTERNAL_DLLS)
LIST(REMOVE_DUPLICATES CampvisExternalDllsDebug)
LIST(REMOVE_DUPLICATES CampvisExternalDllsRelease)
COPY_EXTERNAL_DLLS(CampvisExternalDllsDebug CampvisExternalDllsRelease false)
ENDIF()
ENDIF()
......
......@@ -106,13 +106,7 @@ FOREACH(ModFile ${CampvisModulesApplicationToBeMocced})
LIST(APPEND CampvisApplicationToBeMocced ${ModFileRelative})
ENDFOREACH()
#
# Qt related stuff:
#
QT4_WRAP_CPP(CampvisApplicationMoc ${CampvisApplicationToBeMocced})
LIST(APPEND CampvisApplicationSources ${CampvisApplicationMoc})
QT4_WRAP_UI(CampvisApplicationFormsHeaders ${CampvisApplicationForms})
QT5_WRAP_UI(CampvisApplicationFormsHeaders ${CampvisApplicationForms})
LIST(APPEND CampvisApplicationSources ${CampvisApplicationFormsHeaders})
LINK_DIRECTORIES(${CampvisGlobalLinkDirectories} ${CampvisModulesLinkDirectories})
......@@ -127,14 +121,16 @@ ADD_LIBRARY(campvis-application-lib
${CampvisApplicationSources} ${CampvisApplicationHeaders}
${CampvisApplicationMoc}
)
ADD_DEFINITIONS(${CampvisGlobalDefinitions} ${CampvisModulesDefinitions} ${CampvisApplicationDefinitions} ${QT_DEFINITIONS})
ADD_DEFINITIONS(${CampvisGlobalDefinitions} ${CampvisModulesDefinitions} ${CampvisApplicationDefinitions})
INCLUDE_DIRECTORIES(${CampvisGlobalIncludeDirs} ${CampvisModulesIncludeDirs})
TARGET_LINK_LIBRARIES(campvis-application-lib ${CampvisMainLibs} ${CampvisGlobalExternalLibs} ${CampvisModulesExternalLibs} ${QT_LIBRARIES})
TARGET_LINK_LIBRARIES(campvis-application-lib ${CampvisMainLibs} ${CampvisGlobalExternalLibs} ${CampvisModulesExternalLibs})
qt5_use_modules(campvis-application-lib Widgets OpenGL)
# if campvis-core is built as a shared library, CMake will define the following flag to instruct
# the code to export DLL symbols
SET_TARGET_PROPERTIES(campvis-application-lib PROPERTIES DEFINE_SYMBOL "CAMPVIS_APPLICATION_BUILD_DLL")
IF(CAMPVIS_GROUP_SOURCE_FILES)
DEFINE_SOURCE_GROUPS_FROM_SUBDIR(CampvisApplicationSources ${CampvisHome} "")
DEFINE_SOURCE_GROUPS_FROM_SUBDIR(CampvisApplicationHeaders ${CampvisHome} "")
......@@ -144,8 +140,8 @@ INSTALL(TARGETS campvis-application-lib DESTINATION exports EXPORT campvis-targe
ADD_EXECUTABLE(campvis-application "campvis.cpp")
TARGET_LINK_LIBRARIES(campvis-application campvis-application-lib ${CampvisMainLibs} ${CampvisGlobalExternalLibs} ${CampvisModulesExternalLibs} ${QT_LIBRARIES})
TARGET_LINK_LIBRARIES(campvis-application campvis-application-lib ${CampvisMainLibs} ${CampvisGlobalExternalLibs} ${CampvisModulesExternalLibs})
qt5_use_modules(campvis-application Widgets OpenGL)
IF(CAMPVIS_DEPLOY_SHADERS)
......
......@@ -52,6 +52,7 @@
#include "tools/qtjobprocessor.h"
#include <QApplication>
#include <QThread>
#ifdef CAMPVIS_HAS_SCRIPTING
#include "scripting/glue/luavmstate.h"
......@@ -93,12 +94,11 @@ namespace campvis {
#endif
}
_localContext = new QtThreadedCanvas("", cgt::ivec2(16, 16));
_localContext = new cgt::QtThreadedCanvas("", cgt::ivec2(16, 16));
campvis::init(_localContext, searchPaths);
_mainWindow = new MainWindow(this);
cgt::GLContextScopedLock lock(_localContext);
{
GLJobProc.enqueueJobBlocking([&]() {
_mainWindow->init();
// load textureData from file
......@@ -133,7 +133,7 @@ namespace campvis {
if (! _luaVmState->injectGlobalObjectPointer(this, "campvis::CampVisApplication *", "application"))
LERROR("Could not inject the pipeline into the Lua VM.");
#endif
}
});
// parse argument list and create pipelines
QStringList pipelinesToAdd = this->arguments();
......@@ -185,6 +185,12 @@ namespace campvis {
for (auto it = _workflows.begin(); it != _workflows.end(); ++it)
(*it)->deinit();
// Stop the OpenGLJobProcessor and pass Qt thread affinity back to this thread.
QThread* mainThread = QThread::currentThread();
GLJobProc.stop([&] () {
this->_localContext->context()->moveToThread(mainThread);
});
{
// Deinit everything OpenGL related using the local context.
cgt::GLContextScopedLock lock(_localContext);
......@@ -255,7 +261,8 @@ namespace campvis {
GLCtxtMgr.releaseContext(canvas, false);
s_PipelinesChanged.emitSignal();
pipeline->start();
startOpenGlThreadAndMoveQtThreadAffinity(pipeline, canvas);
}
void CampVisApplication::initGlContextAndPipeline(cgt::GLCanvas* canvas, AbstractPipeline* pipeline) {
......@@ -286,7 +293,7 @@ namespace campvis {
void CampVisApplication::rebuildAllShadersFromFiles() {
// rebuilding all shaders has to be done from OpenGL context, use the local one.
GLJobProc.enqueueJob(makeJobOnHeap(this, &CampVisApplication::triggerShaderRebuild));
GLJobProc.enqueueJob(cgt::makeJobOnHeap(this, &CampVisApplication::triggerShaderRebuild));
}
void CampVisApplication::triggerShaderRebuild() {
......
......@@ -38,6 +38,7 @@
namespace cgt {
class GLCanvas;
class QtCanvas;
class QtThreadedCanvas;
class Texture;
}
......@@ -170,7 +171,7 @@ namespace campvis {
void triggerShaderRebuild();
/// A local OpenGL context used for initialization
cgt::GLCanvas* _localContext;
cgt::QtCanvas* _localContext;
/// Main window hosting GUI stuff
MainWindow* _mainWindow;
......
......@@ -70,7 +70,6 @@ namespace campvis {
static_cast<Geometry1DTransferFunction*>(p_transferFunction.getTF())->addGeometry(TFGeometry1D::createQuad(cgt::vec2(0.f, 1.f), cgt::col4(0, 0, 0, 255), cgt::col4(255, 255, 255, 255)));
GLCtxtMgr.registerContextAndInitGlew(this, "DataContainerInspector");
GLCtxtMgr.releaseContext(this, false);
addProperty(p_currentSlice);
addProperty(p_transferFunction);
......@@ -103,10 +102,12 @@ namespace campvis {
p_renderGChannel.setVisible(false);
p_renderBChannel.setVisible(false);
p_renderAChannel.setVisible(false);
init();
}
DataContainerInspectorCanvas::~DataContainerInspectorCanvas() {
deinit();
}
void DataContainerInspectorCanvas::init() {
......@@ -157,16 +158,17 @@ namespace campvis {
_geometriesDirty = false;
}
if (_textures.empty()) {
return;
}
glPushAttrib(GL_ALL_ATTRIB_BITS);
glViewport(0, 0, size_.x, size_.y);
glClearColor(0.7f, 0.7f, 0.7f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
LGL_ERROR;
if (_textures.empty()) {
glPopAttrib();
return;
}
// update layout dimensions
_numTiles.x = ceil(sqrt(static_cast<float>(_textures.size())));
_numTiles.y = ceil(static_cast<float>(_textures.size()) / _numTiles.x);
......@@ -252,13 +254,17 @@ namespace campvis {
void DataContainerInspectorCanvas::invalidate() {
// only if inited
if (_quad != 0 && _paintShader != 0) {
this->makeCurrent();
paint();
this->swap();
// avoid recursive paints.
if (! cgt::GlContextManager::getRef().checkWhetherThisThreadHasAcquiredOpenGlContext()) {
SimpleJobProc.enqueueJob([this] () {
cgt::GLContextScopedLock lock(this);
paint();
});
}
// if (! cgt::GlContextManager::getRef().checkWhetherThisThreadHasAcquiredOpenGlContext()) {
// SimpleJobProc.enqueueJob(
// [this] () {
// cgt::GLContextScopedLock lock(this);
// paint();
// });
// }
}
}
......
......@@ -146,14 +146,6 @@ namespace campvis {
*/
virtual void paint();
/**
* Gets called when the data collection of this pipeline has changed and thus has notified its observers.
* If \a name equals the name of the renderTarget, the s_renderTargetChanged signal will be emitted.
* \param name Name of the added data.
* \param dh DataHandle to the newly added data.
*/
void onDataContainerDataAdded(const std::string& name, DataHandle dh);
/**
* Slot getting called when one of the observed properties changed and notifies its observers.
* \param prop Property that emitted the signal
......
......@@ -386,17 +386,10 @@ namespace campvis {
}
void DataContainerInspectorWidget::init() {
if (_canvas != 0)
_canvas->init();
_inited = true;
}
void DataContainerInspectorWidget::deinit() {
_inited = false;
if (_canvas != 0)
_canvas->deinit();
_pcWidget->updatePropCollection(0, 0);
if (_dataContainer != 0) {
......@@ -408,6 +401,8 @@ namespace campvis {
if(_propEditorWid != nullptr)
_propEditorWid->deinit();
_inited = false;
}
void DataContainerInspectorWidget::onDCTWidgetSelectionModelSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) {
......
......@@ -435,8 +435,6 @@ namespace campvis {
}
void MainWindow::onBtnProcessorFactoryClicked() {
cgt::OpenGLJobProcessor::ScopedSynchronousGlJobExecution jobGuard;
std::string name = this->_cbProcessorFactory->currentText().toStdString();
if (_selectedPipeline == nullptr)
return;
......@@ -447,9 +445,11 @@ namespace campvis {
if (p == nullptr)
return;
p->init();
_selectedPipeline->addProcessor(p);
GLJobProc.enqueueJobBlocking([&]() {
p->init();
});
_selectedPipeline->addProcessor(p);
emit updatePipelineWidget(_application->_dataContainers, _application->_pipelines);
}
......
......@@ -44,7 +44,7 @@ namespace campvis {
_logDisplay, SLOT(append(const QString&)));
}
void QTextEditLog::logFiltered(const std::string& cat, LogLevel level, const std::string& msg,
void QTextEditLog::logFiltered(const std::string& cat, cgt::LogLevel level, const std::string& msg,
const std::string& /*extendedInfo*/)
{
std::string output = "";
......
......@@ -33,14 +33,12 @@
#include <QTextEdit>
#include <QTextDocument>
using namespace cgt;
namespace campvis {
/**
* QTextEditLog implements logging to a QTextEdit instance.
*/
class CAMPVIS_APPLICATION_API QTextEditLog : private QObject, public Log {
class CAMPVIS_APPLICATION_API QTextEditLog : private QObject, public cgt::Log {
Q_OBJECT
......@@ -77,7 +75,7 @@ namespace campvis {
protected:
QTextEdit* _logDisplay;
void logFiltered(const std::string &cat, LogLevel level, const std::string &msg, const std::string &extendedInfo="");
void logFiltered(const std::string &cat, cgt::LogLevel level, const std::string &msg, const std::string &extendedInfo="");
};
}
......
# Try to find Win32 Qt DLLs to be copied to the bin directory. Once done this will define:
# QT_DLLS_FOUND
# QT_DEBUG_DLLS
# QT_RELEASE_DLLS
#
# Note: FIND_PACKAGE(Qt4 ...) has to be called before in order for ${QT_LIBRARY_DIR}
# and ${QT_BINARY_DIR} to be defined!
# DLLs are either located in Qt library or binary directory
IF(EXISTS ${QT_LIBRARY_DIR}/QtCored4.dll)
SET(DLL_DIR ${QT_LIBRARY_DIR})
ELSEIF(EXISTS ${QT_BINARY_DIR}/QtCored4.dll)
SET(DLL_DIR ${QT_BINARY_DIR})
ELSEIF(EXISTS ${QT_LIBRARY_DIR}/QtCore4.dll)
SET(DLL_DIR ${QT_LIBRARY_DIR})
ELSEIF(EXISTS ${QT_BINARY_DIR}/QtCore4.dll)
SET(DLL_DIR ${QT_BINARY_DIR})
ENDIF()
IF(DLL_DIR)
SET(QT_DLLS_FOUND TRUE)
FOREACH(component ${Qt4DLLs_FIND_COMPONENTS})
IF(EXISTS ${DLL_DIR}/${component}d4.dll)
LIST(APPEND QT_DEBUG_DLLS ${DLL_DIR}/${component}d4.dll)
ELSE()
MESSAGE("Failed to find Qt Debug DLL: ${component}d4.dll")
SET(QT_DLLS_FOUND FALSE)
ENDIF()
IF(EXISTS ${DLL_DIR}/${component}4.dll)
LIST(APPEND QT_RELEASE_DLLS ${DLL_DIR}/${component}4.dll)
ELSE()
MESSAGE(WARNING "Failed to find Qt Release DLL: ${component}4.dll")
SET(QT_DLLS_FOUND FALSE)
ENDIF()
ENDFOREACH()
ELSE()
SET(QT_DLLS_FOUND FALSE)
ENDIF()
UNSET(Qt4DLLsCAMPVIS_DIR)
MARK_AS_ADVANCED(Qt4DLLsCAMPVIS_DIR)
......@@ -196,10 +196,19 @@ ENDIF()
# minimum Qt version
IF(CAMPVIS_BUILD_APPLICATION)
FIND_PACKAGE(Qt4 ${CampvisRequiredQtVersion} REQUIRED QtCore QtGui QtOpenGL)
INCLUDE(${QT_USE_FILE})
SET(CampvisRequiredQtVersion "5.0")
# Tell CMake to run moc when necessary:
SET(CMAKE_AUTOMOC ON)
# As moc files are generated in the binary dir, tell CMake to always look for includes there:
SET(CMAKE_INCLUDE_CURRENT_DIR ON)
FIND_PACKAGE(Qt5Widgets REQUIRED)
FIND_PACKAGE(Qt5OpenGL REQUIRED)
RESOLVE_QT5_DLL_LOCATIONS("Core;Gui;OpenGL;Widgets")
SET(CGT_WITH_QT true)
SET(CampvisRequiredQtVersion "4.8")
ENDIF(CAMPVIS_BUILD_APPLICATION)
......
......@@ -263,6 +263,16 @@ MACRO(LIST_SUBDIRECTORIES Result Directory AbsolutePath)
LIST(SORT ${Result})
ENDMACRO(LIST_SUBDIRECTORIES)
# Resolve Qt5 DLL locations
MACRO(RESOLVE_QT5_DLL_LOCATIONS Components)
FOREACH(component ${Components})
GET_TARGET_PROPERTY(DebugLocation "Qt5::${component}" LOCATION_DEBUG)
GET_TARGET_PROPERTY(ReleaseLocation "Qt5::${component}" LOCATION)
LIST(APPEND CampvisExternalDllsDebug ${DebugLocation})
LIST(APPEND CampvisExternalDllsRelease ${ReleaseLocation})
ENDFOREACH()
ENDMACRO(RESOLVE_QT5_DLL_LOCATIONS)
# copies the passed debug and release DLLs to bin/Debug and bin/Release, resp.
MACRO(COPY_EXTERNAL_DLLS DebugDLLs ReleaseDLLs failOnError)
MESSAGE(STATUS "* Copying external DLLs")
......
......@@ -41,8 +41,10 @@ namespace campvis {
return nullptr;
}
cgt::OpenGLJobProcessor::ScopedSynchronousGlJobExecution jobGuard;
ImageRepresentationGL* toReturn = ImageRepresentationGL::create(const_cast<ImageData*>(tester->getParent()), wtp);
ImageRepresentationGL* toReturn = nullptr;
GLJobProc.enqueueJobBlocking([&]() {
toReturn = ImageRepresentationGL::create(const_cast<ImageData*>(tester->getParent()), wtp);
});
switch (wtp._baseType) {
case WeaklyTypedPointer::UINT8:
......@@ -74,8 +76,10 @@ namespace campvis {
return toReturn;
}
else if (const ImageRepresentationLocal* tester = dynamic_cast<const ImageRepresentationLocal*>(source)) {
cgt::OpenGLJobProcessor::ScopedSynchronousGlJobExecution jobGuard;
ImageRepresentationGL* toReturn = ImageRepresentationGL::create(const_cast<ImageData*>(tester->getParent()), tester->getWeaklyTypedPointer());
ImageRepresentationGL* toReturn = nullptr;
GLJobProc.enqueueJobBlocking([&]() {
toReturn = ImageRepresentationGL::create(const_cast<ImageData*>(tester->getParent()), tester->getWeaklyTypedPointer());
});
return toReturn;
}
......@@ -92,12 +96,15 @@ namespace campvis {
return ImageRepresentationLocal::create(tester->getParent(), tester->getWeaklyTypedPointer());
}
else if (const ImageRepresentationGL* tester = dynamic_cast<const ImageRepresentationGL*>(source)) {
cgt::OpenGLJobProcessor::ScopedSynchronousGlJobExecution jobGuard;
WeaklyTypedPointer wtp = tester->getWeaklyTypedPointerCopy();
if (wtp._pointer != nullptr)
return ImageRepresentationLocal::create(source->getParent(), wtp);
ImageRepresentationLocal* toReturn = nullptr;
return nullptr;
GLJobProc.enqueueJobBlocking([&]() {
WeaklyTypedPointer wtp = tester->getWeaklyTypedPointerCopy();
if (wtp._pointer != nullptr)
toReturn = ImageRepresentationLocal::create(source->getParent(), wtp);
});
return toReturn;
}
return nullptr;
......
......@@ -96,16 +96,17 @@ namespace campvis {
else if (const ImageRepresentationGL* tester = dynamic_cast<const ImageRepresentationGL*>(source)) {
// converting from GL representation
cgt::OpenGLJobProcessor::ScopedSynchronousGlJobExecution jobGuard;
GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>* toReturn = nullptr;
GLJobProc.enqueueJobBlocking([&]() {
if (cgt::Texture::calcMatchingDataType(tester->getTexture()->getInternalFormat()) != TypeTraits<BASETYPE, NUMCHANNELS>::glDataType)
LDEBUGC("CAMPVis.core.datastructures.GenericLocalConversion", "Performing conversion between data types, you may lose information or the resulting data may show other unexpected features.");
if (cgt::Texture::calcMatchingDataType(tester->getTexture()->getInternalFormat()) != TypeTraits<BASETYPE, NUMCHANNELS>::glDataType)
LDEBUGC("CAMPVis.core.datastructures.GenericLocalConversion", "Performing conversion between data types, you may lose information or the resulting data may show other unexpected features.");
WeaklyTypedPointer wtp = tester->getWeaklyTypedPointerConvert(TypeTraits<BASETYPE, NUMCHANNELS>::glDataType);
if (wtp._pointer != nullptr)
return GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::create(tester->getParent(), static_cast<ElementType*>(wtp._pointer));
WeaklyTypedPointer wtp = tester->getWeaklyTypedPointerConvert(TypeTraits<BASETYPE, NUMCHANNELS>::glDataType);
if (wtp._pointer != nullptr)
toReturn = GenericImageRepresentationLocal<BASETYPE, NUMCHANNELS>::create(tester->getParent(), static_cast<ElementType*>(wtp._pointer));
});
return nullptr;
return toReturn;
}
else if (const ThisType* tester = dynamic_cast<const ThisType*>(source)) {
......
......@@ -72,7 +72,7 @@ namespace campvis {
GLCtxtMgr.releaseContext(backgroundGlContext, false);
GLJobProc.setContext(backgroundGlContext);
GLJobProc.start();
startOpenGlThreadAndMoveQtThreadAffinity(cgt::OpenGLJobProcessor::getPtr(), backgroundGlContext);
}
void deinit() {
......@@ -91,6 +91,36 @@ namespace campvis {
ProcessorFactory::deinit();
}
CAMPVIS_CORE_API void startOpenGlThreadAndMoveQtThreadAffinity(cgt::Runnable* runnable, cgt::GLCanvas* canvas) {
// welcome to a complex signalling ping-pong to move the OpenGL context thread affinity
// we will use targetThread as signalling variable and initialize it with nullptr:
void* targetThread = nullptr;
// start the new thread with special init function
runnable->start([&]() {
// the init function sets the signal variable to this thread's thread.
targetThread = canvas->getCurrentThreadPointer();
// wait until the signal variable has been reset to nullptr, since later calls in this
// thread may rely on the QGLContext's thread affinity already been moved.
while (targetThread != nullptr)
std::this_thread::yield();
targetThread = nullptr;
});
// Meanwhile in the main thread:
// wait until the signal variable has been set by the freshly created thread
while (targetThread == nullptr)
std::this_thread::yield();
// set the QGLContext's thread affinity
canvas->moveThreadAffinity(targetThread);
// reset the signal variable so that the new thread can continue.
targetThread = nullptr;
}
CAMPVIS_CORE_API std::string completePath(const std::string& filename) {
if (! cgt::ShaderManager::isInited())
return "";
......
......@@ -32,6 +32,7 @@
#include <vector>
namespace cgt {
class Runnable;
class GLCanvas;
}
......@@ -51,6 +52,13 @@ namespace campvis {
*/
CAMPVIS_CORE_API void deinit();
/**
* Starts the given Runnable and assigns the canvas' thread affinity to the runnable's thread.
* \param runnable Runnable to start.
* \param canvas Canvas whose thread affinity should be set to the thread of \a runnable.
*/
CAMPVIS_CORE_API void startOpenGlThreadAndMoveQtThreadAffinity(cgt::Runnable* runnable, cgt::GLCanvas* canvas);
/**
* Searches in all search paths for the given file and returns valid filename including complete path.
......
......@@ -77,9 +77,11 @@ ENDIF(CGT_WITH_QT)
# define library target
################################################################################
ADD_LIBRARY(cgt ${CGT_SOURCES} ${CGT_HEADERS})
ADD_DEFINITIONS(${CampvisGlobalDefinitions} ${CampvisModulesDefinitions} ${QT_DEFINITIONS})
ADD_DEFINITIONS(${CampvisGlobalDefinitions} ${CampvisModulesDefinitions})
INCLUDE_DIRECTORIES(${CampvisGlobalIncludeDirs} ${CampvisModulesIncludeDirs})
TARGET_LINK_LIBRARIES(cgt ${CampvisGlobalExternalLibs} ${QT_LIBRARIES})
TARGET_LINK_LIBRARIES(cgt ${CampvisGlobalExternalLibs})
qt5_use_modules(cgt Widgets OpenGL)
# if CGT is built as a shared library, CMake will define the following flag to instruct the code to
# export DLL symbols
......
......@@ -57,6 +57,15 @@ GLCanvas::~GLCanvas() {
delete eventHandler_;
}
void GLCanvas::moveThreadAffinity(void* threadPointer) {
// as a default behavior, do nothing
cgtAssert(threadPointer == nullptr, "Expected a nullptr. If this assertion was raised, make sure that you implemented the corresponding subclass of GLCanvas correctly.");
}
void* GLCanvas::getCurrentThreadPointer() {
return nullptr;
}
void GLCanvas::sizeChanged(const ivec2& size) {
size_ = size;
if (painter_)
......
......@@ -100,6 +100,19 @@ public:
/// Canvas subclasses.
virtual void swap() = 0;
/**
* Moves the thread affinity of this OpenGL canvas to the provided thread.
* ATTENTION: This method uses a void pointer to pass some thread-identifying object.
* Every subclass of GLCanvas may expect this pointer to be of a certain type.
* Make sure, that you are aware of passing an object of correct type, i.e. a
* pointer you created through getCurrentThreadPointer().
* \param threadPointer Pointer to some object, has to be of the same type as the one
* of the pointer returned by getCurrentThreadPointer().
*/
virtual void moveThreadAffinity(void* threadPointer);
virtual void* getCurrentThreadPointer();
/**
* If we use several GLCanvases, we have to switch the OpenGL context when we switch between
* canvases; this method sets the context of this canvas as the current one.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment