Commit 1302ca97 authored by schultezub's avatar schultezub
Browse files

It took quite some time, but this commit introduces thread-safe management and...

It took quite some time, but this commit introduces thread-safe management and access to multiple OpenGL contexts.
 * added tgt::QtContextManager for managing multiple OpenGL contexts
 * adapted tgt::GLContext and tgt::QtGLContext, tgt::QtThreadedPainter
 * added PipelineEvaluator for evaluating pipelines in separate threads
 * added various lock/sync features to processors, pipelines, and processors

However, there's still plenty of clean-up work to do: some parts of the tgt library are quite messed up and the core library interfaces need some polishing...


git-svn-id: https://camplinux.in.tum.de/svn/campvis/trunk@195 bb408c1c-ae56-11e1-83d9-df6b3e0c105e
parent c9226858
......@@ -3,15 +3,31 @@
#include "tgt/shadermanager.h"
#include "tgt/qt/qtapplication.h"
#include "tgt/qt/qtcanvas.h"
#include "tgt/qt/qtcontextmanager.h"
#include "tbb/include/tbb/task_scheduler_init.h"
#include "tbb/include/tbb/compat/thread"
#include "tumvispainter.h"
#include "core/pipeline/pipelineevaluator.h"
#include "modules/pipelines/slicevis.h"
#include <functional>
using namespace TUMVis;
SliceVis* sliceVis = 0;
TumVisPainter* painter = 0;
PipelineEvaluator* pe;
void startEvaluator() {
pe = new PipelineEvaluator(sliceVis);
pe->startEvaluation();
}
void startPainter() {
painter->run();
}
/**
* TUMVis main function, application entry point
*
......@@ -21,13 +37,17 @@ using namespace TUMVis;
**/
int main(int argc, char** argv) {
tgt::QtApplication* app = new tgt::QtApplication(argc, argv);
tgt::QtCanvas* canvas = new tgt::QtCanvas("TUMVis");
SliceVis* sliceVis = 0;
tgt::QtContextManager::init();
tgt::QtCanvas* renderCanvas = CtxtMgr.createContext("render", "TUMVis");
tgt::QtCanvas* sliceVisCanvas = CtxtMgr.createContext("sliceVis", "SliceVis");
tbb::task_scheduler_init init;
app->addCanvas(canvas);
renderCanvas->getContext()->acquire();
app->addCanvas(renderCanvas);
//app->addCanvas(sliceVisCanvas);
app->init();
LogMgr.getConsoleLog()->addCat("", true);
LGL_ERROR;
if (argc > 0) {
// ugly hack
......@@ -36,16 +56,20 @@ int main(int argc, char** argv) {
ShdrMgr.addPath(programPath);
ShdrMgr.addPath(programPath + "/core/glsl");
}
LGL_ERROR;
tgt::Camera camera;
canvas->setCamera(&camera);
TumVisPainter* painter;
renderCanvas->setCamera(&camera);
try {
sliceVis = new SliceVis(canvas);
painter = new TumVisPainter(canvas, sliceVis);
canvas->setPainter(painter);
sliceVis = new SliceVis(sliceVisCanvas);
painter = new TumVisPainter(renderCanvas, sliceVis);
LGL_ERROR;
renderCanvas->setPainter(painter);
LGL_ERROR;
sliceVis->init();
LGL_ERROR;
}
catch (tgt::Exception& e) {
LERRORC("main.cpp", "Encountered tgt::Exception: " << e.what());
......@@ -54,12 +78,23 @@ int main(int argc, char** argv) {
LERRORC("main.cpp", "Encountered std::exception: " << e.what());
}
// disconnect OpenGL context from this thread so that the other threads can acquire an OpenGL context.
CtxtMgr.releaseCurrentContext();
std::thread evaluatorThread(&startEvaluator);
std::thread painterThread(&startPainter);
app->run();
painter->stop();
pe->stopEvaluation();
evaluatorThread.join();
painterThread.join();
delete painter;
delete sliceVis;
delete canvas;
delete app;
delete app;
return 0;
return 0;
}
......@@ -26,15 +26,13 @@ namespace TUMVis {
if (getCanvas() == 0)
return;
const tgt::ivec2 size = getCanvas()->getSize();
const tgt::ivec2& size = getCanvas()->getSize();
glViewport(0, 0, size.x, size.y);
// try get Data
const ImageDataRenderTarget* image = _pipeline->getRenderTarget();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (image != 0) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// activate shader
_copyShader->activate();
_copyShader->setIgnoreUniformLocationError(true);
......@@ -51,11 +49,11 @@ namespace TUMVis {
tgt::QuadRenderer::renderQuad();
_copyShader->deactivate();
LGL_ERROR;
}
else {
// TODO: render some nifty error texture
// so long, we do some dummy rendering
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
tgt::Camera c(tgt::vec3(0.f,0.f,2.f));
c.look();
glColor3f(1.f, 0.f, 0.f);
......@@ -91,6 +89,7 @@ namespace TUMVis {
void TumVisPainter::sizeChanged(const tgt::ivec2& size) {
_pipeline->setRenderTargetSize(size);
_renderCondition.wakeAll();
}
void TumVisPainter::init() {
......
......@@ -24,7 +24,7 @@ namespace TUMVis {
TumVisPainter(tgt::QtCanvas* canvas, VisualizationPipeline* pipeline);
virtual void paint();
virtual void sizeChanged(const tgt::ivec2& size);
virtual void sizeChanged(const tgt::ivec2& size);
virtual void init();
void setPipeline(VisualizationPipeline* pipeline);
......
......@@ -28,14 +28,26 @@ namespace TUMVis {
}
void AbstractPipeline::onNotify(const ProcessorObserverArgs& poa) {
tbb::spin_mutex::scoped_lock lock(_localMutex);
_invalidationLevel.setLevel(InvalidationLevel::INVALID_RESULT);
//execute();
s_PipelineInvalidated();
}
const DataContainer& AbstractPipeline::getDataContainer() const {
return _data;
}
void AbstractPipeline::executeProcessor(AbstractProcessor& processor) {
processor.lockProperties();
processor.process(_data);
processor.unlockProperties();
}
tbb::mutex& AbstractPipeline::getEvaluationMutex() {
return _evaluationMutex;
}
InvalidationLevel& AbstractPipeline::getInvalidationLevel() {
return _invalidationLevel;
}
}
#ifndef ABSTRACTPIPELINE_H__
#define ABSTRACTPIPELINE_H__
#include "sigslot/sigslot.h"
#include "tgt/logmanager.h"
#include "tbb/include/tbb/spin_mutex.h"
#include "tbb/include/tbb/mutex.h"
#include "tbb/include/tbb/compat/condition_variable"
#include "core/tools/observer.h"
#include "core/tools/invalidationlevel.h"
#include "core/datastructures/datacontainer.h"
......@@ -62,7 +65,19 @@ namespace TUMVis {
*/
virtual void onNotify(const ProcessorObserverArgs& poa);
tbb::mutex& getEvaluationMutex();
InvalidationLevel& getInvalidationLevel();
sigslot::signal0<> s_PipelineInvalidated;
protected:
/**
* Executes the processor \a processor on the pipeline's data and locks its properties meanwhile.
* \param processor Processor to execute.
*/
void executeProcessor(AbstractProcessor& processor);
DataContainer _data; ///< DataContainer containing local working set of data for this Pipeline
std::vector<AbstractProcessor*> _processors; ///< List of all processors of this pipeline
......@@ -70,6 +85,7 @@ namespace TUMVis {
InvalidationLevel _invalidationLevel; ///< current invalidation level
tbb::spin_mutex _localMutex; ///< mutex for altering local members
tbb::mutex _evaluationMutex; ///< mutex for the evaluation of this pipeline
static const std::string loggerCat_;
};
......
......@@ -37,4 +37,13 @@ namespace TUMVis {
}
void AbstractProcessor::lockProperties() {
_properties.lockAllProperties();
}
void AbstractProcessor::unlockProperties() {
_properties.unlockAllProperties();
}
}
......@@ -107,6 +107,18 @@ namespace TUMVis {
*/
virtual void onNotify(const PropertyObserverArgs& poa);
/**
* Locks all properties in the processor's PropertyCollection and marks them as "in use".
* \sa AbstractProcessor::unlock
*/
void lockProperties();
/**
* Unlocks all properties in the processor's PropertyCollection and marks them as "not in use".
* \sa AbstractProcessor::lock
*/
void unlockProperties();
protected:
InvalidationLevel _invalidationLevel; ///< current invalidation level of this processor
......
#include "pipelineevaluator.h"
namespace TUMVis {
const std::string PipelineEvaluator::loggerCat_ = "TUMVis.core.pipeline.PipelineEvaluator";
PipelineEvaluator::PipelineEvaluator(AbstractPipeline* pipeline)
: _pipeline(pipeline)
, _evaluatePipeline(false)
{
tgtAssert(pipeline != 0, "Pipeline must not be 0.");
pipeline->s_PipelineInvalidated.connect(this, &PipelineEvaluator::OnPipelineInvalidated);
}
PipelineEvaluator::~PipelineEvaluator() {
_pipeline->s_PipelineInvalidated.disconnect(this);
}
void PipelineEvaluator::startEvaluation() {
_evaluatePipeline = true;
std::unique_lock<tbb::mutex> lock(_pipeline->getEvaluationMutex());
while (_evaluatePipeline) {
_pipeline->execute();
while (_evaluatePipeline && _pipeline->getInvalidationLevel().isValid())
_evaluationCondition.wait(lock);
}
}
void PipelineEvaluator::stopEvaluation() {
_evaluatePipeline = false;
_evaluationCondition.notify_all();
}
void PipelineEvaluator::OnPipelineInvalidated() {
if (_evaluatePipeline)
_evaluationCondition.notify_all();
}
}
#ifndef PIPELINEEVALUATOR_H__
#define PIPELINEEVALUATOR_H__
#include "sigslot/sigslot.h"
#include "core/pipeline/abstractpipeline.h"
#include "tbb/include/tbb/compat/condition_variable"
namespace TUMVis {
/**
*
*/
class PipelineEvaluator : public sigslot::has_slots<> {
public:
PipelineEvaluator(AbstractPipeline* pipeline);
~PipelineEvaluator();
void startEvaluation();
void stopEvaluation();
void OnPipelineInvalidated();
protected:
AbstractPipeline* _pipeline;
bool _evaluatePipeline;
std::condition_variable _evaluationCondition;
static const std::string loggerCat_;
};
}
#endif // PIPELINEEVALUATOR_H__
......@@ -63,4 +63,10 @@ namespace TUMVis {
_renderTargetSize.setValue(size);
}
void VisualizationPipeline::lockGLContextAndExecuteProcessor(AbstractProcessor& processor) {
tgt::GLContextScopedLock lock(_canvas->getContext());
executeProcessor(processor);
glFlush();
}
}
......@@ -82,7 +82,6 @@ namespace TUMVis {
/**
* Gets called when the data collection of this pipeline has changed and thus has notified its observers.
* The default behaviour is to do nothing
* \sa GenericObserver::onNotify, DataContainer
* \param poa DataContainerObserverArgs ObserverArgument struct containing the emitting DataContainer and hints to its changes
*/
void onDataContainerDataAdded(const std::string& name, const DataHandle* dh);
......@@ -91,6 +90,13 @@ namespace TUMVis {
sigslot::signal0<> s_renderTargetChanged;
protected:
/**
* Acquires and locks the OpenGL context, executes the processor \a processor on the pipeline's data
* and locks its properties meanwhile.
* \param processor Processor to execute.
*/
void lockGLContextAndExecuteProcessor(AbstractProcessor& processor);
GenericProperty<tgt::ivec2> _renderTargetSize; ///< Viewport size of target canvas
GenericProperty<std::string> _renderTargetID; ///< ID of the render target image to be rendered to the canvas
std::vector<AbstractEventHandler*> _eventHandlers; ///< List of registered event handlers for the pipeline
......
......@@ -33,11 +33,13 @@ namespace TUMVis {
void AbstractProperty::addSharedProperty(AbstractProperty* prop) {
tgtAssert(prop != 0, "Shared property must not be 0!");
tbb::spin_mutex::scoped_lock lock(_localMutex);
_sharedProperties.insert(prop);
}
void AbstractProperty::removeSharedProperty(AbstractProperty* prop) {
tgtAssert(prop != 0, "Shared property must not be 0!");
tbb::spin_mutex::scoped_lock lock(_localMutex);
_sharedProperties.erase(prop);
}
......@@ -46,12 +48,10 @@ namespace TUMVis {
}
void AbstractProperty::lock() {
tbb::spin_mutex::scoped_lock lock(_localMutex);
_inUse = true;
}
void AbstractProperty::unlock() {
tbb::spin_mutex::scoped_lock lock(_localMutex);
_inUse = false;
}
......
......@@ -21,7 +21,7 @@ IF(TGT_WITH_WMI)
ENDIF(TGT_WITH_WMI)
# headers
FILE(GLOB TGT_HEADERS *.h event/*.h navigation/*.h qt/*.qt)
FILE(GLOB TGT_HEADERS *.h event/*.h navigation/*.h qt/*.h)
# sources
SET(TGT_SOURCES
......@@ -72,6 +72,7 @@ SET(TGT_SOURCES
#qt
qt/qtapplication.cpp
qt/qtcanvas.cpp
qt/qtcontextmanager.cpp
qt/qtglcontext.cpp
qt/qtthreadedcanvas.cpp
qt/qtthreadedpainter.cpp
......
......@@ -7,16 +7,15 @@ namespace tgt {
/**
* Abstract base class for thread-safe OpenGL contexts.
*/
class GLContext {
class TGT_API GLContext {
public:
GLContext();
~GLContext();
virtual void acquire() = 0;
virtual void release() = 0;
virtual void lockAndAcquire() = 0;
virtual void releaseAndUnlock() = 0;
virtual void unlock() = 0;
virtual ivec2 getViewportSize() const = 0;
};
......@@ -24,7 +23,7 @@ namespace tgt {
/**
* Scoped lockAndAcquire for a GLContext, that automatically unlocks the context on destruction.
*/
class GLContextScopedLock {
class TGT_API GLContextScopedLock {
public:
GLContextScopedLock(GLContext* context)
: _context(context)
......@@ -34,7 +33,7 @@ namespace tgt {
};
~GLContextScopedLock() {
if (_context)
_context->releaseAndUnlock();
_context->unlock();
}
private:
GLContext* _context;
......
/**********************************************************************
* *
* tgt - Tiny Graphics Toolbox *
* *
* Copyright (C) 2006-2008 Visualization and Computer Graphics Group, *
* Department of Computer Science, University of Muenster, Germany. *
* <http://viscg.uni-muenster.de> *
* *
* This file is part of the tgt library. This library is free *
* software; you can redistribute it and/or modify it under the terms *
* of the GNU Lesser General Public License version 2.1 as published *
* by the Free Software Foundation. *
* *
* 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 for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License in the file "LICENSE.txt" along with this library. *
* If not, see <http://www.gnu.org/licenses/>. *
* *
**********************************************************************/
#ifndef TGT_QTCANVAS_H
#define TGT_QTCANVAS_H
#include "tgt/glcanvas.h"
#include "tgt/qt/qtglcontext.h"
#include <QGLWidget>
#include <QGLFormat>
#include <QMouseEvent>
#include <QKeyEvent>
#include <deque>
namespace tgt {
/**
* Qt implementation of GLCanvas. Inherits QGLWidget and combines the Qt methods and tgt methods.
*/
class QtCanvas : public QGLWidget, public GLCanvas {
public:
/**
* The constructor. Allows the user to specify a shared widget that this canvas will share
* the OpenGL context with. Also, it is possible to specify whether or not a custom
* event-loop has to be used.
*
* @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 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);
QtCanvas(QWidget* parent, bool shared = false, Qt::WFlags f = 0, char* name = 0);
/**
* Destructor. Closes window (if canvas is a window).
*/
~QtCanvas();
/// initialize canvas
virtual void init();
/**
* Called by the Qt framework once at the beginning, before rendering starts.
* This function calls the corresponding GLCanvas method \a initialize.
*/
virtual void initializeGL();
/**
* This is called by the Qt framework every time the canvas is resized.
* This function calls the corresponding GLCanvas method \a sizeChanged.
*
* @param w The new width of the canvas.
* @param h The new height of the canvas.
*/
virtual void resizeGL(int w, int h);
/**
* Called by Qt if there is a paint event; it uses the \a painter_ to paint() something.
*/
virtual void paintGL();
/**
* If you manually want to cause a paint-event, use this function. It will call paintGL()
* via updateGL(). This will cause immediate repainting.
*/
virtual void repaint();
/**
* If you manually want to cause a paint-event, use this function. It will call QWidget::update()
* and repaint when entering main loop next time.
*/
virtual void update();
/// swap buffers
virtual void swap();
/**
* If we use several GLCanvases, we have to switch the OpenGL context when we switch between
* canvases; this method returns the context of this canvas.
*/
virtual QtGLContext* getContext();
virtual void toggleFullScreen();
virtual void enterEvent(QEvent* e);
virtual void leaveEvent(QEvent* e);
virtual void mousePressEvent(QMouseEvent* e);
virtual void mouseReleaseEvent (QMouseEvent* e);
virtual void mouseMoveEvent(QMouseEvent* e);
virtual void mouseDoubleClickEvent(QMouseEvent* e);
virtual void wheelEvent(QWheelEvent* e);
virtual void timerEvent(QTimerEvent* e);
virtual void keyPressEvent(QKeyEvent* event);
virtual void keyReleaseEvent(QKeyEvent* event);
///
/// Helpers used to generate tgt-Events out of qt-Events
///
// map one Qt-mousebutton to one tgt-mousebutton
static tgt::MouseEvent::MouseButtons getButton(QMouseEvent* e);
// map a set of Qt-mousebuttons to a set of tgt-mousebuttons
static tgt::MouseEvent::MouseButtons getButtons(QMouseEvent* e);
static tgt::Event::Modifier getModifier(QInputEvent* e);
static KeyEvent::KeyCode getKey(int key);
static QGLFormat getQGLFormat(const Buffers buffers);
protected:
static QGLWidget* shareWidget_; ///< widget that this canvas shares the OpenGL context with
QtGLContext _context; ///< OpenGL context of this canvas
};
} // namespace
#endif // TGT_QTCANVAS_H
/**********************************************************************
* *
* tgt - Tiny Graphics Toolbox *
* *
* Copyright (C) 2006-2008 Visualization and Computer Graphics Group, *
* Department of Computer Science, University of Muenster, Germany. *
* <http://viscg.uni-muenster.de> *
* *
* This file is part of the tgt library. This library is free *
* software; you can redistribute it and/or modify it under the terms *