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 {
}
void DataContainerInspectorCanvas::onPropertyChanged(const AbstractProperty* prop) {
HasPropertyCollection::onPropertyChanged(prop);
invalidate();
/// if the Mesh Solid Color property is changed, update the mesh's color
......
......@@ -132,9 +132,6 @@ namespace campvis {
_ignoreCanvasSizeUpdate = false;
}
}
else {
HasPropertyCollection::onPropertyChanged(prop);
}
}
const DataContainer& AbstractPipeline::getDataContainer() const {
......@@ -148,19 +145,9 @@ namespace campvis {
void AbstractPipeline::executeProcessor(AbstractProcessor* processor, bool unlockInExtraThred) {
tgtAssert(processor != 0, "Processor must not be 0.");
// execute processor if needed
if (processor->getEnabled() && !processor->isLocked()) {
// update properties if they're invalid
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();
if (! processor->isValid()) {
clock_t startTime = clock();
try {
......@@ -175,15 +162,8 @@ namespace campvis {
if (processor->getClockExecutionTime()) {
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 {
_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) {
tgtAssert(_canvas != 0, "Set a valid canvas before calling this method!");
GLJobProc.enqueueJob(
......
......@@ -173,16 +173,6 @@ namespace campvis {
sigslot::signal0<> s_renderTargetChanged;
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.
* \param processor Processor to execute.
......
......@@ -24,8 +24,12 @@
#include <tbb/compat/thread>
#include "tgt/assert.h"
#include "abstractprocessor.h"
#include "core/properties/abstractproperty.h"
#include "core/tools/job.h"
#include "core/tools/opengljobprocessor.h"
#include "core/tools/simplejobprocessor.h"
namespace campvis {
......@@ -80,7 +84,6 @@ namespace campvis {
}
void AbstractProcessor::onPropertyChanged(const AbstractProperty* prop) {
HasPropertyCollection::onPropertyChanged(prop);
invalidate(prop->getInvalidationLevel());
}
......@@ -122,9 +125,45 @@ namespace campvis {
_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) {
LDEBUG("Called non-overriden updateProperties() in " << getName() << ". Did you forget to override your method?");
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 {
* Available invalidation levels
*/
enum InvalidationLevel {
VALID = 0, ///< Valid
INVALID_RESULT = 1 << 0, ///< Need to rerun the process() method
INVALID_SHADER = 1 << 1, ///< Need to recompile the shader
INVALID_PROPERTIES = 1 << 2, ///< Need to update the properties
VALID = 0, ///< Valid, no need to run the process() method
INVALID_RESULT = 1 << 0, ///< Need to run the updateResult() method
INVALID_SHADER = 1 << 1, ///< Need to run the updateShader() method (e.g. to recompile the shader)
INVALID_PROPERTIES = 1 << 2, ///< Need to run the updateProperties() method (e.g. to adjust property ranges)
FIRST_FREE_TO_USE_INVALIDATION_LEVEL = 1 << 3
};
......@@ -96,12 +96,6 @@ namespace campvis {
*/
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.
* \return The name of this processor.
......@@ -125,6 +119,16 @@ namespace campvis {
* \return The current processor state in terms of stability.
*/
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.
......@@ -149,19 +153,6 @@ namespace campvis {
* \param value The new flag vlaue whether to measure the execution time of this processor.
*/
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.
......@@ -247,25 +238,76 @@ namespace campvis {
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.
sigslot::signal1<AbstractProcessor*> s_invalidated;
/// Signal emitted when the processor has been 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 ========================================================================================
/**
......@@ -274,7 +316,6 @@ namespace campvis {
*/
virtual void onPropertyChanged(const AbstractProperty* prop);
protected:
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
......
......@@ -48,7 +48,6 @@ namespace campvis {
, _fragmentShaderFilename(fragmentShaderFileName)
, _shader(0)
, _bindEntryExitDepthTextures(bindEntryExitDepthTextures)
, _sourceImageTimestamp(0)
{
addProperty(&p_sourceImageID);
addProperty(&p_entryImageID);
......@@ -76,27 +75,14 @@ namespace campvis {
VisualizationProcessor::deinit();
}
void RaycastingProcessor::process(DataContainer& data) {
void RaycastingProcessor::updateResult(DataContainer& data) {
ImageRepresentationGL::ScopedRepresentation img(data, p_sourceImageID.getValue());
ScopedTypedData<RenderData> entryPoints(data, p_entryImageID.getValue());
ScopedTypedData<RenderData> exitPoints(data, p_exitImageID.getValue());
if (img != 0 && entryPoints != 0 && exitPoints != 0) {
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->setIgnoreUniformLocationError(true);
decorateRenderProlog(data, _shader);
_shader->setUniform("_viewportSizeRCP", 1.f / tgt::vec2(getEffectiveViewportSize()));
......@@ -159,4 +145,11 @@ namespace campvis {
validate(AbstractProcessor::INVALID_PROPERTIES);
}
void RaycastingProcessor::updateShader() {
_shader->setHeaders(generateHeader());
_shader->rebuild();
validate(AbstractProcessor::INVALID_SHADER);
}
}
......@@ -84,6 +84,16 @@ namespace campvis {
*/
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().
* 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 {
* \sa RaycastingProcessor::processImpl()
* \param data DataContainer to work on.
*/
virtual void process(DataContainer& data);
virtual void updateResult(DataContainer& dataContainer);
/// \see AbstractProcessor::updateProperties
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().
* Put additional (processor specific) setup code here, create and activate your render target(s), render
......@@ -133,9 +135,6 @@ namespace campvis {
bool _bindEntryExitDepthTextures; ///< Flag whether to also bind the depth textures of the entry-/exit points.
static const std::string loggerCat_;
private:
clock_t _sourceImageTimestamp;
};
}
......
......@@ -111,13 +111,8 @@ namespace campvis {
}
}
void HasPropertyCollection::onPropertyChanged(const AbstractProperty* prop) {
if (prop->getInvalidationLevel() & AbstractProcessor::INVALID_PROPERTIES)
updateProperties();
void HasPropertyCollection::onPropertyChanged(const AbstractProperty* /*prop*/) {
// nothing to do here, method is just provided as convenience for child classes.
}
void HasPropertyCollection::updateProperties() {
}
}
\ No newline at end of file
}
......@@ -112,12 +112,6 @@ namespace campvis {
virtual void onPropertyChanged(const AbstractProperty* prop);
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.
* \param name Property name to search for.
......
......@@ -108,17 +108,13 @@ namespace campvis {
ShdrMgr.dispose(_shader);
}
void AdvancedUsFusion::process(DataContainer& data) {
void AdvancedUsFusion::updateResult(DataContainer& data) {
ImageRepresentationGL::ScopedRepresentation img(data, p_usImageId.getValue());
ImageRepresentationGL::ScopedRepresentation blurred(data, p_blurredImageId.getValue());
ImageRepresentationGL::ScopedRepresentation confidence(data, p_confidenceImageID.getValue());
if (img != 0 && blurred != 0 && confidence != 0) {
if (img->getDimensionality() >= 2) {
FramebufferActivationGuard fag(this);
createAndAttachColorTexture();
createAndAttachDepthTexture();
_shader->activate();
decorateRenderProlog(data, _shader);
if (p_use3DTexture.getValue())
......@@ -135,6 +131,10 @@ namespace campvis {
p_transferFunction.getTF()->bind(_shader, tfUnit);
p_confidenceTF.getTF()->bind(_shader, tf2Unit, "_confidenceTF", "_confidenceTFParams");
FramebufferActivationGuard fag(this);
createAndAttachColorTexture();
createAndAttachDepthTexture();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
QuadRdr.renderQuad();
......@@ -158,7 +158,7 @@ namespace campvis {
void AdvancedUsFusion::updateProperties(DataContainer dc) {
ScopedTypedData<ImageData> img(dc, p_usImageId.getValue());
p_transferFunction.getTF()->setImageHandle(img.getDataHandle());
p_transferFunction.setImageHandle(img.getDataHandle());
const tgt::svec3& imgSize = img->getSize();
if (p_sliceNumber.getMaxValue() != imgSize.z - 1){
p_sliceNumber.setMaxValue(static_cast<int>(imgSize.z) - 1);
......
......@@ -73,9 +73,6 @@ namespace campvis {
/// \see AbstractProcessor::getProcessorState()
virtual ProcessorState getProcessorState() const { return AbstractProcessor::EXPERIMENTAL; };
/// \see AbstractProcessor::process()
virtual void process(DataContainer& data);
DataNameProperty p_usImageId; ///< image ID for input image
DataNameProperty p_blurredImageId;
DataNameProperty p_gradientImageID;
......@@ -92,6 +89,8 @@ namespace campvis {
BoolProperty p_use3DTexture;
protected:
/// \see AbstractProcessor::updateResult
virtual void updateResult(DataContainer& dataContainer);
/// adapts the range of the p_sliceNumber property to the image
virtual void updateProperties(DataContainer dc);
......
......@@ -69,7 +69,7 @@ namespace campvis {
_ccclient = 0;
}
void CampcomMhdReceiver::process(DataContainer& data) {
void CampcomMhdReceiver::updateResult(DataContainer& data) {
validate(INVALID_RESULT);
// Get the last received MHD file:
......
......@@ -25,12 +25,12 @@
#ifndef CAMPCOMMHDRECEIVER_H__
#define CAMPCOMMHDRECEIVER_H__
#include <string>
#include <CommonLib/Main/CAMPComDefinitions.h>
#include <CommonLib/DataTypes/MHDImageData.hpp>
#include <ClientLib/src/CAMPComClient.hpp>
#include <string>
#include <tbb/atomic.h>
#include "core/pipeline/abstractprocessor.h"
......@@ -60,13 +60,6 @@ namespace campvis {
/// \see AbstractProcessor::deinit()
virtual void deinit();
/**
* Transforms the last received MHD image (found in _incomingMhd) into CAMPVis ImageData
* and stores it in \a data.
* \param data DataContainer to work on
*/
virtual void process(DataContainer& data);
/// \see AbstractProcessor::getName()
virtual const std::string getName() const { return "CampcomMhdReceiver"; };
/// \see AbstractProcessor::getDescription()
......@@ -84,6 +77,13 @@ namespace campvis {
Vec3Property p_voxelSize; ///< Voxel Size in mm
protected:
/**
* Transforms the last received MHD image (found in _incomingMhd) into CAMPVis ImageData
* and stores it in \a data.
* \param dataContainer DataContainer to work on
*/
virtual void updateResult(DataContainer& dataContainer);
/// Callback slot for connect button
void onBtnConnectClicked();
......
......@@ -75,17 +75,11 @@ namespace campvis {
VisualizationProcessor::deinit();
}
void GeometryStrainRenderer::process(DataContainer& data) {
void GeometryStrainRenderer::updateResult(DataContainer& data) {
ScopedTypedData<GeometryData> proxyGeometry(data, p_geometryID.getValue());
ImageRepresentationGL::ScopedRepresentation strainData(data, p_strainId.getValue());
if (proxyGeometry != 0 && strainData != 0 && _shader != 0) {
if (hasInvalidShader()) {
_shader->setHeaders(generateGlslHeader());
_shader->rebuild();
validate(INVALID_SHADER);
}
// set modelview and projection matrices
FramebufferActivationGuard fag(this);
createAndAttachColorTexture();
......@@ -125,4 +119,10 @@ namespace campvis {
return toReturn;
}
void GeometryStrainRenderer::updateShader() {
_shader->setHeaders(generateGlslHeader());
_shader->rebuild();
validate(INVALID_SHADER);
}
}
......@@ -69,8 +69,6 @@ namespace campvis {
/// \see AbstractProcessor::getProcessorState()
virtual ProcessorState getProcessorState() const { return AbstractProcessor::EXPERIMENTAL; };
virtual void process(DataContainer& data);
DataNameProperty p_geometryID; ///< ID for input geometry
DataNameProperty p_strainId; ///< ID for input strain data
DataNameProperty p_renderTargetID; ///< image ID for output image
......@@ -79,6 +77,11 @@ namespace campvis {
Vec4Property p_color; ///< rendering color
protected:
/// \see AbstractProcessor::updateResult
virtual void updateResult(DataContainer& dataContainer);
/// \see AbstractProcessor::updateShader
virtual void updateShader();