Commit cd9d3feb authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Refactoring AbstractProcessor::process() for clearer semantics and better and...

Refactoring AbstractProcessor::process() for clearer semantics and better and more uniform handling of invalidation levels:
 * AbstractProcessor::process() now calls updateShader(), updateProperties() and/or updateResult() with respect to the current invalidation level
 * each processor shall no longer override process() but the updateXYZ() methods, at minimum updateResult()
 * AbstractProcessor::process() takes care of (un)locking the processor itself (no need to do this from the outside anymore)

Further implicit changes:
 * Removed redundant HasPropertyCollection::updateProperties()
parent 882f4d50
...@@ -620,7 +620,6 @@ namespace campvis { ...@@ -620,7 +620,6 @@ namespace campvis {
} }
void DataContainerInspectorCanvas::onPropertyChanged(const AbstractProperty* prop) { void DataContainerInspectorCanvas::onPropertyChanged(const AbstractProperty* prop) {
HasPropertyCollection::onPropertyChanged(prop);
invalidate(); invalidate();
/// if the Mesh Solid Color property is changed, update the mesh's color /// if the Mesh Solid Color property is changed, update the mesh's color
......
...@@ -132,9 +132,6 @@ namespace campvis { ...@@ -132,9 +132,6 @@ namespace campvis {
_ignoreCanvasSizeUpdate = false; _ignoreCanvasSizeUpdate = false;
} }
} }
else {
HasPropertyCollection::onPropertyChanged(prop);
}
} }
const DataContainer& AbstractPipeline::getDataContainer() const { const DataContainer& AbstractPipeline::getDataContainer() const {
...@@ -148,19 +145,9 @@ namespace campvis { ...@@ -148,19 +145,9 @@ namespace campvis {
void AbstractPipeline::executeProcessor(AbstractProcessor* processor, bool unlockInExtraThred) { void AbstractPipeline::executeProcessor(AbstractProcessor* processor, bool unlockInExtraThred) {
tgtAssert(processor != 0, "Processor must not be 0."); tgtAssert(processor != 0, "Processor must not be 0.");
// execute processor if needed
if (processor->getEnabled() && !processor->isLocked()) { if (processor->getEnabled() && !processor->isLocked()) {
// update properties if they're invalid if (! processor->isValid()) {
if (processor->hasInvalidProperties()) {
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()?");
#endif
}
// execute processor if needed
if (processor->hasInvalidResult()) {
processor->lockProcessor();
clock_t startTime = clock(); clock_t startTime = clock();
try { try {
...@@ -175,15 +162,8 @@ namespace campvis { ...@@ -175,15 +162,8 @@ namespace campvis {
if (processor->getClockExecutionTime()) { if (processor->getClockExecutionTime()) {
clock_t endTime = clock(); clock_t endTime = clock();
LDEBUG("Executed processor " << processor->getName() << " duration: " << (endTime - startTime)); LINFO("Executed processor " << processor->getName() << " duration: " << (endTime - startTime));
} }
// Unlocking processors might be expensive, since a long chain of invalidations might be started
// -> do this in another thread...
if (unlockInExtraThred)
SimpleJobProc.enqueueJob(makeJob(processor, &AbstractProcessor::unlockProcessor));
else
processor->unlockProcessor();
} }
} }
} }
...@@ -235,16 +215,6 @@ namespace campvis { ...@@ -235,16 +215,6 @@ namespace campvis {
_processors.push_back(processor); _processors.push_back(processor);
} }
void AbstractPipeline::lockAllProcessors() {
for (std::vector<AbstractProcessor*>::iterator it = _processors.begin(); it != _processors.end(); ++it)
(*it)->lockProcessor();
}
void AbstractPipeline::unlockAllProcessors() {
for (std::vector<AbstractProcessor*>::iterator it = _processors.begin(); it != _processors.end(); ++it)
(*it)->unlockProcessor();
}
void AbstractPipeline::lockGLContextAndExecuteProcessor(AbstractProcessor* processor) { void AbstractPipeline::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!");
GLJobProc.enqueueJob( GLJobProc.enqueueJob(
......
...@@ -173,16 +173,6 @@ namespace campvis { ...@@ -173,16 +173,6 @@ namespace campvis {
sigslot::signal0<> s_renderTargetChanged; sigslot::signal0<> s_renderTargetChanged;
protected: protected:
/**
* Locks all processors.
*/
void lockAllProcessors();
/**
* Unlocks all processors.
*/
void unlockAllProcessors();
/** /**
* 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.
......
...@@ -24,8 +24,12 @@ ...@@ -24,8 +24,12 @@
#include <tbb/compat/thread> #include <tbb/compat/thread>
#include "tgt/assert.h" #include "tgt/assert.h"
#include "abstractprocessor.h" #include "abstractprocessor.h"
#include "core/properties/abstractproperty.h" #include "core/properties/abstractproperty.h"
#include "core/tools/job.h"
#include "core/tools/opengljobprocessor.h"
#include "core/tools/simplejobprocessor.h"
namespace campvis { namespace campvis {
...@@ -80,7 +84,6 @@ namespace campvis { ...@@ -80,7 +84,6 @@ namespace campvis {
} }
void AbstractProcessor::onPropertyChanged(const AbstractProperty* prop) { void AbstractProcessor::onPropertyChanged(const AbstractProperty* prop) {
HasPropertyCollection::onPropertyChanged(prop);
invalidate(prop->getInvalidationLevel()); invalidate(prop->getInvalidationLevel());
} }
...@@ -122,9 +125,45 @@ namespace campvis { ...@@ -122,9 +125,45 @@ namespace campvis {
_clockExecutionTime = value; _clockExecutionTime = value;
} }
void AbstractProcessor::process(DataContainer& data, bool unlockInExtraThread) {
// use a scoped lock for exception safety
AbstractProcessor::ScopedLock lock(this, unlockInExtraThread);
if (hasInvalidShader())
updateShader();
if (hasInvalidProperties())
updateProperties(data);
if (hasInvalidResult())
updateResult(data);
}
void AbstractProcessor::updateShader() {
LDEBUG("Called non-overriden updateShader() in " << getName() << ". Did you forget to override your method?");
validate(INVALID_SHADER);
}
void AbstractProcessor::updateProperties(DataContainer& dc) { void AbstractProcessor::updateProperties(DataContainer& dc) {
LDEBUG("Called non-overriden updateProperties() in " << getName() << ". Did you forget to override your method?");
validate(INVALID_PROPERTIES); validate(INVALID_PROPERTIES);
} }
// ================================================================================================
AbstractProcessor::ScopedLock::ScopedLock(AbstractProcessor* p, bool unlockInExtraThread)
: _p(p)
, _unlockInExtraThread(unlockInExtraThread)
{
_p->lockProcessor();
}
AbstractProcessor::ScopedLock::~ScopedLock() {
// Unlocking processors might be expensive, since a long chain of invalidations might be started
// -> do this in another thread...
if (_unlockInExtraThread)
SimpleJobProc.enqueueJob(makeJob(_p, &AbstractProcessor::unlockProcessor));
else
_p->unlockProcessor();
}
} }
...@@ -57,10 +57,10 @@ namespace campvis { ...@@ -57,10 +57,10 @@ namespace campvis {
* Available invalidation levels * Available invalidation levels
*/ */
enum InvalidationLevel { enum InvalidationLevel {
VALID = 0, ///< Valid VALID = 0, ///< Valid, no need to run the process() method
INVALID_RESULT = 1 << 0, ///< Need to rerun the process() method INVALID_RESULT = 1 << 0, ///< Need to run the updateResult() method
INVALID_SHADER = 1 << 1, ///< Need to recompile the shader INVALID_SHADER = 1 << 1, ///< Need to run the updateShader() method (e.g. to recompile the shader)
INVALID_PROPERTIES = 1 << 2, ///< Need to update the properties INVALID_PROPERTIES = 1 << 2, ///< Need to run the updateProperties() method (e.g. to adjust property ranges)
FIRST_FREE_TO_USE_INVALIDATION_LEVEL = 1 << 3 FIRST_FREE_TO_USE_INVALIDATION_LEVEL = 1 << 3
}; };
...@@ -96,12 +96,6 @@ namespace campvis { ...@@ -96,12 +96,6 @@ namespace campvis {
*/ */
virtual void deinit(); virtual void deinit();
/**
* Execute this processor.
* \param data DataContainer to work on.
**/
virtual void process(DataContainer& data) = 0;
/** /**
* Gets the name of this very processor. To be defined by every subclass. * Gets the name of this very processor. To be defined by every subclass.
* \return The name of this processor. * \return The name of this processor.
...@@ -125,6 +119,16 @@ namespace campvis { ...@@ -125,6 +119,16 @@ namespace campvis {
* \return The current processor state in terms of stability. * \return The current processor state in terms of stability.
*/ */
virtual ProcessorState getProcessorState() const = 0; virtual ProcessorState getProcessorState() const = 0;
/**
* Execute this processor.
* Locks the processor and calls updateShader(), updateProperties() and/or updateResult()
* with respect to the current invalidation level.
*
* \param data DataContainer to work on.
* \param unlockInExtraThread Flag whether the processor shall be unlockedin an extra thread (since unlock might be expensive).
**/
void process(DataContainer& data, bool unlockInExtraThread = false);
/** /**
* Gets the flag whether this processor is currently enabled. * Gets the flag whether this processor is currently enabled.
...@@ -149,19 +153,6 @@ namespace campvis { ...@@ -149,19 +153,6 @@ namespace campvis {
* \param value The new flag vlaue whether to measure the execution time of this processor. * \param value The new flag vlaue whether to measure the execution time of this processor.
*/ */
void setClockExecutionTime(bool value); void setClockExecutionTime(bool value);
/**
*
* Locks all properties in the processor's PropertyCollection and marks them as "in use".
* \sa AbstractProcessor::unlockProcessor
*/
void lockProcessor();
/**
* Unlocks all properties in the processor's PropertyCollection and marks them as "not in use".
* \sa AbstractProcessor::lockProcessor
*/
void unlockProcessor();
/** /**
* Returns the current lockProcessor status of this processor. * Returns the current lockProcessor status of this processor.
...@@ -247,25 +238,76 @@ namespace campvis { ...@@ -247,25 +238,76 @@ namespace campvis {
validate(static_cast<int>(il)); validate(static_cast<int>(il));
} }
/**
* Gets called from the pipeline before calling process(), when this processor has an INVALID_PROPERTIES level.
* \note You may overload this method as needed. The default implementation only validates
* the INVALID_PROPERTIES level again.
* \note There is also an overloadable updateProperties() in the HasPropertyCollection super class,
* which is called from the processor itself. If you do not need access to the DataContainer
* of the parent pipeline, you can also use that method.
* \see HasPropertyCollection::updateProperties()
* \param dc DataContainer The DataContainer of the calling pipeline.
*/
virtual void updateProperties(DataContainer& dc);
/// Signal emitted when the processor has been invalidated. /// Signal emitted when the processor has been invalidated.
sigslot::signal1<AbstractProcessor*> s_invalidated; sigslot::signal1<AbstractProcessor*> s_invalidated;
/// Signal emitted when the processor has been validated. /// Signal emitted when the processor has been validated.
sigslot::signal1<AbstractProcessor*> s_validated; sigslot::signal1<AbstractProcessor*> s_validated;
protected:
/**
* Scoped lock of an AbstractProcessor that automatically unlocks the processor on destruction.
* Useful for exception safety.
*/
struct ScopedLock {
/**
* Constructs a new Scoped lock, locking \a p and unlocking \a p on destruction.
* \param p Processor to lock
* \param unlockInExtraThread Unlock \a p in extra thread (since this might be an expensive operation)
*/
ScopedLock(AbstractProcessor* p, bool unlockInExtraThread);
/// Destructor, unlocks the processor
~ScopedLock();
AbstractProcessor* _p; ///< The processor to lock
bool _unlockInExtraThread; ///< Unlock _p in extra thread (since this might be an expensive operation)
};
/**
* Gets called from default process() method when having an invalidation level of INVALID_SHADER.
*
* Override this method for your needs, for instance if you need to recompile your shaders.
* The default implementation only validates the INVALID_SHADER level again.
*/
virtual void updateShader();
/**
* Gets called from default process() method when having an invalidation level of INVALID_PROPERTIES.
*
* Override this method for your needs, for instance if you need to adjust your properties
* to incoming data or other properties' settings. The default implementation only
* validates the INVALID_PROPERTIES level again.
*
* \param dc DataContainer The DataContainer of the calling pipeline.
*/
virtual void updateProperties(DataContainer& dataContainer);
/**
* Implement this method to your needs to compute the result/output of your processor.
* This method is considered to contain the actual algorithm each processor realizes. It
* gets called from default process() method when having an invalidation level of
* INVALID_RESULT.
*
* \note The default implementation only validates the INVALID_SHADER level again.
* \param dataContainer The DataContainer to work on.
*/
virtual void updateResult(DataContainer& dataContainer) = 0;
/**
*
* Locks all properties in the processor's PropertyCollection and marks them as "in use".
* \sa AbstractProcessor::unlockProcessor
*/
void lockProcessor();
/**
* Unlocks all properties in the processor's PropertyCollection and marks them as "not in use".
* \sa AbstractProcessor::lockProcessor
*/
void unlockProcessor();
// = Slots ======================================================================================== // = Slots ========================================================================================
/** /**
...@@ -274,7 +316,6 @@ namespace campvis { ...@@ -274,7 +316,6 @@ namespace campvis {
*/ */
virtual void onPropertyChanged(const AbstractProperty* prop); virtual void onPropertyChanged(const AbstractProperty* prop);
protected:
tbb::atomic<bool> _enabled; ///< flag whether this processor is currently enabled tbb::atomic<bool> _enabled; ///< flag whether this processor is currently enabled
tbb::atomic<bool> _clockExecutionTime; ///< flag whether to measure the execution time of this processor tbb::atomic<bool> _clockExecutionTime; ///< flag whether to measure the execution time of this processor
......
...@@ -48,7 +48,6 @@ namespace campvis { ...@@ -48,7 +48,6 @@ namespace campvis {
, _fragmentShaderFilename(fragmentShaderFileName) , _fragmentShaderFilename(fragmentShaderFileName)
, _shader(0) , _shader(0)
, _bindEntryExitDepthTextures(bindEntryExitDepthTextures) , _bindEntryExitDepthTextures(bindEntryExitDepthTextures)
, _sourceImageTimestamp(0)
{ {
addProperty(&p_sourceImageID); addProperty(&p_sourceImageID);
addProperty(&p_entryImageID); addProperty(&p_entryImageID);
...@@ -76,27 +75,14 @@ namespace campvis { ...@@ -76,27 +75,14 @@ namespace campvis {
VisualizationProcessor::deinit(); VisualizationProcessor::deinit();
} }
void RaycastingProcessor::process(DataContainer& data) { void RaycastingProcessor::updateResult(DataContainer& data) {
ImageRepresentationGL::ScopedRepresentation img(data, p_sourceImageID.getValue()); ImageRepresentationGL::ScopedRepresentation img(data, p_sourceImageID.getValue());
ScopedTypedData<RenderData> entryPoints(data, p_entryImageID.getValue()); ScopedTypedData<RenderData> entryPoints(data, p_entryImageID.getValue());
ScopedTypedData<RenderData> exitPoints(data, p_exitImageID.getValue()); ScopedTypedData<RenderData> exitPoints(data, p_exitImageID.getValue());
if (img != 0 && entryPoints != 0 && exitPoints != 0) { if (img != 0 && entryPoints != 0 && exitPoints != 0) {
if (img->getDimensionality() == 3) { if (img->getDimensionality() == 3) {
if (img.getDataHandle().getTimestamp() != _sourceImageTimestamp) {
// source DataHandle has changed
_sourceImageTimestamp = img.getDataHandle().getTimestamp();
p_transferFunction.setImageHandle(img.getDataHandle());
}
if (hasInvalidShader()) {
_shader->setHeaders(generateHeader());
_shader->rebuild();
validate(INVALID_SHADER);
}
_shader->activate(); _shader->activate();
_shader->setIgnoreUniformLocationError(true); _shader->setIgnoreUniformLocationError(true);
decorateRenderProlog(data, _shader); decorateRenderProlog(data, _shader);
_shader->setUniform("_viewportSizeRCP", 1.f / tgt::vec2(getEffectiveViewportSize())); _shader->setUniform("_viewportSizeRCP", 1.f / tgt::vec2(getEffectiveViewportSize()));
...@@ -159,4 +145,11 @@ namespace campvis { ...@@ -159,4 +145,11 @@ namespace campvis {
validate(AbstractProcessor::INVALID_PROPERTIES); validate(AbstractProcessor::INVALID_PROPERTIES);
} }
void RaycastingProcessor::updateShader() {
_shader->setHeaders(generateHeader());
_shader->rebuild();
validate(AbstractProcessor::INVALID_SHADER);
}
} }
...@@ -84,6 +84,16 @@ namespace campvis { ...@@ -84,6 +84,16 @@ namespace campvis {
*/ */
virtual void deinit(); virtual void deinit();
DataNameProperty p_sourceImageID; ///< image ID for input image
DataNameProperty p_entryImageID; ///< image ID for output entry points image
DataNameProperty p_exitImageID; ///< image ID for output exit points image
CameraProperty p_camera; ///< Camera used for ray casting
TransferFunctionProperty p_transferFunction; ///< Transfer function
FloatProperty p_jitterStepSizeMultiplier; ///< Step size multiplier for entry points jitter
FloatProperty p_samplingRate; ///< Ray casting sampling rate
protected:
/** /**
* Performs sanity checks, sets up the rendering and calls RaycastingProcessor::processImpl(). * Performs sanity checks, sets up the rendering and calls RaycastingProcessor::processImpl().
* This method first reads the input image, entry and exit points from \a data and validates them. On sucess * This method first reads the input image, entry and exit points from \a data and validates them. On sucess
...@@ -93,21 +103,13 @@ namespace campvis { ...@@ -93,21 +103,13 @@ namespace campvis {
* \sa RaycastingProcessor::processImpl() * \sa RaycastingProcessor::processImpl()
* \param data DataContainer to work on. * \param data DataContainer to work on.
*/ */
virtual void process(DataContainer& data); virtual void updateResult(DataContainer& dataContainer);
/// \see AbstractProcessor::updateProperties /// \see AbstractProcessor::updateProperties
virtual void updateProperties(DataContainer& dc); virtual void updateProperties(DataContainer& dc);
/// \see AbstractProcessor::updateShader
virtual void updateShader();
DataNameProperty p_sourceImageID; ///< image ID for input image
DataNameProperty p_entryImageID; ///< image ID for output entry points image
DataNameProperty p_exitImageID; ///< image ID for output exit points image
CameraProperty p_camera; ///< Camera used for ray casting
TransferFunctionProperty p_transferFunction; ///< Transfer function
FloatProperty p_jitterStepSizeMultiplier; ///< Step size multiplier for entry points jitter
FloatProperty p_samplingRate; ///< Ray casting sampling rate
protected:
/** /**
* Gets called by RaycastingProcessor::process(). * Gets called by RaycastingProcessor::process().
* Put additional (processor specific) setup code here, create and activate your render target(s), render * Put additional (processor specific) setup code here, create and activate your render target(s), render
...@@ -133,9 +135,6 @@ namespace campvis { ...@@ -133,9 +135,6 @@ namespace campvis {
bool _bindEntryExitDepthTextures; ///< Flag whether to also bind the depth textures of the entry-/exit points. bool _bindEntryExitDepthTextures; ///< Flag whether to also bind the depth textures of the entry-/exit points.
static const std::string loggerCat_; static const std::string loggerCat_;
private:
clock_t _sourceImageTimestamp;
}; };
} }
......
...@@ -111,13 +111,8 @@ namespace campvis { ...@@ -111,13 +111,8 @@ namespace campvis {
} }
} }
void HasPropertyCollection::onPropertyChanged(const AbstractProperty* prop) { void HasPropertyCollection::onPropertyChanged(const AbstractProperty* /*prop*/) {
if (prop->getInvalidationLevel() & AbstractProcessor::INVALID_PROPERTIES) // nothing to do here, method is just provided as convenience for child classes.
updateProperties();
} }
void HasPropertyCollection::updateProperties() { }
}
}
\ No newline at end of file
...@@ -112,12 +112,6 @@ namespace campvis { ...@@ -112,12 +112,6 @@ namespace campvis {
virtual void onPropertyChanged(const AbstractProperty* prop); virtual void onPropertyChanged(const AbstractProperty* prop);
protected: protected:
/**
* Gets called, when one of the properties invalidates with an INVALID_PROPERTIES level.
* \note You may overload this method as needed.
*/
virtual void updateProperties();
/** /**
* Searches _properties for a property named \a name. * Searches _properties for a property named \a name.
* \param name Property name to search for. * \param name Property name to search for.
......
...@@ -108,17 +108,13 @@ namespace campvis { ...@@ -108,17 +108,13 @@ namespace campvis {
ShdrMgr.dispose(_shader); ShdrMgr.dispose(_shader);
} }
void AdvancedUsFusion::process(DataContainer& data) { void AdvancedUsFusion::updateResult(DataContainer& data) {
ImageRepresentationGL::ScopedRepresentation img(data, p_usImageId.getValue()); ImageRepresentationGL::ScopedRepresentation img(data, p_usImageId.getValue());
ImageRepresentationGL::ScopedRepresentation blurred(data, p_blurredImageId.getValue()); ImageRepresentationGL::ScopedRepresentation blurred(data, p_blurredImageId.getValue());
ImageRepresentationGL::ScopedRepresentation confidence(data, p_confidenceImageID.getValue()); ImageRepresentationGL::ScopedRepresentation confidence(data, p_confidenceImageID.getValue());
if (img != 0 && blurred != 0 && confidence != 0) { if (img != 0 && blurred != 0 && confidence != 0) {
if (img->getDimensionality() >= 2) { if (img->getDimensionality() >= 2) {
FramebufferActivationGuard fag(this);
createAndAttachColorTexture();
createAndAttachDepthTexture();
_shader->activate(); _shader->activate();
decorateRenderProlog(data, _shader); decorateRenderProlog(data, _shader);
if (p_use3DTexture.getValue()) if (p_use3DTexture.getValue())
...@@ -135,6 +131,10 @@ namespace campvis { ...@@ -135,6 +131,10 @@ namespace campvis {
p_transferFunction.getTF()->bind(_shader, tfUnit); p_transferFunction.getTF()->bind(_shader, tfUnit);
p_confidenceTF.getTF()->bind(_shader, tf2Unit, "_confidenceTF", "_confidenceTFParams"); p_confidenceTF.getTF()->bind(_shader, tf2Unit, "_confidenceTF", "_confidenceTFParams");