Commit 57e41972 authored by schultezub's avatar schultezub
Browse files

Started implementing Job-Pattern for OpenGL operations

 * introducing OpenGLJobProcessor (the name somehow sucks...)
 * renamed JobPool to PriorityPool (still needs some work)
 * AbstractPipeline::executeProcessor() now takes a pointer as argument (necessary for job pattern)

git-svn-id: https://camplinux.in.tum.de/svn/campvis/trunk@251 bb408c1c-ae56-11e1-83d9-df6b3e0c105e
parent 0eb22d19
...@@ -41,8 +41,8 @@ using namespace TUMVis; ...@@ -41,8 +41,8 @@ using namespace TUMVis;
**/ **/
int main(int argc, char** argv) { int main(int argc, char** argv) {
TumVisApplication app(argc, argv); TumVisApplication app(argc, argv);
//app.addVisualizationPipeline("SliceVis", new SliceVis()); app.addVisualizationPipeline("SliceVis", new SliceVis());
app.addVisualizationPipeline("DVRVis", new DVRVis()); //app.addVisualizationPipeline("DVRVis", new DVRVis());
app.init(); app.init();
int toReturn = app.run(); int toReturn = app.run();
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "application/tumvispainter.h" #include "application/tumvispainter.h"
#include "application/gui/mainwindow.h" #include "application/gui/mainwindow.h"
#include "core/tools/opengljobprocessor.h"
#include "core/pipeline/abstractpipeline.h" #include "core/pipeline/abstractpipeline.h"
#include "core/pipeline/visualizationpipeline.h" #include "core/pipeline/visualizationpipeline.h"
#include "core/pipeline/pipelineevaluator.h" #include "core/pipeline/pipelineevaluator.h"
...@@ -64,6 +65,8 @@ namespace TUMVis { ...@@ -64,6 +65,8 @@ namespace TUMVis {
_mainWindow = new MainWindow(this); _mainWindow = new MainWindow(this);
tgt::QtContextManager::init(); tgt::QtContextManager::init();
OpenGLJobProcessor::init();
} }
TumVisApplication::~TumVisApplication() { TumVisApplication::~TumVisApplication() {
...@@ -134,12 +137,15 @@ namespace TUMVis { ...@@ -134,12 +137,15 @@ namespace TUMVis {
it->second->init(); it->second->init();
} }
GLJobProc.start();
_initialized = true; _initialized = true;
} }
void TumVisApplication::deinit() { void TumVisApplication::deinit() {
tgtAssert(_initialized, "Tried to deinitialize uninitialized TumVisApplication."); tgtAssert(_initialized, "Tried to deinitialize uninitialized TumVisApplication.");
GLJobProc.stop();
{ {
// Deinit everything OpenGL related using the local context. // Deinit everything OpenGL related using the local context.
tgt::GLContextScopedLock lock(_localContext->getContext()); tgt::GLContextScopedLock lock(_localContext->getContext());
...@@ -162,6 +168,8 @@ namespace TUMVis { ...@@ -162,6 +168,8 @@ namespace TUMVis {
} }
} }
OpenGLJobProcessor::deinit();
tgt::QtContextManager::deinit(); tgt::QtContextManager::deinit();
tgt::deinit(); tgt::deinit();
......
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
#include "core/datastructures/imagedatarendertarget.h" #include "core/datastructures/imagedatarendertarget.h"
#include "core/pipeline/visualizationpipeline.h" #include "core/pipeline/visualizationpipeline.h"
#include "core/tools/job.h"
#include "core/tools/opengljobprocessor.h"
namespace TUMVis { namespace TUMVis {
const std::string TumVisPainter::loggerCat_ = "TUMVis.core.TumVisPainter"; const std::string TumVisPainter::loggerCat_ = "TUMVis.core.TumVisPainter";
...@@ -69,11 +71,14 @@ namespace TUMVis { ...@@ -69,11 +71,14 @@ namespace TUMVis {
std::unique_lock<tbb::mutex> lock(CtxtMgr.getGlMutex()); std::unique_lock<tbb::mutex> lock(CtxtMgr.getGlMutex());
while (! _stopExecution) { while (! _stopExecution) {
getCanvas()->getContext()->acquire(); if (_dirty)
GLJobProc.enqueueJob(getCanvas(), new CallMemberFuncJob<TumVisPainter>(this, &TumVisPainter::paint), Normal);
/*getCanvas()->getContext()->acquire();
paint(); paint();
getCanvas()->swap(); getCanvas()->swap();*/
while (!_stopExecution && !_dirty) //while (!_stopExecution)
_renderCondition.wait(lock); _renderCondition.wait(lock);
} }
...@@ -140,6 +145,8 @@ namespace TUMVis { ...@@ -140,6 +145,8 @@ namespace TUMVis {
} }
LGL_ERROR; LGL_ERROR;
} }
getCanvas()->swap();
} }
void TumVisPainter::sizeChanged(const tgt::ivec2& size) { void TumVisPainter::sizeChanged(const tgt::ivec2& size) {
......
...@@ -84,10 +84,12 @@ namespace TUMVis { ...@@ -84,10 +84,12 @@ namespace TUMVis {
return _data; return _data;
} }
void AbstractPipeline::executeProcessor(AbstractProcessor& processor) { void AbstractPipeline::executeProcessor(AbstractProcessor* processor) {
processor.lockProperties(); tgtAssert(processor != 0, "Processor must not be 0.");
processor.process(_data);
processor.unlockProperties(); processor->lockProperties();
processor->process(_data);
processor->unlockProperties();
} }
InvalidationLevel& AbstractPipeline::getInvalidationLevel() { InvalidationLevel& AbstractPipeline::getInvalidationLevel() {
......
...@@ -128,7 +128,7 @@ namespace TUMVis { ...@@ -128,7 +128,7 @@ namespace TUMVis {
* Executes the processor \a processor on the pipeline's data and locks its properties meanwhile. * Executes the processor \a processor on the pipeline's data and locks its properties meanwhile.
* \param processor Processor to execute. * \param processor Processor to execute.
*/ */
void executeProcessor(AbstractProcessor& processor); void executeProcessor(AbstractProcessor* processor);
DataContainer _data; ///< DataContainer containing local working set of data for this Pipeline DataContainer _data; ///< DataContainer containing local working set of data for this Pipeline
std::vector<AbstractProcessor*> _processors; ///< List of all processors of this pipeline std::vector<AbstractProcessor*> _processors; ///< List of all processors of this pipeline
......
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include "tgt/glcanvas.h" #include "tgt/glcanvas.h"
#include "tgt/glcontext.h" #include "tgt/glcontext.h"
#include "core/datastructures/imagedatarendertarget.h" #include "core/datastructures/imagedatarendertarget.h"
#include "core/tools/job.h"
#include "core/tools/opengljobprocessor.h"
namespace TUMVis { namespace TUMVis {
const std::string VisualizationPipeline::loggerCat_ = "TUMVis.core.datastructures.VisualizationPipeline"; const std::string VisualizationPipeline::loggerCat_ = "TUMVis.core.datastructures.VisualizationPipeline";
...@@ -94,12 +96,16 @@ namespace TUMVis { ...@@ -94,12 +96,16 @@ namespace TUMVis {
_renderTargetSize.setValue(size); _renderTargetSize.setValue(size);
} }
void VisualizationPipeline::lockGLContextAndExecuteProcessor(AbstractProcessor& processor) { void VisualizationPipeline::lockGLContextAndExecuteProcessor(AbstractProcessor* processor) {
tgtAssert(_canvas != 0, "Set a valid canvas before calling this method!"); tgtAssert(_canvas != 0, "Set a valid canvas before calling this method!");
tgt::GLContextScopedLock lock(_canvas->getContext()); GLJobProc.enqueueJob(
_canvas,
new CallMemberFunc1ArgJob<VisualizationPipeline, AbstractProcessor*>(this, &VisualizationPipeline::executeProcessor, processor),
Normal);
/*tgt::GLContextScopedLock lock(_canvas->getContext());
executeProcessor(processor); executeProcessor(processor);
glFinish(); // TODO: is glFlush enough or do we need a glFinish here? glFinish(); // TODO: is glFlush enough or do we need a glFinish here?
LGL_ERROR; LGL_ERROR;*/
} }
void VisualizationPipeline::setCanvas(tgt::GLCanvas* canvas) { void VisualizationPipeline::setCanvas(tgt::GLCanvas* canvas) {
......
...@@ -143,7 +143,7 @@ namespace TUMVis { ...@@ -143,7 +143,7 @@ namespace TUMVis {
* and locks its properties meanwhile. * and locks its properties meanwhile.
* \param processor Processor to execute. * \param processor Processor to execute.
*/ */
void lockGLContextAndExecuteProcessor(AbstractProcessor& processor); void lockGLContextAndExecuteProcessor(AbstractProcessor* processor);
GenericProperty<tgt::ivec2> _renderTargetSize; ///< Viewport size of target canvas GenericProperty<tgt::ivec2> _renderTargetSize; ///< Viewport size of target canvas
StringProperty _renderTargetID; ///< ID of the render target image to be rendered to the canvas StringProperty _renderTargetID; ///< ID of the render target image to be rendered to the canvas
......
...@@ -56,7 +56,7 @@ namespace TUMVis { ...@@ -56,7 +56,7 @@ namespace TUMVis {
* Specific job, that is calling a member function pasing no argument. * Specific job, that is calling a member function pasing no argument.
*/ */
template<class T> template<class T>
class CallMemberFuncJob { class CallMemberFuncJob : public AbstractJob {
public: public:
/** /**
* Creates an new job, that is calling \a callee on \a target pasing no argument. * Creates an new job, that is calling \a callee on \a target pasing no argument.
...@@ -92,7 +92,7 @@ namespace TUMVis { ...@@ -92,7 +92,7 @@ namespace TUMVis {
* Specific job, that is calling a member function pasing a single argument. * Specific job, that is calling a member function pasing a single argument.
*/ */
template<class T, class A1> template<class T, class A1>
class CallMemberFunc1ArgJob { class CallMemberFunc1ArgJob : public AbstractJob {
public: public:
/** /**
* Creates an new job, that is calling \a callee on \a target pasing \a arg1 as single argument. * Creates an new job, that is calling \a callee on \a target pasing \a arg1 as single argument.
...@@ -100,7 +100,7 @@ namespace TUMVis { ...@@ -100,7 +100,7 @@ namespace TUMVis {
* \param callee Member function to call * \param callee Member function to call
* \param arg1 Argument to pass to \a callee * \param arg1 Argument to pass to \a callee
*/ */
CallMemberFunc1ArgJob(T* target, void (T::*callee)(A1), const A1& arg1) CallMemberFunc1ArgJob(T* target, void (T::*callee)(A1), A1 arg1)
: _target(target) : _target(target)
, _callee(callee) , _callee(callee)
, _arg1(arg1) , _arg1(arg1)
...@@ -124,7 +124,7 @@ namespace TUMVis { ...@@ -124,7 +124,7 @@ namespace TUMVis {
protected: protected:
T* _target; ///< Target object T* _target; ///< Target object
void (T::*_callee)(A1); /// <Member function to call void (T::*_callee)(A1); /// <Member function to call
const A1& _arg1; ///< Argument to pass to \a callee A1 _arg1; ///< Argument to pass to \a callee
}; };
} }
......
...@@ -28,50 +28,7 @@ ...@@ -28,50 +28,7 @@
#include "jobpool.h" #include "jobpool.h"
#include "tgt/assert.h"
#include "core/tools/job.h"
namespace TUMVis { namespace TUMVis {
const size_t JobPool::NUM_PRIORITIES = 3;
JobPool::JobPool()
: _queues(0)
{
_queues = new tbb::concurrent_queue<AbstractJob*>[NUM_PRIORITIES];
}
JobPool::~JobPool() {
// delete jobs
AbstractJob* toDelete = 0;
for (size_t i = 0; i < NUM_PRIORITIES; ++i) {
while (_queues[i].try_pop(toDelete))
delete toDelete;
}
// delete queues
delete[] _queues;
}
void JobPool::enqueueJob(AbstractJob* job, JobPriority priority) {
size_t i = static_cast<size_t>(priority);
tgtAssert(i < NUM_PRIORITIES, "Job priority index must be lower than the total number or priorities.");
tgtAssert(job != 0, "Job must not be 0");
_queues[i].push(job);
s_enqueuedJob();
}
AbstractJob* JobPool::dequeueJob() {
// very simple scheduling algorithm. This should be made fairer and avoid starving!
AbstractJob* toReturn = 0;
for (size_t i = 0; i < NUM_PRIORITIES; ++i) {
if (_queues[i].try_pop(toReturn));
return toReturn;
}
return 0;
}
} }
...@@ -37,57 +37,106 @@ namespace TUMVis { ...@@ -37,57 +37,106 @@ namespace TUMVis {
class AbstractJob; class AbstractJob;
/** /**
* A JobPool manages multible Jobs in queues with different priorities. * Enumeration of the different priorities of items.
*/
enum PriorityPoolPriority {
Realtime = 0, ///< Realtime items are always considered first during dequeueing.
Normal = 1, ///< Items with normal priorities are dequeued as soon as there are no realtime items left
Low = 2 ///< Low priority items are only considered if there are no items in the queue with higher priority
};
/**
* A PriorityPool manages multiple items of type T in queues with different priorities.
* Similar to a priority queue but different...
* *
* \note This class is to be considered as thread-safe. * \note This class is to be considered as thread-safe.
* \todo Implement a suitable scheduling strategy to avoid starving of low priority jobs. * \todo Implement a suitable scheduling strategy to avoid starving of low priority items.
* This sounds like a good opportunity to take a look at the Betriebssysteme lecture slides. :) * This sounds like a good opportunity to take a look at the Betriebssysteme lecture slides. :)
*/ */
class JobPool { template<class T>
class PriorityPool {
public: public:
/**
* Enumeration of the different priorities of jobs.
*/
enum JobPriority {
Realtime = 0, ///< Realtime jobs are always considered first during dequeueing.
Normal = 1, ///< Jobs with normal priorities are dequeued as soon as there are no realtime jobs left
Low = 2 ///< Low priority jobs are only considered if there are no jobs in the queue with higher priority
};
/** /**
* Creates a new JobPool * Creates a new PriorityPool
*/ */
JobPool(); PriorityPool();
/** /**
* Destructor, deletes all jobs which are still enqueued. * Destructor, deletes all items which are still enqueued.
*/ */
~JobPool(); ~PriorityPool();
/** /**
* Enqueues the given Job with the given priority. * Enqueues the given Job with the given priority.
* *
* \note JobPool takes ownership of \a job. * \note PriorityPool takes ownership of \a item.
* \param job Job to enqueue, JobPool takes ownership of this Job! * \param item Item to enqueue, PriorityPool takes ownership of this Job!
* \param priority Priority of the job to enqueue * \param priority Priority of the item to enqueue
*/ */
void enqueueJob(AbstractJob* job, JobPriority priority); void enqueueJob(T* item, PriorityPoolPriority priority);
/** /**
* Dequeues the next job according to the scheduling strategy. * Dequeues the next item according to the scheduling strategy.
* \note The calling function takes the ownership of the returned job! * \note The calling function takes the ownership of the returned item!
* \todo Develop a good scheduling strategy and describe it here. * \todo Develop a good scheduling strategy and describe it here.
* \return The next job to execute, 0 if there is currently no job to execute. The caller takes ownership of the job! * \return The next item to execute, 0 if there is currently no item to execute. The caller takes ownership of the item!
*/ */
AbstractJob* dequeueJob(); T* dequeueJob();
/// Signal being emitted, when a job has been enqueued. /// Signal being emitted, when a item has been enqueued.
sigslot::signal0<> s_enqueuedJob; sigslot::signal0<> s_enqueuedJob;
protected: protected:
static const size_t NUM_PRIORITIES; ///< total number of piorities, make sure that this matches the JobPriority enum. static const size_t NUM_PRIORITIES; ///< total number of piorities, make sure that this matches the Priority enum.
tbb::concurrent_queue<AbstractJob*>* _queues; ///< Array of job queues, one for each JobPriority tbb::concurrent_queue<T*>* _queues; ///< Array of item queues, one for each Priority
}; };
// ================================================================================================
template<class T>
const size_t TUMVis::PriorityPool<T>::NUM_PRIORITIES = 3;
template<class T>
TUMVis::PriorityPool<T>::PriorityPool()
{
_queues = new tbb::concurrent_queue<T*>[NUM_PRIORITIES];
}
template<class T>
TUMVis::PriorityPool<T>::~PriorityPool() {
// delete jobs
T* toDelete = 0;
for (size_t i = 0; i < NUM_PRIORITIES; ++i) {
while (_queues[i].try_pop(toDelete))
delete toDelete;
}
// delete queues
delete[] _queues;
}
template<class T>
void TUMVis::PriorityPool<T>::enqueueJob(T* item, PriorityPoolPriority priority) {
size_t i = static_cast<size_t>(priority);
tgtAssert(i < NUM_PRIORITIES, "Item priority index must be lower than the total number or priorities.");
tgtAssert(item != 0, "Item must not be 0");
_queues[i].push(item);
s_enqueuedJob();
}
template<class T>
T* TUMVis::PriorityPool<T>::dequeueJob() {
// very simple scheduling algorithm. This should be made fairer and avoid starving!
T* toReturn = 0;
for (size_t i = 0; i < NUM_PRIORITIES; ++i) {
if (_queues[i].try_pop(toReturn))
return toReturn;
}
return 0;
}
} }
#endif // JOBPOOL_H__ #endif // JOBPOOL_H__
\ No newline at end of file
// ================================================================================================
//
// This file is part of the TUMVis Visualization 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
//
// 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 "opengljobprocessor.h"
#include "tgt/assert.h"
#include "tgt/qt/qtcontextmanager.h"
#include "core/tools/job.h"
namespace TUMVis {
OpenGLJobProcessor::OpenGLJobProcessor()
: _currentContext(0)
{
_jobPool.s_enqueuedJob.connect(this, &OpenGLJobProcessor::OnEnqueuedJob);
}
OpenGLJobProcessor::~OpenGLJobProcessor() {
_jobPool.s_enqueuedJob.disconnect(this);
}
void OpenGLJobProcessor::stop() {
_stopExecution = true;
_evaluationCondition.notify_all();
Runnable::stop();
}
void OpenGLJobProcessor::run() {
std::unique_lock<tbb::mutex> lock(CtxtMgr.getGlMutex());
while (! _stopExecution) {
OpenGLJob* job = 0;
while (job = _jobPool.dequeueJob()) {
if (_currentContext != job->_canvas) {
if (_currentContext != 0) {
glFinish();
LGL_ERROR;
}
job->_canvas->getContext()->acquire();
_currentContext = job->_canvas;
}
job->_job->execute();
delete job->_job;
delete job;
}
// while (! _stopExecution && _jobPool.empty())
_evaluationCondition.wait(lock);
}
// release OpenGL context, so that other threads can access it
CtxtMgr.releaseCurrentContext();
}
void OpenGLJobProcessor::enqueueJob(tgt::GLCanvas* canvas, AbstractJob* job, PriorityPoolPriority priority) {
_jobPool.enqueueJob(new OpenGLJob(job, canvas), priority);
}
void OpenGLJobProcessor::OnEnqueuedJob() {
_evaluationCondition.notify_all();
}
}
// ================================================================================================
//
// This file is part of the TUMVis Visualization 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
//
// 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 OPENGLJOBPROCESSOR_H__