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"