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 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__
#define OPENGLJOBPROCESSOR_H__