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 67cb4073 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Refactoring pipeline concept #3: Introducing PipelineFactory and PipelineRegistrar

parent 2d55cb00
......@@ -66,23 +66,23 @@ int main(int argc, char** argv) {
#endif
CampVisApplication app(argc, argv);
// app.addVisualizationPipeline("Advanced Ultrasound Visualization", new AdvancedUsVis());
// app.addVisualizationPipeline("Confidence Map Generation", new CmBatchGeneration());
// app.addVisualizationPipeline("IXPV", new IxpvDemo());
app.addVisualizationPipeline("SliceVis", new SliceVis());
// app.addVisualizationPipeline("DVRVis", new DVRVis());
// app.addVisualizationPipeline("VolumeRendererDemo", new VolumeRendererDemo());
app.addVisualizationPipeline("VolumeExplorerDemo", new VolumeExplorerDemo());
// app.addVisualizationPipeline("Advanced Ultrasound Visualization", new AdvancedUsVis(app.getDataContainer()));
// app.addVisualizationPipeline("Confidence Map Generation", new CmBatchGeneration(app.getDataContainer()));
// app.addVisualizationPipeline("IXPV", new IxpvDemo(app.getDataContainer()));
app.addVisualizationPipeline("SliceVis", new SliceVis(app.getDataContainer()));
// app.addVisualizationPipeline("DVRVis", new DVRVis(app.getDataContainer()));
// app.addVisualizationPipeline("VolumeRendererDemo", new VolumeRendererDemo(app.getDataContainer()));
app.addVisualizationPipeline("VolumeExplorerDemo", new VolumeExplorerDemo(app.getDataContainer()));
#ifdef HAS_KISSCL
// app.addVisualizationPipeline("DVR with OpenCL", new OpenCLPipeline());
// app.addVisualizationPipeline("DVR with OpenCL", new OpenCLPipeline(app.getDataContainer()));
#endif
#ifdef CAMPVIS_HAS_MODULE_SCR_MSK
app.addVisualizationPipeline("US Compounding", new UsCompounding());
app.addVisualizationPipeline("US Compounding", new UsCompounding(app.getDataContainer()));
#endif
#ifdef CAMPVIS_HAS_MODULE_COLUMBIA
app.addVisualizationPipeline("Columbia", new Columbia1());
app.addVisualizationPipeline("Columbia", new Columbia1(app.getDataContainer()));
#endif
......
......@@ -50,6 +50,7 @@
#include "core/tools/quadrenderer.h"
#include "core/pipeline/abstractpipeline.h"
#include "core/pipeline/autoevaluationpipeline.h"
#include "core/pipeline/pipelinefactory.h"
namespace campvis {
......@@ -173,6 +174,8 @@ namespace campvis {
GLJobProc.start();
_initialized = true;
LINFO(PipelineFactory::getRef().toString());
}
void CampVisApplication::deinit() {
......@@ -270,4 +273,12 @@ namespace campvis {
_mainWindow->addDockWidget(area, dock);
}
DataContainer* CampVisApplication::getDataContainer() {
return &_dataContainer;
}
const DataContainer* CampVisApplication::getDataContainer() const {
return &_dataContainer;
}
}
......@@ -38,6 +38,8 @@
#include <utility>
#include <vector>
#include "core/datastructures/datacontainer.h"
namespace tgt {
class QtThreadedCanvas;
}
......@@ -125,10 +127,25 @@ namespace campvis {
*/
int run();
/**
* Gets the DataContainer of this CampvisApplication
* \return _dataContainer
*/
DataContainer* getDataContainer();
/**
* Gets the DataContainer of this CampvisApplication
* \return _dataContainer
*/
const DataContainer* getDataContainer() const;
/// Signal emitted when the collection of pipelines has changed.
sigslot::signal0<> s_PipelinesChanged;
private:
/// The (currently) one and only DataContainer in CampvisApplication
DataContainer _dataContainer;
/// All pipelines (incuding VisualizationPipelines)
std::vector<AbstractPipeline*> _pipelines;
/// All visualisations (i.e. VisualizationPipelines with their corresponding painters/canvases)
......
......@@ -34,8 +34,8 @@
#include "tgt/glcontext.h"
#include "tgt/tgt_gl.h"
#include "core/pipeline/abstractprocessor.h"
#include "core/pipeline/visualizationprocessor.h"
#include "core/pipeline/abstractprocessor.h"
#include "core/tools/job.h"
#include "core/tools/opengljobprocessor.h"
#include "core/tools/simplejobprocessor.h"
......@@ -67,15 +67,18 @@ namespace {
namespace campvis {
const std::string AbstractPipeline::loggerCat_ = "CAMPVis.core.datastructures.AbstractPipeline";
AbstractPipeline::AbstractPipeline()
AbstractPipeline::AbstractPipeline(DataContainer* dc)
: HasPropertyCollection()
, tgt::EventHandler()
, tgt::EventListener()
, _data(dc)
, _canvas(0)
, _canvasSize("CanvasSize", "Canvas Size", tgt::ivec2(128, 128), tgt::ivec2(1, 1), tgt::ivec2(4096, 4096))
, _ignoreCanvasSizeUpdate(false)
, _renderTargetID("renderTargetID", "Render Target ID", "AbstractPipeline.renderTarget", DataNameProperty::READ)
{
tgtAssert(_data != 0, "Pointer to the DataContainer for this pipeline must not be 0!");
_enabled = true;
addProperty(&_renderTargetID);
......@@ -87,7 +90,7 @@ namespace campvis {
void AbstractPipeline::init() {
_renderTargetID.s_changed.connect<AbstractPipeline>(this, &AbstractPipeline::onPropertyChanged);
_data.s_dataAdded.connect(this, &AbstractPipeline::onDataContainerDataAdded);
_data->s_dataAdded.connect(this, &AbstractPipeline::onDataContainerDataAdded);
initAllProperties();
......@@ -116,11 +119,11 @@ namespace campvis {
}
}
_data.s_dataAdded.disconnect(this);
_data->s_dataAdded.disconnect(this);
_renderTargetID.s_changed.disconnect(this);
// clear DataContainer
_data.clear();
_data->clear();
}
void AbstractPipeline::onPropertyChanged(const AbstractProperty* prop) {
......@@ -140,11 +143,11 @@ namespace campvis {
}
const DataContainer& AbstractPipeline::getDataContainer() const {
return _data;
return *_data;
}
DataContainer& AbstractPipeline::getDataContainer() {
return _data;
return *_data;
}
void AbstractPipeline::executeProcessor(AbstractProcessor* processor, bool unlockInExtraThred) {
......@@ -153,7 +156,7 @@ namespace campvis {
if (processor->getEnabled() && !processor->isLocked()) {
// update properties if they're invalid
if (processor->hasInvalidProperties()) {
processor->updateProperties(_data);
processor->updateProperties(*_data);
#if CAMPVIS_DEBUG
if (processor->hasInvalidProperties())
LDEBUG("Processor " << processor->getName() << " still has INVALID_PROPERTIES level. Did you forget to validate the processor in updateProperties()?");
......@@ -166,7 +169,7 @@ namespace campvis {
clock_t startTime = clock();
try {
processor->process(_data);
processor->process(*_data);
}
catch (std::exception& e) {
LERROR("Caught unhandled exception while executing processor " << processor->getName() << ": " << e.what());
......
......@@ -45,6 +45,7 @@
#include "core/properties/floatingpointproperty.h"
#include "core/properties/propertycollection.h"
#include <map>
#include <vector>
namespace tgt {
......@@ -63,8 +64,11 @@ namespace campvis {
* Creates a AbstractPipeline.
* If you derive from AbstractPipeline, you will have to implement the pipeline evaluation
* logic yourself. You might want to have a look at AutoEvaluationPipeline.
*
* \param dc Pointer to the DataContainer containing local working set of data for this
* pipeline, must not be 0, must be valid the whole lifetime of this pipeline.
*/
AbstractPipeline();
AbstractPipeline(DataContainer* dc);
/**
* Virtual Destructor
......@@ -213,7 +217,8 @@ namespace campvis {
virtual void onPropertyChanged(const AbstractProperty* prop);
DataContainer _data; ///< DataContainer containing local working set of data for this Pipeline
/// 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
......
......@@ -40,8 +40,8 @@
namespace campvis {
const std::string AutoEvaluationPipeline::loggerCat_ = "CAMPVis.core.datastructures.AutoEvaluationPipeline";
AutoEvaluationPipeline::AutoEvaluationPipeline()
: AbstractPipeline()
AutoEvaluationPipeline::AutoEvaluationPipeline(DataContainer* dc)
: AbstractPipeline(dc)
{
}
......
......@@ -41,13 +41,16 @@ namespace campvis {
* Specializtaion of AbstractPipeline performing automatic execution of invalidated processors.
* AutoEvaluationPipeline connects to the s_(in)validated signals of all of its processors and
* executes processors with invalid results using the correct threads.
*
* \param dc Pointer to the DataContainer containing local working set of data for this
* pipeline, must not be 0, must be valid the whole lifetime of this pipeline.
*/
class AutoEvaluationPipeline : public AbstractPipeline {
public:
/**
* Creates a AutoEvaluationPipeline.
*/
AutoEvaluationPipeline();
AutoEvaluationPipeline(DataContainer* dc);
/**
* Virtual Destructor
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universitt Mnchen
// Boltzmannstr. 3, 85748 Garching b. Mnchen, Germany
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// The licensing of this softare is not yet resolved. Until then, redistribution in source or
// binary forms outside the CAMP chair is not permitted, unless explicitly stated in legal form.
// However, the names of the original authors and the above copyright notice must retain in its
// original state in any case.
//
// Legal disclaimer provided by the BSD license:
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// ================================================================================================
#include "digraphvisualizationpipeline.h"
#include "tgt/tgt_gl.h"
#include "tgt/glcanvas.h"
#include "tgt/glcontext.h"
#include "core/pipeline/visualizationprocessor.h"
#include "core/tools/job.h"
#include "core/tools/opengljobprocessor.h"
#include "core/tools/simplejobprocessor.h"
#include <stack>
namespace campvis {
const std::string DigraphVisualizationPipeline::loggerCat_ = "CAMPVis.core.datastructures.DigraphVisualizationPipeline";
DigraphVisualizationPipeline::DependencyNode::DependencyNode(AbstractProcessor* processor)
: _processor(processor)
{
tgtAssert(_processor != 0, "Processor must not be 0.");
if (_processor != 0) {
_isVisualizationProcessor = (dynamic_cast<VisualizationProcessor*>(_processor) != 0);
}
}
void DigraphVisualizationPipeline::DependencyNode::addDependency(DependencyNode* dependency) {
tgtAssert(dependency != 0, "DependencyNode must not be 0.");
// check for acyclicness
std::stack<DependencyNode*> nodesToVisit;
for (std::set<DependencyNode*>::const_iterator it = dependency->_dependentNodes.begin(); it != dependency->_dependentNodes.end(); ++it) {
nodesToVisit.push(*it);
}
while (! nodesToVisit.empty()) {
DependencyNode* node = nodesToVisit.top();
nodesToVisit.pop();
if (node == this) {
LERROR("Circle in dependency graph detected. DependencyNode insertion aborted.");
return;
}
for (std::set<DependencyNode*>::const_iterator it = dependency->_dependentNodes.begin(); it != dependency->_dependentNodes.end(); ++it) {
nodesToVisit.push(*it);
}
}
// add dependency
_dependentNodes.insert(dependency);
}
// ================================================================================================
DigraphVisualizationPipeline::DigraphVisualizationPipeline()
: AutoEvaluationPipeline()
{
}
DigraphVisualizationPipeline::~DigraphVisualizationPipeline() {
// delete all DependencyNodes
for (std::map<AbstractProcessor*, DependencyNode*>::iterator it = _processorNodeMap.begin(); it != _processorNodeMap.end(); ++it) {
delete it->second;
}
}
void DigraphVisualizationPipeline::addProcessor(AbstractProcessor* processor) {
tgtAssert(processor != 0, "Processor must not be 0!");
// add processor to processor list and connect signals
AutoEvaluationPipeline::addProcessor(processor);
processor->s_invalidated.connect<DigraphVisualizationPipeline>(this, &DigraphVisualizationPipeline::onProcessorInvalidated);
// create DependencyNode
std::map<AbstractProcessor*, DependencyNode*>::iterator lb = _processorNodeMap.lower_bound(processor);
if (lb == _processorNodeMap.end() || lb->first != processor) {
DependencyNode* node = new DependencyNode(processor);
_processorNodeMap.insert(lb, std::make_pair(processor, node));
}
else {
LDEBUG("Processor already added!");
}
}
void DigraphVisualizationPipeline::addProcessorDependency(AbstractProcessor* fatherProc, AbstractProcessor* childProc) {
tgtAssert(fatherProc != 0, "Father Processor must not be 0!");
tgtAssert(childProc != 0, "Child Processor must not be 0!");
// find corresponding DependencyNodes
std::map<AbstractProcessor*, DependencyNode*>::iterator fatherNode = _processorNodeMap.find(fatherProc);
std::map<AbstractProcessor*, DependencyNode*>::iterator childNode = _processorNodeMap.find(childProc);
if (fatherNode == _processorNodeMap.end() || childNode == _processorNodeMap.end()) {
tgtAssert(false, "Add processors first before defining dependencies between them!");
LERROR("Could not find DependencyNodes for at least one of the given processors. No dependency added!");
return;
}
// add dependency
fatherNode->second->addDependency(childNode->second);
}
void DigraphVisualizationPipeline::onProcessorInvalidated(AbstractProcessor* processor) {
// dirty hack - implement proper initialization...
if (_canvas == 0)
return;
// TODO: think about a more elaborate implementation, this one doesn't care about the processor graph
std::map<AbstractProcessor*, DependencyNode*>::iterator node = _processorNodeMap.find(processor);
if (node != _processorNodeMap.end()) {
if (node->second->_isVisualizationProcessor) {
GLJobProc.enqueueJob(
_canvas,
makeJobOnHeap<DigraphVisualizationPipeline, AbstractProcessor*, bool>(this, &DigraphVisualizationPipeline::executeProcessor, processor, true),
OpenGLJobProcessor::SerialJob);
}
else {
SimpleJobProc.enqueueJob(makeJob<DigraphVisualizationPipeline, AbstractProcessor*, bool>(this, &DigraphVisualizationPipeline::executeProcessor, processor, false));
}
}
else {
LWARNING("Caught invalidation of a processor that is not in the processor graph!");
}
}
}
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, Germany
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// The licensing of this softare is not yet resolved. Until then, redistribution in source or
// binary forms outside the CAMP chair is not permitted, unless explicitly stated in legal form.
// However, the names of the original authors and the above copyright notice must retain in its
// original state in any case.
//
// Legal disclaimer provided by the BSD license:
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// ================================================================================================
#include "pipelinefactory.h"
#include <sstream>
namespace campvis {
// declare one single symbol for the PipelineFactory singleton
tbb::atomic<PipelineFactory*> PipelineFactory::_singleton;
std::string PipelineFactory::toString() {
std::stringstream ss;
ss << _pipelineMap.size() << " Pipelines registered: ";
for (std::map< std::string, AbstractPipeline* (*)(DataContainer*) >::iterator it = _pipelineMap.begin(); it != _pipelineMap.end(); ++it) {
ss << it->first << ", ";
}
return ss.str();
}
}
\ No newline at end of file
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, Germany
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// The licensing of this softare is not yet resolved. Until then, redistribution in source or
// binary forms outside the CAMP chair is not permitted, unless explicitly stated in legal form.
// However, the names of the original authors and the above copyright notice must retain in its
// original state in any case.
//
// Legal disclaimer provided by the BSD license:
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// ================================================================================================
#ifndef DIGRAPHVISUALIZATIONPIPELINE_H__
#define DIGRAPHVISUALIZATIONPIPELINE_H__
#include "sigslot/sigslot.h"
#include "tgt/vector.h"
#include "tgt/event/eventlistener.h"
#include "core/pipeline/autoevaluationpipeline.h"
#include "core/properties/genericproperty.h"
#include <map>
#include <set>
namespace campvis {
class VisualizationProcessor;
/**
* Specialization of the AutoEvaluationPipeline that performs automatic evaluation based on
* an acyclic directed dependency graph.
*
* \todo Implement thread-safety. The current graph implementation is \b not thread-safe.
*/
class DigraphVisualizationPipeline : public AutoEvaluationPipeline {
public:
/**
* Creates a DigraphVisualizationPipeline.
*/
DigraphVisualizationPipeline();
/**
* Virtual Destructor
**/
virtual ~DigraphVisualizationPipeline();
/**
* Execute this pipeline.
* Pipeline must have a valid canvas set before calling this method.
**/
virtual void execute() = 0;
/**
* Adds a processor to this pipeline, so that it will be managed.
*
* \note Add each processor only once!
* \param processor Processor to add
*/
void addProcessor(AbstractProcessor* processor);
/**
* Adds a dependency link between two processors.
* The processor \a childProc needs to be executed every time when \a fatherProc has been evaluated.
* \note Add each dependency between two processors only once!
* \param fatherProc The processor \a childProc is dependent on.
* \param childProc The dependent processor of \a fatherProc.
*/
void addProcessorDependency(AbstractProcessor* fatherProc, AbstractProcessor* childProc);
protected:
/**
* Struct for the nodes defining the processor evaluation dependency graph.
* Each Node represents one processor and stores the processors which are dependent on this
* processors, hence, the ones which need to be updated afterwards.
*
* \todo This struct is \b not thread-safe!
*/
struct DependencyNode {
/**
* Creates a new DependencyNode for the given Processor
* \param processor The processor this node represents. Must not be 0.
*/
DependencyNode(AbstractProcessor* processor);
/**
* Adds the given DependencyNode as dependency (child) of this node.
* \param dependency DependencyNode containing a processor which needs to be updated after this one.
*/
void addDependency(DependencyNode* dependency);
AbstractProcessor* _processor; ///< Processor this node wraps around
bool _isVisualizationProcessor; ///< Flag whether \a _processor is a VisualizationProcessor (hence, needs an OpenGL context)
std::set<DependencyNode*> _dependentNodes; ///< Set of all dependent nodes.
};
/**
* Slot getting called when one of the observed processors got invalidated.
* The default behaviour is just to set the invalidation level to invalid.
* \param processor The processor that emitted the signal
*/
virtual void onProcessorInvalidated(AbstractProcessor* processor);
static const std::string loggerCat_;
std::map<AbstractProcessor*, DependencyNode*> _processorNodeMap;
tbb::atomic<DependencyNode*> _topNodeToEvaluate;
};
}
#endif // DIGRAPHVISUALIZATIONPIPELINE_H__
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, Germany
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// The licensing of this softare is not yet resolved. Until then, redistribution in source or
// binary forms outside the CAMP chair is not permitted, unless explicitly stated in legal form.
// However, the names of the original authors and the above copyright notice must retain in its
// original state in any case.
//
// Legal disclaimer provided by the BSD license:
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// ================================================================================================
#ifndef PIPELINEFACTORY_H__
#define PIPELINEFACTORY_H__
#include "tgt/logmanager.h"
#include "tgt/singleton.h"
#include <tbb/atomic.h>