Commit c9226858 authored by schultezub's avatar schultezub
Browse files

* adapted tgt::Canvas to support thread-safety

 * introduced abstract tgt::GLContext for thread-safe management of an OpenGL context
 * introduced Qt-threading based tgt::QtGLContext, tgt::QtThreadedCanvas and tgt::QtThreadedPainter
 * adapted tumvis-core and tumvis-application to new OpenGL context handling

git-svn-id: https://camplinux.in.tum.de/svn/campvis/trunk@194 bb408c1c-ae56-11e1-83d9-df6b3e0c105e
parent 5ea6ef24
......@@ -37,12 +37,12 @@ int main(int argc, char** argv) {
ShdrMgr.addPath(programPath + "/core/glsl");
}
tgt::Camera camera;
tgt::Camera camera;
canvas->setCamera(&camera);
TumVisPainter* painter;
try {
sliceVis = new SliceVis();
sliceVis = new SliceVis(canvas);
painter = new TumVisPainter(canvas, sliceVis);
canvas->setPainter(painter);
sliceVis->init();
......
......@@ -2,6 +2,7 @@
#include "tgt/assert.h"
#include "tgt/camera.h"
#include "tgt/glcontext.h"
#include "tgt/quadrenderer.h"
#include "tgt/quadric.h"
......@@ -10,8 +11,8 @@
namespace TUMVis {
const std::string TumVisPainter::loggerCat_ = "TUMVis.core.TumVisPainter";
TumVisPainter::TumVisPainter(tgt::GLCanvas* canvas, VisualizationPipeline* pipeline)
: tgt::Painter(canvas)
TumVisPainter::TumVisPainter(tgt::QtCanvas* canvas, VisualizationPipeline* pipeline)
: tgt::QtThreadedPainter(canvas)
, _pipeline(0)
, _dirty(0)
, _currentlyRendering(false)
......@@ -24,14 +25,7 @@ namespace TUMVis {
void TumVisPainter::paint() {
if (getCanvas() == 0)
return;
{
tbb::mutex::scoped_lock lock(_localMutex);
_currentlyRendering = true;
_dirty = false;
}
// get OpenGL focus and setup context
getCanvas()->getGLFocus();
const tgt::ivec2 size = getCanvas()->getSize();
glViewport(0, 0, size.x, size.y);
......@@ -62,8 +56,8 @@ namespace TUMVis {
// TODO: render some nifty error texture
// so long, we do some dummy rendering
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
getCamera()->setPosition(tgt::vec3(0.f,0.f,2.f));
getCamera()->look();
tgt::Camera c(tgt::vec3(0.f,0.f,2.f));
c.look();
glColor3f(1.f, 0.f, 0.f);
tgt::Sphere sphere(.5f, 64, 32);
sphere.render();
......@@ -96,9 +90,6 @@ namespace TUMVis {
}
void TumVisPainter::sizeChanged(const tgt::ivec2& size) {
if (getCanvas()) {
getCanvas()->getGLFocus();
}
_pipeline->setRenderTargetSize(size);
}
......@@ -128,12 +119,9 @@ namespace TUMVis {
}
void TumVisPainter::onPipelineInvalidated() {
if (! _currentlyRendering)
getCanvas()->repaint();
else {
tbb::mutex::scoped_lock lock(_localMutex);
_dirty = true;
}
// TODO: What happens, if the mutex is still acquired?
// Will the render thread woken up as soon as it is released?
_renderCondition.wakeAll();
}
}
\ No newline at end of file
#include "sigslot/sigslot.h"
#include "tbb/include/tbb/mutex.h"
#include "tgt/logmanager.h"
#include "tgt/glcanvas.h"
#include "tgt/painter.h"
#include "tgt/qt/qtthreadedpainter.h"
#include "tgt/event/eventhandler.h"
#include "core/pipeline/visualizationpipeline.h"
......@@ -18,9 +19,9 @@ namespace TUMVis {
*
* \todo
*/
class TumVisPainter : public tgt::Painter, public sigslot::has_slots<> {
class TumVisPainter : public tgt::QtThreadedPainter, public sigslot::has_slots<> {
public:
TumVisPainter(tgt::GLCanvas* canvas, VisualizationPipeline* pipeline);
TumVisPainter(tgt::QtCanvas* canvas, VisualizationPipeline* pipeline);
virtual void paint();
virtual void sizeChanged(const tgt::ivec2& size);
......
......@@ -28,10 +28,10 @@ namespace TUMVis {
}
void AbstractPipeline::onNotify(const ProcessorObserverArgs& poa) {
// tbb::spin_mutex::scoped_lock lock(_localMutex);
// _invalidationLevel.setLevel(InvalidationLevel::INVALID_RESULT);
tbb::spin_mutex::scoped_lock lock(_localMutex);
_invalidationLevel.setLevel(InvalidationLevel::INVALID_RESULT);
execute();
//execute();
}
const DataContainer& AbstractPipeline::getDataContainer() const {
......
......@@ -2,17 +2,20 @@
#include "tgt/camera.h"
#include "tgt/quadrenderer.h"
#include "tgt/quadric.h"
#include "tgt/glcontext.h"
#include "core/datastructures/imagedatarendertarget.h"
namespace TUMVis {
const std::string VisualizationPipeline::loggerCat_ = "TUMVis.core.datastructures.VisualizationPipeline";
VisualizationPipeline::VisualizationPipeline()
VisualizationPipeline::VisualizationPipeline(tgt::GLCanvas* canvas)
: AbstractPipeline()
, tgt::EventListener()
, _renderTargetSize("canvasSize", "Canvas Size", tgt::ivec2(128, 128))
, _renderTargetID("renderTargetID", "Render Target ID", "VisualizationPipeline.renderTarget")
, _canvas(canvas)
{
tgtAssert(canvas != 0, "Canvas must not be 0.");
_data.s_dataAdded.connect(this, &VisualizationPipeline::onDataContainerDataAdded);
}
......@@ -38,6 +41,7 @@ namespace TUMVis {
}
void VisualizationPipeline::init() {
tgt::GLContextScopedLock lock(_canvas->getContext());
AbstractPipeline::init();
}
......
......@@ -23,8 +23,9 @@ namespace TUMVis {
public:
/**
* Creates a VisualizationPipeline.
* \param canvas Canvas hosting the OpenGL context for this pipeline.
*/
VisualizationPipeline();
VisualizationPipeline(tgt::GLCanvas* canvas);
/**
* Virtual Destructor
......@@ -94,6 +95,8 @@ namespace TUMVis {
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
tgt::GLCanvas* _canvas; ///< Canvas hosting the OpenGL context for this pipeline.
static const std::string loggerCat_;
};
......
......@@ -38,6 +38,7 @@ SET(TGT_SOURCES
framebufferobject.cpp
frustum.cpp
glcanvas.cpp
glcontext.cpp
gpucapabilities.cpp
gpucapabilitieswindows.cpp
guiapplication.cpp
......@@ -71,6 +72,9 @@ SET(TGT_SOURCES
#qt
qt/qtapplication.cpp
qt/qtcanvas.cpp
qt/qtglcontext.cpp
qt/qtthreadedcanvas.cpp
qt/qtthreadedpainter.cpp
qt/qttimer.cpp
)
......
......@@ -23,6 +23,7 @@
**********************************************************************/
#include "tgt/glcanvas.h"
#include "tgt/glcontext.h"
#include "tgt/camera.h"
#include "tgt/painter.h"
......@@ -62,6 +63,7 @@ void GLCanvas::sizeChanged(const ivec2& size) {
}
void GLCanvas::paint() {
GLContextScopedLock lock(getContext());
if (painter_)
painter_->paint();
if (autoFlush_) {
......@@ -91,8 +93,10 @@ Painter* GLCanvas::getPainter() const {
void GLCanvas::initPainter() {
if (painter_) {
getGLFocus();
painter_->init();
{
GLContextScopedLock lock(getContext());
painter_->init();
}
painter_->sizeChanged(size_);
}
}
......
......@@ -38,6 +38,7 @@
namespace tgt {
class Camera;
class GLContext;
class Painter;
/**
......@@ -103,7 +104,15 @@ public:
*
* To be overridden by derived Canvas classes.
*/
virtual void getGLFocus() = 0;
//virtual void getGLFocus() = 0;
/**
* 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.
*
* To be overridden by derived Canvas classes.
*/
virtual GLContext* getContext() = 0;
/**
* Toggles fullscreen mode.
......
#include "glcontext.h"
namespace tgt {
GLContext::GLContext() {
}
GLContext::~GLContext() {
}
}
#ifndef GLCONTEXT_H__
#define GLCONTEXT_H__
#include "tgt/vector.h"
namespace tgt {
/**
* Abstract base class for thread-safe OpenGL contexts.
*/
class GLContext {
public:
GLContext();
~GLContext();
virtual void acquire() = 0;
virtual void release() = 0;
virtual void lockAndAcquire() = 0;
virtual void releaseAndUnlock() = 0;
virtual ivec2 getViewportSize() const = 0;
};
/**
* Scoped lockAndAcquire for a GLContext, that automatically unlocks the context on destruction.
*/
class GLContextScopedLock {
public:
GLContextScopedLock(GLContext* context)
: _context(context)
{
if (_context)
_context->lockAndAcquire();
};
~GLContextScopedLock() {
if (_context)
_context->releaseAndUnlock();
}
private:
GLContext* _context;
};
} // namespace tgt
#endif // GLCONTEXT_H__
......@@ -33,8 +33,9 @@ QtCanvas::QtCanvas(const std::string& title,
const ivec2& size,
const Buffers buffers,
QWidget* parent, bool shared, Qt::WFlags f, char* /*name*/)
: QGLWidget(getQGLFormat(buffers), parent, (shared ? shareWidget_ : 0), f),
GLCanvas(title, size, buffers)
: QGLWidget(getQGLFormat(buffers), parent, (shared ? shareWidget_ : 0), f)
, GLCanvas(title, size, buffers)
, _context(this)
{
resize(size.x, size.y);
if (shared && shareWidget_ == 0)
......@@ -58,8 +59,9 @@ QtCanvas::QtCanvas(const std::string& title,
}
QtCanvas::QtCanvas(QWidget* parent, bool shared, Qt::WFlags f, char* /*name*/)
: QGLWidget(parent, (shared ? shareWidget_ : 0), f),
GLCanvas()
: QGLWidget(parent, (shared ? shareWidget_ : 0), f)
, GLCanvas()
, _context(this)
{
if (shared && shareWidget_ == 0)
shareWidget_ = this;
......@@ -107,9 +109,8 @@ void QtCanvas::swap() {
QGLWidget::swapBuffers();
}
void QtCanvas::getGLFocus() {
QGLWidget::doneCurrent();
QGLWidget::makeCurrent();
QtGLContext* QtCanvas::getContext() {
return &_context;
}
void QtCanvas::toggleFullScreen() {
......
......@@ -26,6 +26,7 @@
#define TGT_QTCANVAS_H
#include "tgt/glcanvas.h"
#include "tgt/qt/qtglcontext.h"
#include <QGLWidget>
#include <QGLFormat>
......@@ -103,10 +104,11 @@ public:
virtual void swap();
/**
* 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.
* 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 void getGLFocus();
virtual QtGLContext* getContext();
virtual void toggleFullScreen();
virtual void enterEvent(QEvent* e);
......@@ -134,7 +136,8 @@ public:
static QGLFormat getQGLFormat(const Buffers buffers);
protected:
static QGLWidget* shareWidget_; ///< widget that this canvas shares the OpenGL context with
static QGLWidget* shareWidget_; ///< widget that this canvas shares the OpenGL context with
QtGLContext _context; ///< OpenGL context of this canvas
};
......
#include "qtglcontext.h"
#include "tgt/qt/qtcanvas.h"
#include "tgt/assert.h"
namespace tgt {
QtGLContext::QtGLContext(QtCanvas* canvas)
: GLContext()
, _canvas(canvas)
{
tgtAssert(canvas != 0, "Canvas must not be 0.");
}
QtGLContext::~QtGLContext() {
}
void QtGLContext::acquire() {
_canvas->makeCurrent();
}
void QtGLContext::release() {
_canvas->doneCurrent();
}
void QtGLContext::lockAndAcquire() {
_renderMutex.lock();
acquire();
}
void QtGLContext::releaseAndUnlock() {
release();
_renderMutex.unlock();
}
ivec2 QtGLContext::getViewportSize() const {
return _canvas->getSize();
}
QMutex& QtGLContext::getRenderMutex() {
return _renderMutex;
}
}
#ifndef QTGLCONTEXT_H__
#define QTGLCONTEXT_H__
#include "tgt/glcontext.h"
#include <qmutex.h>
namespace tgt {
class QtCanvas;
/**
* Manages the OpenGL context of a QtCanvas and offers methods for thread-safe access.
*/
class QtGLContext : public GLContext {
public:
/**
* Creates a new QtGLContext for the OpenGL context of \a canvas.
* \param canvas QtCanvas with the OpenGL context to manage
*/
QtGLContext(QtCanvas* canvas);
/**
* Destructor
*/
~QtGLContext();
/**
* Acquires the OpenGL context.
*/
virtual void acquire();
/**
* Releases the OpenGL context.
*/
virtual void release();
/**
* Acquires the OpenGL context of the canvas and locks it for thread-safety
* \note Do not forget to call releaseAndUnlock() when finished rendering!
*/
virtual void lockAndAcquire();
/**
* Releases and unlocks the OpenGL context of the canvas.
*/
virtual void releaseAndUnlock();
/**
* Returns the viewport size of the managed OpenGL context.
* \return _canvas->getSize()
*/
virtual ivec2 getViewportSize() const;
/**
* Returns the mutex protecting the context for multi-threaded access.
* \return _renderMutex
*/
QMutex& getRenderMutex();
protected:
QtCanvas* _canvas; ///< QtCanvas with the OpenGL context to manage
QMutex _renderMutex; ///< Mutex protecting the context for multi-threaded access
};
} // namespace tgt
#endif // QTGLCONTEXT_H__
#include "qtthreadedcanvas.h"
#include "tgt/assert.h"
#include "tgt/qt/qtthreadedpainter.h"
namespace tgt {
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*/)
: QtCanvas(title, size, buffers, parent, shared, f, name)
{
}
QtThreadedCanvas::~QtThreadedCanvas() {
}
void QtThreadedCanvas::startRendering() {
if (painter_ != 0) {
QtThreadedPainter* painter = static_cast<QtThreadedPainter*>(painter_);
painter->start();
}
}
void QtThreadedCanvas::stopRendering() {
if (painter_ != 0) {
QtThreadedPainter* painter = static_cast<QtThreadedPainter*>(painter_);
painter->stop();
painter->wait();
}
}
void QtThreadedCanvas::resizeGL(int w, int h) {
if (painter_ != 0) {
painter_->sizeChanged(ivec2(w, h));
}
}
void QtThreadedCanvas::paintGL() {
// all painting done in QtThreadedPainter
}
void QtThreadedCanvas::repaint() {
// all painting done in QtThreadedPainter
}
void QtThreadedCanvas::closeEvent(QCloseEvent *evt) {
stopRendering();
QtCanvas::closeEvent(evt);
}
void QtThreadedCanvas::paint() {
// all painting done in QtThreadedPainter
}
void QtThreadedCanvas::setPainter(Painter* p, bool initPainter /* = true */) {
tgtAssert(dynamic_cast<QtThreadedPainter*>(p) != 0, "Painter must be of type QtThreadedPainter!");
GLCanvas::setPainter(p, initPainter);
}
}
#ifndef QTTHREADEDCANVAS_H__
#define QTTHREADEDCANVAS_H__
#include "tgt/qt/qtcanvas.h"
#include <qmutex.h>
#include <qwaitcondition.h>
namespace tgt {
/**
* Subclass of QtCanvas that manages a separate rendering thread.
*/
class QtThreadedCanvas : public tgt::QtCanvas {
public:
QtThreadedCanvas(
const std::string& title = "",
const tgt::ivec2& size = tgt::ivec2(tgt::GLCanvas::DEFAULT_WINDOW_WIDTH, tgt::GLCanvas::DEFAULT_WINDOW_HEIGHT),
const tgt::GLCanvas::Buffers buffers = tgt::GLCanvas::RGBADD,
QWidget* parent = 0, bool shared = false, Qt::WFlags f = 0, char* name = 0);
~QtThreadedCanvas();
virtual void setPainter(tgt::Painter* p, bool initPainter = true);
void startRendering();
void stopRendering();
/**
* 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();
/**
* Called by Qt if there is a paint event; it uses the \a painter_ to paint() something.
*/
virtual void paint();
/**
* 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();
virtual void closeEvent(QCloseEvent *evt);
protected:
};
}
#endif // QTTHREADEDCANVAS_H__
#include "qtthreadedpainter.h"
#include "tgt/qt/qtcanvas.h"
#include "tgt/qt/qtthreadedcanvas.h"
namespace tgt {
QtThreadedPainter::QtThreadedPainter(QtCanvas* canvas)