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
......@@ -31,6 +31,12 @@
#include "gpucapabilitieswindows.h"
#include "logmanager.h"
#ifdef CGT_WITH_WMI
// due to some weird include hierarchies, comdef.h is missing an assert define in MSVC 2010, so we provide it with one.
#define assert(_Expression)
#include <comdef.h>
#endif
#include <windows.h>
#include <WinBase.h>
#include <tchar.h>
......
......@@ -37,7 +37,6 @@
#ifdef CGT_WITH_WMI
#include <wbemidl.h>
#include <comdef.h>
#endif
namespace cgt {
......
......@@ -36,24 +36,6 @@
namespace cgt {
OpenGLJobProcessor::ScopedSynchronousGlJobExecution::ScopedSynchronousGlJobExecution()
: _lock(nullptr)
{
if (! GLCtxtMgr.checkWhetherThisThreadHasAcquiredOpenGlContext()) {
GLJobProc.pause();
_lock = new cgt::GLContextScopedLock(GLJobProc.getContext());
}
}
OpenGLJobProcessor::ScopedSynchronousGlJobExecution::~ScopedSynchronousGlJobExecution() {
if (_lock != nullptr) {
delete _lock;
GLJobProc.resume();
}
}
// ================================================================================================
OpenGLJobProcessor::OpenGLJobProcessor()
{
_pause = 0;
......@@ -135,5 +117,13 @@ namespace cgt {
return _context;
}
void OpenGLJobProcessor::enqueueJobBlocking(std::function<void(void)> fn) {
char signalVariable = 0;
enqueueJob(cgt::makeJobOnHeap([&signalVariable, fn]() { fn(); signalVariable = 1; }));
while (signalVariable == 0)
std::this_thread::yield();
}
}
......@@ -61,26 +61,6 @@ namespace cgt {
friend class AbstractJob; ///< so the custom new/delete operator can access the memory pool
public:
/**
* Scope guard to ensure that encapsulated job is synchronously executed in an arbitrary OpenGL context.
* This scope guard checks whether current thread is OpenGLJobProcessor thread. If so, it
* does nothing. If this thread is not the OpenGL thread, the OpenGLJobProcessor is paused,
* an arbitrary OpenGL context acquired. Upon destruction the OpenGLJobProcessor is resumed.
*/
class CGT_API ScopedSynchronousGlJobExecution {
public:
ScopedSynchronousGlJobExecution();
~ScopedSynchronousGlJobExecution();
private:
// disable copying
explicit ScopedSynchronousGlJobExecution(const ScopedSynchronousGlJobExecution& rhs);
ScopedSynchronousGlJobExecution& operator=(ScopedSynchronousGlJobExecution rhs);
cgt::GLContextScopedLock* _lock;
};
/**
* Destructor, deletes all unfinished jobs.
*/
......@@ -123,6 +103,15 @@ namespace cgt {
*/
void enqueueJob(AbstractJob* job);
/**
* Enqueues the given job and blockes the execution until the job has been processed.
*
* \note OpenGLJobProcessor takes ownership of \a job.
* \param fn Functor defining job to execute!
*/
void enqueueJobBlocking(std::function<void(void)> fn);
protected:
// Protected constructor since it's a singleton
OpenGLJobProcessor();
......
......@@ -28,6 +28,9 @@
#include "qtcanvas.h"
#include <QThread>
#include <QOpenGLContext>
namespace cgt {
// shared context widget
......@@ -36,9 +39,9 @@ QGLWidget* QtCanvas::shareWidget_ = 0;
QtCanvas::QtCanvas(const std::string& title,
const ivec2& size,
const Buffers buffers,
QWidget* parent, bool shared, Qt::WFlags f, char* /*name*/)
QWidget* parent, bool shared, Qt::WindowFlags f, char* /*name*/)
: GLCanvas(title, size, buffers)
, QGLWidget(getQGLFormat(buffers), parent, (shared ? shareWidget_ : 0), f)
, QGLWidget(getQGLFormat(buffers), 0, (shared ? shareWidget_ : 0), f)
{
resize(size.x, size.y);
if (shared && shareWidget_ == 0)
......@@ -60,11 +63,13 @@ QtCanvas::QtCanvas(const std::string& title,
depthSize_ = format().depthBufferSize();
doubleBuffered_ = doubleBuffer();
stereoViewing_ = format().stereo();
connect(this, SIGNAL(s_sizeChangedExternally(int, int)), this, SLOT(sizeChangedExternally(int, int)));
}
QtCanvas::QtCanvas(QWidget* parent, bool shared, Qt::WFlags f, char* /*name*/)
QtCanvas::QtCanvas(QWidget* parent, bool shared, Qt::WindowFlags f, char* /*name*/)
: GLCanvas()
, QGLWidget(parent, (shared ? shareWidget_ : 0), f)
, QGLWidget(0, (shared ? shareWidget_ : 0), f)
{
if (shared && shareWidget_ == 0)
shareWidget_ = this;
......@@ -84,6 +89,15 @@ QtCanvas::QtCanvas(QWidget* parent, bool shared, Qt::WFlags f, char* /*name*/)
QtCanvas::~QtCanvas() {}
void QtCanvas::moveThreadAffinity(void* threadPointer) {
QThread* qThreadPointer = static_cast<QThread*>(threadPointer);
this->context()->moveToThread(qThreadPointer);
}
void* QtCanvas::getCurrentThreadPointer() {
return QThread::currentThread();
}
void QtCanvas::initializeGL() {
}
......@@ -105,7 +119,7 @@ void QtCanvas::update() {
void QtCanvas::swap() {
QGLWidget::swapBuffers();
this->makeCurrent();
QGLWidget::makeCurrent();
}
void QtCanvas::toggleFullScreen() {
......@@ -566,7 +580,9 @@ KeyEvent::KeyCode QtCanvas::getKey(int key) {
}
void QtCanvas::setSize(ivec2 newSize) {
QWidget::resize(newSize.x, newSize.y);
// pass size change command through Qt's event messaging system to enforce execution in GUI thread.
// (setSize() may be called externally from a different thread).
emit s_sizeChangedExternally(newSize.x, newSize.y);
}
QSize QtCanvas::sizeHint() const {
......@@ -576,11 +592,15 @@ QSize QtCanvas::sizeHint() const {
}
void QtCanvas::acquireAsCurrentContext() {
this->makeCurrent();
QGLWidget::makeCurrent();
}
void QtCanvas::releaseAsCurrentContext() {
this->doneCurrent();
QGLWidget::doneCurrent();
}
void QtCanvas::sizeChangedExternally(int w, int h) {
QWidget::resize(w, h);
}
......
......@@ -44,7 +44,9 @@ namespace cgt {
/**
* Qt implementation of GLCanvas. Inherits QGLWidget and combines the Qt methods and cgt methods.
*/
class CGT_API QtCanvas : public GLCanvas, public QGLWidget {
class CGT_API QtCanvas : public QGLWidget, public GLCanvas {
Q_OBJECT;
public:
/**
* The constructor. Allows the user to specify a shared widget that this canvas will share
......@@ -53,22 +55,25 @@ public:
*
* @param parent The parent widget of this canvas.
* @param shared If this is true, this canvas will share its OpenGL context with the static \a shareWidget_.
* @param f Qt::Wflags can be passed to this constructor to control the qt features, like stereo-buffering.
* @param f Qt::WindowFlags can be passed to this constructor to control the qt features, like stereo-buffering.
* @param useCustomEventloop Are the incoming events supposed to be handled by the standard callbacks or not?
* @param name A name can be passed for debugging purposes.
*/
QtCanvas(const std::string& title = "",
const ivec2& size = ivec2(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT),
const Buffers buffers = RGBADD,
QWidget* parent = 0, bool shared = false, Qt::WFlags f = 0, char* name = 0);
QWidget* parent = 0, bool shared = false, Qt::WindowFlags f = 0, char* name = 0);
QtCanvas(QWidget* parent, bool shared = false, Qt::WFlags f = 0, char* name = 0);
QtCanvas(QWidget* parent, bool shared = false, Qt::WindowFlags f = 0, char* name = 0);
/**
* Destructor. Closes window (if canvas is a window).
*/
virtual ~QtCanvas();
virtual void moveThreadAffinity(void* threadPointer) override;
virtual void* getCurrentThreadPointer() override;
/**
* Called by the Qt framework once at the beginning, before rendering starts.
......@@ -147,7 +152,15 @@ public:
static KeyEvent::KeyCode getKey(int key);
static QGLFormat getQGLFormat(const Buffers buffers);
signals:
void s_sizeChangedExternally(int w, int h);
protected slots:
void sizeChangedExternally(int w, int h);
protected:
static QGLWidget* shareWidget_; ///< widget that this canvas shares the OpenGL context with
};
......
......@@ -32,7 +32,7 @@
namespace cgt {
QtThreadedCanvas::QtThreadedCanvas(const std::string& title /*= ""*/, const ivec2& size /*= ivec2(GLCanvas::DEFAULT_WINDOW_WIDTH, GLCanvas::DEFAULT_WINDOW_HEIGHT)*/, const GLCanvas::Buffers buffers /*= GLCanvas::RGBADD*/, QWidget* parent /*= 0*/, bool shared /*= false*/, Qt::WFlags f /*= 0*/, char* name /*= 0*/)
QtThreadedCanvas::QtThreadedCanvas(const std::string& title /*= ""*/, const ivec2& size /*= ivec2(GLCanvas::DEFAULT_WINDOW_WIDTH, GLCanvas::DEFAULT_WINDOW_HEIGHT)*/, const GLCanvas::Buffers buffers /*= GLCanvas::RGBADD*/, QWidget* parent /*= 0*/, bool shared /*= false*/, Qt::WindowFlags f /*= 0*/, char* name /*= 0*/)
: QtCanvas(title, size, buffers, parent, shared, f, name)
{
}
......@@ -50,7 +50,7 @@ namespace cgt {
// all painting done in threaded painter
}
void QtThreadedCanvas::paintEvent(QPaintEvent* /* event */) {
void QtThreadedCanvas::paintEvent(QPaintEvent* e) {
if (painter_ != nullptr)
painter_->repaint();
}
......
......@@ -43,7 +43,7 @@ namespace cgt {
const std::string& title = "",
const cgt::ivec2& size = cgt::ivec2(cgt::GLCanvas::DEFAULT_WINDOW_WIDTH, cgt::GLCanvas::DEFAULT_WINDOW_HEIGHT),
const cgt::GLCanvas::Buffers buffers = cgt::GLCanvas::RGBADD,
QWidget* parent = 0, bool shared = true, Qt::WFlags f = 0, char* name = 0);
QWidget* parent = 0, bool shared = true, Qt::WindowFlags f = 0, char* name = 0);
virtual ~QtThreadedCanvas();
......
......@@ -30,7 +30,10 @@
namespace cgt {
void invokeThread(Runnable* r) {
r->_initFunction();
r->run();
r->_deinitFunction();
r->_running = false;
}
......@@ -38,6 +41,8 @@ namespace cgt {
: _stopExecution()
, _thread(0)
{
_initFunction = [] () {};
_deinitFunction = [] () {};
_stopExecution = false;
_running = false;
}
......@@ -49,11 +54,13 @@ namespace cgt {
delete _thread;
}
void Runnable::stop() {
void Runnable::stop(std::function<void(void)> deinitFunction) {
if (_thread == 0)
return;
_stopExecution = true;
_deinitFunction = deinitFunction;
_stopExecution = true;
try {
if (_thread->joinable())
_thread->join();
......@@ -63,11 +70,12 @@ namespace cgt {
}
}
void Runnable::start() {
void Runnable::start(std::function<void(void)> initFunction) {
_initFunction = initFunction;
_thread = new std::thread(&invokeThread, this);
_running = true;
}
// ================================================================================================
RunnableWithConditionalWait::RunnableWithConditionalWait()
......@@ -78,14 +86,16 @@ namespace cgt {
}
void RunnableWithConditionalWait::stop() {
void RunnableWithConditionalWait::stop(std::function<void(void)> deinitFunction) {
_deinitFunction = deinitFunction;
while (_running) {
_stopExecution = true;
_evaluationCondition.notify_all();
std::this_thread::yield();
}
Runnable::stop();
Runnable::stop(deinitFunction);
}
}
......@@ -33,6 +33,7 @@
#include <ext/threading.h>
#include <tbb/atomic.h>
#include <functional>
namespace cgt {
class RunnableWithConditionalWait;
......@@ -44,7 +45,10 @@ namespace cgt {
* and waits for the thread to finish. Hence, you should test for _stopExecution in your run() method.
*/
class CGT_API Runnable {
// the launch function needs to be a free function but have access to Runnable's internals.
friend void invokeThread(Runnable* r);
// We do not want to expose _running, but to RunnableWithConditionalWait needs access.
friend class RunnableWithConditionalWait;
public:
......@@ -52,22 +56,24 @@ namespace cgt {
* Creates a new Runnable object
*/
Runnable();
/**
* Destructor, stops and waits for the thread to finish if the thread is still running.
*/
virtual ~Runnable();
/**
* Creates the new thread evaluating the run() method.
* Creates the new thread first calling provided function and the calling the run() method.
* \param initFunction Function to run as first code in the newly created thread (for initing purposes), defaults to a NOP.
* \sa Runnable::run
*/
virtual void start();
virtual void start(std::function<void(void)> initFunction = [] () {});
/**
* Sets the _stopExecution flag and waits for the thread to finish.
* \param deinitFunction Function to run as last code in the thread (for deiniting purposes), defaults to a NOP.
*/
virtual void stop();
virtual void stop(std::function<void(void)> deinitFunction = [] () {});
/**
* Entrance point for the new thread. To be overwritten in subclasses.
......@@ -75,7 +81,9 @@ namespace cgt {
virtual void run() = 0;
protected:
tbb::atomic<bool> _stopExecution; ///< Flag whether the thread should stop
tbb::atomic<bool> _stopExecution; ///< Flag whether the thread should stop
std::function<void(void)> _initFunction; ///< Function to execute in the thread as very first code.
std::function<void(void)> _deinitFunction; ///< Function to execute in the thread as very last code.
private:
/// Runnables are not copyable
......@@ -83,8 +91,8 @@ namespace cgt {
/// Runnables are not copyable
Runnable& operator =(Runnable const&);
std::thread* _thread; ///< Thread of the Runnable
tbb::atomic<bool> _running;
std::thread* _thread; ///< Thread of the Runnable
tbb::atomic<bool> _running; ///< Flag whether the thread is still running
};
......@@ -102,7 +110,7 @@ namespace cgt {
* Creates a new RunnableWithConditionalWait object
*/
RunnableWithConditionalWait();
/**
* Destructor, stops and waits for thread to finish if the thread is still running.
*/
......@@ -111,7 +119,7 @@ namespace cgt {
/**
* Sets the _stopExecution flag and waits for the thread to finish.
*/
virtual void stop();
virtual void stop(std::function<void(void)> deinitFunction = [] () {});
protected:
/// conditional wait to be used when there are currently no jobs to process
......
......@@ -236,18 +236,20 @@ namespace campvis {
p_outputImage.addSharedProperty(&_raycaster->p_targetImageID);
_raycaster->s_invalidated.connect(this, &VolumeRenderer::onProcessorInvalidated);
cgt::OpenGLJobProcessor::ScopedSynchronousGlJobExecution jobGuard;
_raycaster->init();
_raycaster->p_sourceImageID.setValue(currentRaycaster->p_sourceImageID.getValue());
_raycaster->p_entryImageID.setValue(currentRaycaster->p_entryImageID.getValue());
_raycaster->p_exitImageID.setValue(currentRaycaster->p_exitImageID.getValue());
_raycaster->p_targetImageID.setValue(currentRaycaster->p_targetImageID.getValue());
_raycaster->p_camera.setValue(currentRaycaster->p_camera.getValue());
_raycaster->p_transferFunction.replaceTF(currentRaycaster->p_transferFunction.getTF()->clone());
_raycaster->p_jitterStepSizeMultiplier.setValue(currentRaycaster->p_jitterStepSizeMultiplier.getValue());
_raycaster->p_samplingRate.setValue(currentRaycaster->p_samplingRate.getValue());
currentRaycaster->deinit();
GLJobProc.enqueueJobBlocking([&]() {
_raycaster->init();
_raycaster->p_sourceImageID.setValue(currentRaycaster->p_sourceImageID.getValue());
_raycaster->p_entryImageID.setValue(currentRaycaster->p_entryImageID.getValue());
_raycaster->p_exitImageID.setValue(currentRaycaster->p_exitImageID.getValue());
_raycaster->p_targetImageID.setValue(currentRaycaster->p_targetImageID.getValue());
_raycaster->p_camera.setValue(currentRaycaster->p_camera.getValue());
_raycaster->p_transferFunction.replaceTF(currentRaycaster->p_transferFunction.getTF()->clone());
_raycaster->p_jitterStepSizeMultiplier.setValue(currentRaycaster->p_jitterStepSizeMultiplier.getValue());
_raycaster->p_samplingRate.setValue(currentRaycaster->p_samplingRate.getValue());
currentRaycaster->deinit();
});
invalidate(PG_INVALID | EEP_INVALID | RAYCASTER_INVALID | AbstractProcessor::INVALID_RESULT);
delete currentRaycaster;
......
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