Currently job artifacts in CI/CD pipelines on LRZ GitLab never expire. Starting from Wed 26.1.2022 the default expiration time will be 30 days (GitLab default). Currently existing 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 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();
/**
* Generates the GLSL header.
*/
......