05.11., 9:00 - 11:00: Due to updates GitLab may be unavailable for some minutes between 09:00 and 11:00.

Started refactoring the CAMPVis OpenGL Wrapping API:

The new OpenGL wrapping API allows for full multi-threaded access to OpenGL contexts. Instead of one single thread scheduling all OpenGL jobs for all contexts, the new GlContextManager allows for OpenGL access from multiple threads while ensuring that each OpenGL context is acquired by only one thread at a time.

Detailed list of changes:
* tgt::GlContextManager keeping track of which threads acquire which OpenGL contexts and which threads currently have a context acquired.
* OpenGLJobProcessor does no longer schedules and execute the OpenGL calls for all existing contexts, but only for one single context that can be used for background tasks or other jobs that explicitly need a valid OpenGL context.
* AbstractPipeline now implements the Runnable interface and thus runs in it's own thread. This thread also owns the pipeline's OpenGL context.
* AbstractPipeline has a new pure virtual method executePipeline() that has to perform all computations done by the pipeline.
* AbstractPipeline now takes directly care of calling Painter::paint() of the pipeline's canvas (instead of signalling the Painter). However, the Painter interface needs further cleanup.
* AutoEvaluationPipeline was adapted to the new AbstractPipeline API, hence executing processors is no longer delegated to the OpenGLJobProcessor or the SimpleJobProcessor but entirely done in AutoEvaluationPipeline::executePipeline() and thus in the pipeline's thread.
* Adjusted CampVisApplication, DataContainerInspectorWidget, and GeometryTransferFunctionEditor to the new API.
parent 2e1956c9
......@@ -74,9 +74,7 @@ namespace campvis {
sigslot::signal_manager::init();
sigslot::signal_manager::getRef().start();
_mainWindow = new MainWindow(this);
tgt::GlContextManager::init();
OpenGLJobProcessor::init();
SimpleJobProcessor::init();
QtJobProcessor::init();
......@@ -106,11 +104,11 @@ namespace campvis {
tgt::init(featureset);
LogMgr.getConsoleLog()->addCat("", true);
_mainWindow = new MainWindow(this);
// create a local OpenGL context and init GL
_localContext = new QtThreadedCanvas("", tgt::ivec2(16, 16));
tgt::GLContextScopedLock lock(_localContext);
tgt::GlContextManager::getRef().registerContextAndInitGlew(_localContext);
tgt::GlContextManager::getRef().registerContextAndInitGlew(_localContext, "Local Context");
tgt::initGL(featureset);
ShdrMgr.setDefaultGlslVersion("330");
......@@ -150,8 +148,6 @@ namespace campvis {
LERROR("Your system does not support GLSL Shader Version 3.30, which is mandatory. CAMPVis will probably not work as intended.");
}
GLJobProc.start();
GLJobProc.registerContext(_localContext);
// load textureData from file
tgt::TextureReaderTga trt;
......@@ -179,6 +175,7 @@ namespace campvis {
if (! _luaVmState->execString("inspect = require 'inspect'"))
LERROR("Error setting up Lua VM.");
#endif
GLCtxtMgr.releaseContext(_localContext, false);
// parse argument list and create pipelines
QStringList pipelinesToAdd = this->arguments();
......@@ -189,6 +186,9 @@ namespace campvis {
addPipeline(pipelinesToAdd[i].toStdString(), p);
}
GLJobProc.setContext(_localContext);
GLJobProc.start();
_initialized = true;
}
......@@ -236,7 +236,7 @@ namespace campvis {
tgtAssert(_initialized, "Tried to run uninitialized CampVisApplication.");
// disconnect OpenGL context from this thread so that the other threads can acquire an OpenGL context.
tgt::GlContextManager::getRef().releaseCurrentContext();
//tgt::GlContextManager::getRef().releaseCurrentContext();
_mainWindow->show();
......@@ -251,7 +251,6 @@ namespace campvis {
// create canvas and painter for the pipeline and connect all together
tgt::QtThreadedCanvas* canvas = new tgt::QtThreadedCanvas("CAMPVis", tgt::ivec2(512, 512));
GLJobProc.registerContext(canvas);
canvas->init();
CampVisPainter* painter = new CampVisPainter(canvas, pipeline);
......@@ -265,10 +264,7 @@ namespace campvis {
_pipelineWindows[pipeline] = _mainWindow->addVisualizationPipelineWidget(name, canvas);
// initialize context (GLEW) and pipeline in OpenGL thread)
GLJobProc.enqueueJob(
canvas,
makeJobOnHeap<CampVisApplication, tgt::GLCanvas*, AbstractPipeline*>(this, &CampVisApplication::initGlContextAndPipeline, canvas, pipeline),
OpenGLJobProcessor::SerialJob);
initGlContextAndPipeline(canvas, pipeline);
#ifdef CAMPVIS_HAS_SCRIPTING
if (! _luaVmState->injectObjectPointerToTable(pipeline, "campvis::AutoEvaluationPipeline *", "pipelines", static_cast<int>(_pipelines.size())))
......@@ -278,11 +274,13 @@ namespace campvis {
_luaVmState->execString("inspect(pipelines)");
#endif
GLCtxtMgr.releaseContext(canvas, false);
s_PipelinesChanged.emitSignal();
pipeline->start();
}
void CampVisApplication::initGlContextAndPipeline(tgt::GLCanvas* canvas, AbstractPipeline* pipeline) {
tgt::GlContextManager::getRef().registerContextAndInitGlew(canvas);
tgt::GlContextManager::getRef().registerContextAndInitGlew(canvas, pipeline->getName());
pipeline->init();
LGL_ERROR;
......@@ -311,11 +309,10 @@ namespace campvis {
void CampVisApplication::rebuildAllShadersFromFiles() {
// rebuilding all shaders has to be done from OpenGL context, use the local one.
GLJobProc.enqueueJob(_localContext, makeJobOnHeap(this, &CampVisApplication::triggerShaderRebuild), OpenGLJobProcessor::SerialJob);
GLJobProc.enqueueJob(makeJobOnHeap(this, &CampVisApplication::triggerShaderRebuild));
}
void CampVisApplication::triggerShaderRebuild() {
if (! ShdrMgr.rebuildAllShadersFromFile()) {
LERROR("Could not rebuild all shaders from file.");
return;
......
......@@ -161,7 +161,7 @@ namespace campvis {
}
void CampVisPainter::repaint() {
GLJobProc.enqueueJob(getCanvas(), makeJobOnHeap(this, &CampVisPainter::paint), OpenGLJobProcessor::PaintJob);
//GLJobProc.enqueueJob(getCanvas(), makeJobOnHeap(this, &CampVisPainter::paint), OpenGLJobProcessor::PaintJob);
}
void CampVisPainter::onRenderTargetChanged() {
......
......@@ -67,15 +67,8 @@ namespace campvis {
{
static_cast<Geometry1DTransferFunction*>(p_transferFunction.getTF())->addGeometry(TFGeometry1D::createQuad(tgt::vec2(0.f, 1.f), tgt::col4(0, 0, 0, 255), tgt::col4(255, 255, 255, 255)));
makeCurrent();
// Init GLEW for this context
GLenum err = glewInit();
if (err != GLEW_OK) {
// Problem: glewInit failed, something is seriously wrong.
tgtAssert(false, "glewInit failed");
std::cerr << "glewInit failed, error: " << glewGetErrorString(err) << std::endl;
exit(EXIT_FAILURE);
}
GLCtxtMgr.registerContextAndInitGlew(this, "DataContainerInspector");
GLCtxtMgr.releaseContext(this, false);
addProperty(p_currentSlice);
addProperty(p_transferFunction);
......@@ -117,7 +110,6 @@ namespace campvis {
void DataContainerInspectorCanvas::init() {
initAllProperties();
GLJobProc.registerContext(this);
_paintShader = ShdrMgr.load("core/glsl/passthrough.vert", "application/glsl/datacontainerinspector.frag", "");
_paintShader->setAttributeLocation(0, "in_Position");
_paintShader->setAttributeLocation(1, "in_TexCoords");
......@@ -147,7 +139,8 @@ namespace campvis {
_textures.clear();
ShdrMgr.dispose(_paintShader);
delete _quad;
GLJobProc.deregisterContext(this);
GLCtxtMgr.removeContext(this);
}
QSize DataContainerInspectorCanvas::sizeHint() const {
......@@ -261,8 +254,11 @@ namespace campvis {
void DataContainerInspectorCanvas::invalidate() {
// only if inited
if (_quad != 0 && _paintShader != 0)
GLJobProc.enqueueJob(this, makeJobOnHeap(this, &DataContainerInspectorCanvas::paint), OpenGLJobProcessor::PaintJob);
if (_quad != 0 && _paintShader != 0) {
// TODO: check, whether this should be done in an extra thread
tgt::GLContextScopedLock lock(this);
paint();
}
}
void DataContainerInspectorCanvas::createQuad() {
......
......@@ -467,10 +467,8 @@ namespace campvis {
if (! filename.isEmpty()) {
// Texture access needs OpenGL context - dispatch method call:
GLJobProc.enqueueJob(
_canvas,
makeJobOnHeap(&DataContainerInspectorWidget::saveToFile, handle, filename.toStdString()),
OpenGLJobProcessor::SerialJob);
tgt::GLContextScopedLock lock(_canvas);
saveToFile(handle, filename.toStdString());
}
}
}
......
......@@ -70,8 +70,6 @@ namespace campvis {
Geometry1DTransferFunctionEditor::~Geometry1DTransferFunctionEditor() {
disconnectFromTf();
if (OpenGLJobProcessor::isInited())
GLJobProc.deregisterContext(_canvas);
if (tgt::GlContextManager::isInited())
tgt::GlContextManager::getRef().removeContext(_canvas);
}
......@@ -177,9 +175,11 @@ namespace campvis {
}
void Geometry1DTransferFunctionEditor::sizeChanged(const tgt::ivec2& size) {
tbb::mutex::scoped_lock lock(_localMutex);
for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
(*it)->setViewportSize(size);
{
tbb::mutex::scoped_lock lock(_localMutex);
for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
(*it)->setViewportSize(size);
}
}
invalidate();
}
......@@ -220,7 +220,9 @@ namespace campvis {
}
void Geometry1DTransferFunctionEditor::invalidate() {
GLJobProc.enqueueJob(_canvas, makeJobOnHeap(this, &Geometry1DTransferFunctionEditor::paint), OpenGLJobProcessor::PaintJob);
// TODO: check, whether this should be done in an extra thread
tgt::GLContextScopedLock lock(_canvas);
paint();
}
void Geometry1DTransferFunctionEditor::setupGUI() {
......@@ -237,8 +239,8 @@ namespace campvis {
_layout->addWidget(lblOpacityBottom, 3, 0, 1, 1, Qt::AlignRight);
_canvas = new tgt::QtThreadedCanvas("", tgt::ivec2(256, 128), tgt::GLCanvas::RGBA_BUFFER, 0, false);
GLJobProc.registerContext(_canvas);
GLJobProc.enqueueJob(_canvas, makeJobOnHeap<tgt::GlContextManager, tgt::GLCanvas*>(tgt::GlContextManager::getPtr(), &tgt::GlContextManager::registerContextAndInitGlew, _canvas), OpenGLJobProcessor::SerialJob);
GLCtxtMgr.registerContextAndInitGlew(_canvas, "Geometry1DTransferFunctionEditor");
GLCtxtMgr.releaseContext(_canvas, false);
_canvas->setPainter(this, false);
_layout->addWidget(_canvas, 1, 1, 3, 3);
......@@ -275,7 +277,7 @@ namespace campvis {
_canvas->getEventHandler()->clearEventListeners();
for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
if (WholeTFGeometryManipulator* tester = dynamic_cast<WholeTFGeometryManipulator*>(*it)) {
tester->s_selected.disconnect(this);
tester->s_selected.disconnect(this);
}
delete *it;
}
......
......@@ -63,8 +63,6 @@ namespace campvis {
Geometry2DTransferFunctionEditor::~Geometry2DTransferFunctionEditor() {
disconnectFromTf();
if (OpenGLJobProcessor::isInited())
GLJobProc.deregisterContext(_canvas);
if (tgt::GlContextManager::isInited())
tgt::GlContextManager::getRef().removeContext(_canvas);
}
......@@ -161,9 +159,11 @@ namespace campvis {
}
void Geometry2DTransferFunctionEditor::sizeChanged(const tgt::ivec2& size) {
tbb::mutex::scoped_lock lock(_localMutex);
for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
(*it)->setViewportSize(size);
{
tbb::mutex::scoped_lock lock(_localMutex);
for (std::vector<AbstractTFGeometryManipulator*>::iterator it = _manipulators.begin(); it != _manipulators.end(); ++it) {
(*it)->setViewportSize(size);
}
}
invalidate();
}
......@@ -198,7 +198,9 @@ namespace campvis {
}
void Geometry2DTransferFunctionEditor::invalidate() {
GLJobProc.enqueueJob(_canvas, makeJobOnHeap(this, &Geometry2DTransferFunctionEditor::paint), OpenGLJobProcessor::PaintJob);
// TODO: check, whether this should be done in an extra thread
tgt::GLContextScopedLock lock(_canvas);
paint();
}
void Geometry2DTransferFunctionEditor::setupGUI() {
......@@ -215,8 +217,8 @@ namespace campvis {
_layout->addWidget(lblOpacityBottom, 3, 0, 1, 1, Qt::AlignRight);
_canvas = new tgt::QtThreadedCanvas("", tgt::ivec2(256, 128), tgt::GLCanvas::RGBA_BUFFER, 0, false);
GLJobProc.registerContext(_canvas);
GLJobProc.enqueueJob(_canvas, makeJobOnHeap<tgt::GlContextManager, tgt::GLCanvas*>(tgt::GlContextManager::getPtr(), &tgt::GlContextManager::registerContextAndInitGlew, _canvas), OpenGLJobProcessor::SerialJob);
GLCtxtMgr.registerContextAndInitGlew(_canvas, "Geometry2DTransferFunctionEditor");
GLCtxtMgr.releaseContext(_canvas, false);
_canvas->setPainter(this, false);
_layout->addWidget(_canvas, 1, 1, 3, 3);
......
......@@ -29,6 +29,8 @@
#include "tgt/exception.h"
#include "tgt/glcanvas.h"
#include "tgt/glcontextmanager.h"
#include "tgt/painter.h"
#include "tgt/tgt_gl.h"
#include "core/pipeline/visualizationprocessor.h"
......@@ -78,17 +80,18 @@ namespace campvis {
tgtAssert(_data != 0, "Pointer to the DataContainer for this pipeline must not be 0!");
_enabled = false;
_pipelineDirty = true;
addProperty(_renderTargetID);
addProperty(_canvasSize);
}
AbstractPipeline::~AbstractPipeline() {
}
void AbstractPipeline::init() {
_renderTargetID.s_changed.connect<AbstractPipeline>(this, &AbstractPipeline::onPropertyChanged);
_data->s_dataAdded.connect(this, &AbstractPipeline::onDataContainerDataAdded);
initAllProperties();
......@@ -104,6 +107,8 @@ namespace campvis {
}
void AbstractPipeline::deinit() {
stop();
deinitAllProperties();
// deinitialize all processors:
......@@ -117,18 +122,51 @@ namespace campvis {
}
}
_data->s_dataAdded.disconnect(this);
_renderTargetID.s_changed.disconnect(this);
// clear DataContainer
_data->clear();
}
void AbstractPipeline::run() {
std::unique_lock<std::mutex> lock(*tgt::GlContextManager::getRef().getGlMutexForContext(_canvas));
tgt::GlContextManager::getRef().acquireContext(_canvas, false);
while (! _stopExecution) {
if (_enabled && _pipelineDirty) {
// mark pipeline as not dirty
_pipelineDirty = false;
// execute pipeline
executePipeline();
// paint on canvas
// FIXME: clean up the whole painter crap...
_canvas->getPainter()->paint();
}
if (!_enabled || !_pipelineDirty) {
tgt::GlContextManager::getRef().releaseContext(_canvas, false);
_evaluationCondition.wait(lock);
tgt::GlContextManager::getRef().acquireContext(_canvas, false);
}
}
tgt::GlContextManager::getRef().releaseContext(_canvas, false);
}
void AbstractPipeline::stop() {
_stopExecution = true;
_evaluationCondition.notify_all();
Runnable::stop();
}
void AbstractPipeline::onPropertyChanged(const AbstractProperty* prop) {
if (prop == &_renderTargetID) {
s_renderTargetChanged.emitSignal();
setPipelineDirty();
}
else if (prop == &_canvasSize && _canvas != 0 && !_ignoreCanvasSizeUpdate) {
else if (prop == &_canvasSize && _canvas != nullptr && !_ignoreCanvasSizeUpdate) {
if (_canvasSize.getValue() != _canvas->getSize()) {
_ignoreCanvasSizeUpdate = true;
_canvas->setSize(_canvasSize.getValue());
......@@ -173,6 +211,30 @@ namespace campvis {
}
}
void AbstractPipeline::executeProcessorAndCheckOpenGLState(AbstractProcessor* processor) {
AbstractPipeline::executeProcessor(processor);
#ifdef CAMPVIS_DEBUG
tgtAssert(getGlBool(GL_DEPTH_TEST) == false, "Invalid OpenGL state after processor execution, GL_DEPTH_TEST != false.");
tgtAssert(getGlBool(GL_SCISSOR_TEST) == false, "Invalid OpenGL state after processor execution, GL_SCISSOR_TEST != false.");
tgtAssert(getGlInt(GL_CULL_FACE_MODE) == GL_BACK, "Invalid OpenGL state after processor execution, GL_CULL_FACE_MODE != GL_BACk.");
tgtAssert(getGlInt(GL_DEPTH_FUNC) == GL_LESS, "Invalid OpenGL state after processor execution, GL_DEPTH_FUNC != GL_LESS.");
tgtAssert(getGlFloat(GL_DEPTH_CLEAR_VALUE) == 1.f, "Invalid OpenGL state after processor execution, GL_DEPTH_CLEAR_VALUE != 1.f.");
tgtAssert(getGlFloat(GL_RED_SCALE) == 1.f, "Invalid OpenGL state after processor execution, GL_RED_SCALE != 1.f.");
tgtAssert(getGlFloat(GL_GREEN_SCALE) == 1.f, "Invalid OpenGL state after processor execution, GL_GREEN_SCALE != 1.f.");
tgtAssert(getGlFloat(GL_BLUE_SCALE) == 1.f, "Invalid OpenGL state after processor execution, GL_BLUE_SCALE != 1.f.");
tgtAssert(getGlFloat(GL_ALPHA_SCALE) == 1.f, "Invalid OpenGL state after processor execution, GL_ALPHA_SCALE != 1.f.");
tgtAssert(getGlFloat(GL_RED_BIAS) == 0.f, "Invalid OpenGL state after processor execution, GL_RED_BIAS != 0.f.");
tgtAssert(getGlFloat(GL_GREEN_BIAS) == 0.f, "Invalid OpenGL state after processor execution, GL_GREEN_BIAS != 0.f.");
tgtAssert(getGlFloat(GL_BLUE_BIAS) == 0.f, "Invalid OpenGL state after processor execution, GL_BLUE_BIAS != 0.f.");
tgtAssert(getGlFloat(GL_ALPHA_BIAS) == 0.f, "Invalid OpenGL state after processor execution, GL_ALPHA_BIAS != 0.f.");
#endif
}
const std::vector<AbstractProcessor*>& AbstractPipeline::getProcessors() const {
return _processors;
}
......@@ -183,6 +245,7 @@ namespace campvis {
void AbstractPipeline::setEnabled(bool enabled) {
_enabled = enabled;
setPipelineDirty();
}
void AbstractPipeline::onEvent(tgt::Event* e) {
......@@ -225,44 +288,10 @@ namespace campvis {
_processors.push_back(processor);
}
void AbstractPipeline::lockGLContextAndExecuteProcessor(AbstractProcessor* processor) {
tgtAssert(_canvas != 0, "Set a valid canvas before calling this method!");
GLJobProc.enqueueJob(
_canvas,
makeJobOnHeap<AbstractPipeline, AbstractProcessor*>(this, &AbstractPipeline::executeProcessor, processor),
OpenGLJobProcessor::SerialJob);
}
void AbstractPipeline::executeProcessorAndCheckOpenGLState(AbstractProcessor* processor) {
AbstractPipeline::executeProcessor(processor);
#ifdef CAMPVIS_DEBUG
tgtAssert(getGlBool(GL_DEPTH_TEST) == false, "Invalid OpenGL state after processor execution, GL_DEPTH_TEST != false.");
tgtAssert(getGlBool(GL_SCISSOR_TEST) == false, "Invalid OpenGL state after processor execution, GL_SCISSOR_TEST != false.");
tgtAssert(getGlInt(GL_CULL_FACE_MODE) == GL_BACK, "Invalid OpenGL state after processor execution, GL_CULL_FACE_MODE != GL_BACk.");
tgtAssert(getGlInt(GL_DEPTH_FUNC) == GL_LESS, "Invalid OpenGL state after processor execution, GL_DEPTH_FUNC != GL_LESS.");
tgtAssert(getGlFloat(GL_DEPTH_CLEAR_VALUE) == 1.f, "Invalid OpenGL state after processor execution, GL_DEPTH_CLEAR_VALUE != 1.f.");
void AbstractPipeline::setPipelineDirty() {
_pipelineDirty = true;
tgtAssert(getGlFloat(GL_RED_SCALE) == 1.f, "Invalid OpenGL state after processor execution, GL_RED_SCALE != 1.f.");
tgtAssert(getGlFloat(GL_GREEN_SCALE) == 1.f, "Invalid OpenGL state after processor execution, GL_GREEN_SCALE != 1.f.");
tgtAssert(getGlFloat(GL_BLUE_SCALE) == 1.f, "Invalid OpenGL state after processor execution, GL_BLUE_SCALE != 1.f.");
tgtAssert(getGlFloat(GL_ALPHA_SCALE) == 1.f, "Invalid OpenGL state after processor execution, GL_ALPHA_SCALE != 1.f.");
tgtAssert(getGlFloat(GL_RED_BIAS) == 0.f, "Invalid OpenGL state after processor execution, GL_RED_BIAS != 0.f.");
tgtAssert(getGlFloat(GL_GREEN_BIAS) == 0.f, "Invalid OpenGL state after processor execution, GL_GREEN_BIAS != 0.f.");
tgtAssert(getGlFloat(GL_BLUE_BIAS) == 0.f, "Invalid OpenGL state after processor execution, GL_BLUE_BIAS != 0.f.");
tgtAssert(getGlFloat(GL_ALPHA_BIAS) == 0.f, "Invalid OpenGL state after processor execution, GL_ALPHA_BIAS != 0.f.");
#endif
}
void AbstractPipeline::onDataContainerDataAdded(std::string name, DataHandle dh) {
if (name == _renderTargetID.getValue()) {
s_renderTargetChanged.emitSignal();
}
if (_enabled)
_evaluationCondition.notify_all();
}
}
......@@ -28,6 +28,7 @@
#include "sigslot/sigslot.h"
#include "tgt/logmanager.h"
#include "tgt/runnable.h"
#include "tgt/vector.h"
#include "tgt/event/eventhandler.h"
#include "tgt/event/eventlistener.h"
......@@ -54,7 +55,7 @@ namespace campvis {
/**
* Abstract base class for CAMPVis Pipelines.
*/
class CAMPVIS_CORE_API AbstractPipeline : public HasPropertyCollection, public tgt::EventHandler, public tgt::EventListener {
class CAMPVIS_CORE_API AbstractPipeline : public HasPropertyCollection, public tgt::Runnable, public tgt::EventHandler, public tgt::EventListener {
public:
/**
* Creates a AbstractPipeline.
......@@ -114,6 +115,14 @@ namespace campvis {
*/
virtual void onEvent(tgt::Event* e);
/**
* Entrance point for the pipeline thread.
* \see Runnable::run
*/
virtual void run();
/// \see Runnable::stop
virtual void stop();
/**
* Returns the DataContainer of this pipeline, const version.
......@@ -169,29 +178,26 @@ namespace campvis {
*/
const std::string& getRenderTargetID() const;
/**
* returns the currently set canvas for the pipeline
*/
tgt::GLCanvas * getCanvas() {
return _canvas;
}
/// Signal emitted when the pipeline's render target has changed
sigslot::signal0 s_renderTargetChanged;
protected:
/**
* Executes the processor \a processor on the pipeline's data and locks its properties meanwhile.
* \param processor Processor to execute.
* Sets the resultDirty flag of this pipeline and starts its execution if necessary.
*/
void executeProcessor(AbstractProcessor* processor);
void setPipelineDirty();
/**
* Acquires and locks the OpenGL context, executes the processor \a processor on the pipeline's data
* and locks its properties meanwhile.
* Executes this pipeline.
* To be implemented in the subclass.
*/
virtual void executePipeline() = 0;
/**
* Executes the processor \a processor on the pipeline's data and locks its properties meanwhile.
* \param processor Processor to execute.
*/
void lockGLContextAndExecuteProcessor(AbstractProcessor* processor);
void executeProcessor(AbstractProcessor* processor);
/**
* Executes \a processor and afterwards checks the OpenGL state to be valid.
......@@ -207,19 +213,11 @@ namespace campvis {
*/
virtual void onPropertyChanged(const AbstractProperty* prop);
/**
* 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.
*/
virtual void onDataContainerDataAdded(std::string name, DataHandle dh);
/// Pointer to the DataContainer containing local working set of data for this Pipeline, must not be 0.
DataContainer* _data;
std::vector<AbstractProcessor*> _processors; ///< List of all processors of this pipeline
tbb::atomic<bool> _enabled; ///< flag whether this pipeline is currently enabled
tgt::GLCanvas* _canvas; ///< Canvas hosting the OpenGL context for this pipeline.
IVec2Property _canvasSize; ///< original canvas size
......@@ -227,6 +225,11 @@ namespace campvis {
DataNameProperty _renderTargetID; ///< ID of the render target image to be rendered to the canvas
private:
std::condition_variable _evaluationCondition; ///< conditional wait to be used when the pipeline currently does not need to be executed.
tbb::atomic<bool> _enabled; ///< flag whether this pipeline is currently enabled
tbb::atomic<bool> _pipelineDirty; ///< Flag whether this pipeline is dirty and executePipeline() needs to be called.
static const std::string loggerCat_;
};
......
......@@ -62,26 +62,10 @@ namespace campvis {
}
void AutoEvaluationPipeline::onProcessorInvalidated(AbstractProcessor* processor) {
if (_canvas == 0 || _enabled == false)
if (_canvas == nullptr || getEnabled() == false)
return;
tbb::concurrent_hash_map<AbstractProcessor*, bool>::const_accessor a;
if (_isVisProcessorMap.find(a, processor)) {
if (a->second) {
// is VisualizationProcessor
GLJobProc.enqueueJob(
_canvas,
makeJobOnHeap<AutoEvaluationPipeline, AbstractProcessor*>(this, &AutoEvaluationPipeline::executeProcessorAndCheckOpenGLState, processor),
OpenGLJobProcessor::SerialJob);
}
else {
SimpleJobProc.enqueueJob(makeJob<AutoEvaluationPipeline, AbstractProcessor*>(this, &AutoEvaluationPipeline::executeProcessor, processor));
}
}
else {
tgtAssert(false, "Could not find processor in processor map.");
LWARNING("Caught invalidation of a non-registered processor!");
}
setPipelineDirty();
}
void AutoEvaluationPipeline::addProcessor(AbstractProcessor* processor) {
......@@ -91,6 +75,15 @@ namespace campvis {
AbstractPipeline::addProcessor(processor);
}
void AutoEvaluationPipeline::executePipeline() {
// execute each processor (we do this n*n times, as we might have a complex dependency graph)
for (size_t i = 0; i < _processors.size(); ++i) {
for (size_t i = 0; i < _processors.size(); ++i) {
_processors[i]->process(getDataContainer());
}
}
}
void AutoEvaluationPipeline::onDataNamePropertyChanged(const AbstractProperty* prop) {
// static_cast is safe since this slot only get called for DataNameProperties
// const_cast is not beautiful but safe here as well
......@@ -151,8 +144,6 @@ namespace campvis {
++it;
}
}
AbstractPipeline::onDataContainerDataAdded(name, dh);
}
void AutoEvaluationPipeline::findDataNamePropertiesAndAddToPortMap(const HasPropertyCollection* hpc) {
......
......@@ -64,6 +64,9 @@ namespace campvis {
virtual void addProcessor(AbstractProcessor* processor);
protected:
/// \see AbstractPipeline::executePipeline()
virtual void executePipeline();
/**
* Slot getting called when one of the observed processors got invalidated.
* The default behaviour is to dispatch a job to execute the invalidated processor and emit the s_invalidated signal.
......
......@@ -34,14 +34,10 @@
namespace campvis {
std::thread::id OpenGLJobProcessor::_this_thread_id;