Commit 57e41972 authored by schultezub's avatar schultezub

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;
**/
int main(int argc, char** argv) {
TumVisApplication app(argc, argv);
//app.addVisualizationPipeline("SliceVis", new SliceVis());
app.addVisualizationPipeline("DVRVis", new DVRVis());
app.addVisualizationPipeline("SliceVis", new SliceVis());
//app.addVisualizationPipeline("DVRVis", new DVRVis());
app.init();
int toReturn = app.run();
......
......@@ -42,6 +42,7 @@
#include "application/tumvispainter.h"
#include "application/gui/mainwindow.h"
#include "core/tools/opengljobprocessor.h"
#include "core/pipeline/abstractpipeline.h"
#include "core/pipeline/visualizationpipeline.h"
#include "core/pipeline/pipelineevaluator.h"
......@@ -64,6 +65,8 @@ namespace TUMVis {
_mainWindow = new MainWindow(this);
tgt::QtContextManager::init();
OpenGLJobProcessor::init();
}
TumVisApplication::~TumVisApplication() {
......@@ -134,12 +137,15 @@ namespace TUMVis {
it->second->init();
}
GLJobProc.start();
_initialized = true;
}
void TumVisApplication::deinit() {
tgtAssert(_initialized, "Tried to deinitialize uninitialized TumVisApplication.");
GLJobProc.stop();
{
// Deinit everything OpenGL related using the local context.
tgt::GLContextScopedLock lock(_localContext->getContext());
......@@ -162,6 +168,8 @@ namespace TUMVis {
}
}
OpenGLJobProcessor::deinit();
tgt::QtContextManager::deinit();
tgt::deinit();
......
......@@ -38,6 +38,8 @@
#include "core/datastructures/imagedatarendertarget.h"
#include "core/pipeline/visualizationpipeline.h"
#include "core/tools/job.h"
#include "core/tools/opengljobprocessor.h"
namespace TUMVis {
const std::string TumVisPainter::loggerCat_ = "TUMVis.core.TumVisPainter";
......@@ -69,11 +71,14 @@ namespace TUMVis {
std::unique_lock<tbb::mutex> lock(CtxtMgr.getGlMutex());
while (! _stopExecution) {
getCanvas()->getContext()->acquire();
if (_dirty)
GLJobProc.enqueueJob(getCanvas(), new CallMemberFuncJob<TumVisPainter>(this, &TumVisPainter::paint), Normal);
/*getCanvas()->getContext()->acquire();
paint();
getCanvas()->swap();
getCanvas()->swap();*/
while (!_stopExecution && !_dirty)
//while (!_stopExecution)
_renderCondition.wait(lock);
}
......@@ -140,6 +145,8 @@ namespace TUMVis {
}
LGL_ERROR;
}
getCanvas()->swap();
}
void TumVisPainter::sizeChanged(const tgt::ivec2& size) {
......
......@@ -84,10 +84,12 @@ namespace TUMVis {
return _data;
}
void AbstractPipeline::executeProcessor(AbstractProcessor& processor) {
processor.lockProperties();
processor.process(_data);
processor.unlockProperties();
void AbstractPipeline::executeProcessor(AbstractProcessor* processor) {
tgtAssert(processor != 0, "Processor must not be 0.");
processor->lockProperties();
processor->process(_data);
processor->unlockProperties();
}
InvalidationLevel& AbstractPipeline::getInvalidationLevel() {
......
......@@ -128,7 +128,7 @@ namespace TUMVis {
* Executes the processor \a processor on the pipeline's data and locks its properties meanwhile.
* \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
std::vector<AbstractProcessor*> _processors; ///< List of all processors of this pipeline
......
......@@ -31,6 +31,8 @@
#include "tgt/glcanvas.h"
#include "tgt/glcontext.h"
#include "core/datastructures/imagedatarendertarget.h"
#include "core/tools/job.h"
#include "core/tools/opengljobprocessor.h"
namespace TUMVis {
const std::string VisualizationPipeline::loggerCat_ = "TUMVis.core.datastructures.VisualizationPipeline";
......@@ -94,12 +96,16 @@ namespace TUMVis {
_renderTargetSize.setValue(size);
}
void VisualizationPipeline::lockGLContextAndExecuteProcessor(AbstractProcessor& processor) {
void VisualizationPipeline::lockGLContextAndExecuteProcessor(AbstractProcessor* processor) {
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);
glFinish(); // TODO: is glFlush enough or do we need a glFinish here?
LGL_ERROR;
LGL_ERROR;*/
}
void VisualizationPipeline::setCanvas(tgt::GLCanvas* canvas) {
......
......@@ -143,7 +143,7 @@ namespace TUMVis {
* and locks its properties meanwhile.
* \param processor Processor to execute.
*/
void lockGLContextAndExecuteProcessor(AbstractProcessor& processor);
void lockGLContextAndExecuteProcessor(AbstractProcessor* processor);
GenericProperty<tgt::ivec2> _renderTargetSize; ///< Viewport size of target canvas
StringProperty _renderTargetID; ///< ID of the render target image to be rendered to the canvas
......
......@@ -56,7 +56,7 @@ namespace TUMVis {
* Specific job, that is calling a member function pasing no argument.
*/
template<class T>
class CallMemberFuncJob {
class CallMemberFuncJob : public AbstractJob {
public:
/**
* Creates an new job, that is calling \a callee on \a target pasing no argument.
......@@ -92,7 +92,7 @@ namespace TUMVis {
* Specific job, that is calling a member function pasing a single argument.
*/
template<class T, class A1>
class CallMemberFunc1ArgJob {
class CallMemberFunc1ArgJob : public AbstractJob {
public:
/**
* Creates an new job, that is calling \a callee on \a target pasing \a arg1 as single argument.
......@@ -100,7 +100,7 @@ namespace TUMVis {
* \param callee Member function to call
* \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)
, _callee(callee)
, _arg1(arg1)
......@@ -124,7 +124,7 @@ namespace TUMVis {
protected:
T* _target; ///< Target object
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 @@
#include "jobpool.h"
#include "tgt/assert.h"
#include "core/tools/job.h"
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 {
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.
* \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. :)
*/
class JobPool {
template<class T>
class PriorityPool {
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.
*
* \note JobPool takes ownership of \a job.
* \param job Job to enqueue, JobPool takes ownership of this Job!
* \param priority Priority of the job to enqueue
* \note PriorityPool takes ownership of \a item.
* \param item Item to enqueue, PriorityPool takes ownership of this Job!
* \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.
* \note The calling function takes the ownership of the returned job!
* Dequeues the next item according to the scheduling strategy.
* \note The calling function takes the ownership of the returned item!
* \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;
protected:
static const size_t NUM_PRIORITIES; ///< total number of piorities, make sure that this matches the JobPriority enum.
tbb::concurrent_queue<AbstractJob*>* _queues; ///< Array of job queues, one for each JobPriority
static const size_t NUM_PRIORITIES; ///< total number of piorities, make sure that this matches the Priority enum.
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__
\ 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__
#define OPENGLJOBPROCESSOR_H__
#include "sigslot/sigslot.h"
#include "tgt/singleton.h"
#include "tbb/include/tbb/atomic.h"
#include "tbb/include/tbb/compat/condition_variable"
#include "core/tools/jobpool.h"
#include "core/tools/runnable.h"
namespace tgt {
class GLCanvas;
}
namespace TUMVis {
/**
*
* \todo Check if the inheritance of PriorityPool is a cool design or not...
*/
class OpenGLJobProcessor : public tgt::Singleton<OpenGLJobProcessor>, public Runnable, public sigslot::has_slots<> {
friend class tgt::Singleton<OpenGLJobProcessor>;
public:
struct OpenGLJob {
OpenGLJob(AbstractJob* job, tgt::GLCanvas* canvas)
: _job(job)
, _canvas(canvas)
{}
AbstractJob* _job; ///< Job to execute
tgt::GLCanvas* _canvas; ///< OpenGL context to execute job in
};
virtual ~OpenGLJobProcessor();
/// \see Runnable::stop
void stop();
/**
* Performs the pipeline evaluation using conditional wait.
* \sa Runnable::run
*/
void run();
/**
* Enqueues the given Job with the given priority.
*
* \note OpenGLJobProcessor takes ownership of \a job.
* \param job Job to enqueue, PriorityPool takes ownership of this Job!
* \param priority Priority of the job to enqueue
*/
void enqueueJob(tgt::GLCanvas* canvas, AbstractJob* job, PriorityPoolPriority priority);
/**
* Slot to be called by _jobPool::s_enqueuedJob signal.
*/
void OnEnqueuedJob();
protected:
OpenGLJobProcessor();
PriorityPool<OpenGLJob> _jobPool; ///< PriorityPool to process
std::condition_variable _evaluationCondition; ///< conditional wait to be used when there are currently no jobs to process
tgt::GLCanvas* _currentContext; ///< current active OpenGL context
};
}
#define GLJobProc tgt::Singleton<OpenGLJobProcessor>::getRef()
#endif // OPENGLJOBPROCESSOR_H__
......@@ -53,7 +53,6 @@ namespace TUMVis {
}
void ReferenceCounted::removeReference() {
// TODO: I'm afraid this is not 100% thread-safe - refCount might change between atomic decrement, check and deletion...
if (--_refCount == 0)
delete this;
}
......
......@@ -135,7 +135,7 @@ namespace kisscl {
std::pair<Context*, Device*> p = std::make_pair(context, device);
auto lb = _commandQueues.lower_bound(p);
std::map< std::pair<Context*, Device*>, CommandQueue*>::iterator lb = _commandQueues.lower_bound(p);
if (lb == _commandQueues.end() || lb ->first != p) {
CommandQueue* queue = new CommandQueue(context, device, _commandQueueProperties);
_commandQueues.insert(lb, std::make_pair(p, queue));
......
......@@ -113,7 +113,7 @@ namespace TUMVis {
// TODO: think whether we want to lock all processors already here.
}
if (! _imageReader.getInvalidationLevel().isValid()) {
executeProcessor(_imageReader);
executeProcessor(&_imageReader);
// convert data
DataContainer::ScopedTypedData<ImageData> img(_data, "reader.output");
......@@ -135,16 +135,16 @@ namespace TUMVis {
}
}
if (! _eepGenerator.getInvalidationLevel().isValid()) {
lockGLContextAndExecuteProcessor(_eepGenerator);
lockGLContextAndExecuteProcessor(&_eepGenerator);
}
if (! _eepGenerator.getInvalidationLevel().isValid() || !_drrraycater.getInvalidationLevel().isValid()) {
lockGLContextAndExecuteProcessor(_drrraycater);
lockGLContextAndExecuteProcessor(&_drrraycater);
}
if (! _eepGenerator.getInvalidationLevel().isValid() || !_simpleRaycaster.getInvalidationLevel().isValid()) {
lockGLContextAndExecuteProcessor(_simpleRaycaster);
lockGLContextAndExecuteProcessor(&_simpleRaycaster);
}
if (! _eepGenerator.getInvalidationLevel().isValid() || !_clRaycaster.getInvalidationLevel().isValid()) {
lockGLContextAndExecuteProcessor(_clRaycaster);
lockGLContextAndExecuteProcessor(&_clRaycaster);
}
}
......
......@@ -72,7 +72,7 @@ namespace TUMVis {
// TODO: think whether we want to lock all processors already here.
}
if (! _imageReader.getInvalidationLevel().isValid()) {
executeProcessor(_imageReader);
executeProcessor(&_imageReader);
// convert data
DataContainer::ScopedTypedData<ImageData> img(_data, "reader.output");
......@@ -82,7 +82,7 @@ namespace TUMVis {
}
}
if (! _sliceExtractor.getInvalidationLevel().isValid()) {
lockGLContextAndExecuteProcessor(_sliceExtractor);
lockGLContextAndExecuteProcessor(&_sliceExtractor);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment