Commit b02a8044 authored by Karthik Krishnan's avatar Karthik Krishnan

ENH: Part 3 of the multi-threading and optimization of space leaping

This adds customized functions for traverising the input scalars in
one pass to compute the min-max-gradient_max space leaping structure.
Caching of the output to ensure that the entire output is not regenerated
based on flags is also added. It was missing earlier.

Please see the earlier two feature commits :

  MultiThread-SpaceLeaping-VolumeReadering-Feature*
parent 1f254e07
......@@ -14,6 +14,7 @@ SET(RENDERING_EXAMPLES_SRCS
IF(VTK_USE_METAIO)
LIST(APPEND RENDERING_EXAMPLES_SRCS
GPURenderDemo
FixedPointVolumeRayCastMapperCT
)
ENDIF(VTK_USE_METAIO)
......
......@@ -756,8 +756,14 @@ vtkFixedPointVolumeRayCastMapper::vtkFixedPointVolumeRayCastMapper()
this->TableScale[3] = 1;
this->SpaceLeapFilter = vtkVolumeRayCastSpaceLeapingImageFilter::New();
// Cached space leaping output. This is shared between runs. The output
// of the last run is passed back to the SpaceLeapFilter and its reused
// since we may not be updating every flag in this structure.
this->MinMaxVolumeCache = vtkImageData::New();
}
//----------------------------------------------------------------------------
// Destruct a vtkFixedPointVolumeRayCastMapper - clean up any memory used
vtkFixedPointVolumeRayCastMapper::~vtkFixedPointVolumeRayCastMapper()
{
......@@ -843,6 +849,8 @@ vtkFixedPointVolumeRayCastMapper::~vtkFixedPointVolumeRayCastMapper()
delete [] this->TransformedClippingPlanes;
this->ImageDisplayHelper->Delete();
this->MinMaxVolumeCache->Delete();
}
float vtkFixedPointVolumeRayCastMapper::ComputeRequiredImageSampleDistance( float desiredTime,
......@@ -1051,13 +1059,16 @@ void vtkFixedPointVolumeRayCastMapper::UpdateMinMaxVolume( vtkVolume *vol )
return;
}
// Set the update flags, telling the filter what to update...
this->SpaceLeapFilter->SetInput(this->GetInput());
this->SpaceLeapFilter->SetCurrentScalars(this->CurrentScalars);
this->SpaceLeapFilter->SetIndependentComponents(
vol->GetProperty()->GetIndependentComponents());
this->SpaceLeapFilter->SetComputeMinMax(needToUpdate&0x02);
this->SpaceLeapFilter->SetComputeGradientOpacity(needToUpdate&0x04);
this->SpaceLeapFilter->SetComputeMinMax((needToUpdate&0x02) ? 1 : 0);
this->SpaceLeapFilter->SetComputeGradientOpacity((needToUpdate&0x04)? 1 : 0);
this->SpaceLeapFilter->SetUpdateGradientOpacityFlags(
(this->GradientOpacityRequired && (needToUpdate&0x01)) ? 1 : 0 );
this->SpaceLeapFilter->SetGradientMagnitude(this->GradientMagnitude);
this->SpaceLeapFilter->SetTableSize(this->TableSize);
this->SpaceLeapFilter->SetTableShift(this->TableShift);
......@@ -1069,15 +1080,25 @@ void vtkFixedPointVolumeRayCastMapper::UpdateMinMaxVolume( vtkVolume *vol )
this->SpaceLeapFilter->SetGradientOpacityTable(
compIdx, this->GradientOpacityTable[compIdx]);
}
this->SpaceLeapFilter->SetCache(this->MinMaxVolumeCache);
this->SpaceLeapFilter->Update();
this->MinMaxVolume =
this->SpaceLeapFilter->GetMinMaxVolume(this->MinMaxVolumeSize);
// Cached space leaping output. This is shared between runs. The output
// of the last run is passed back to the SpaceLeapFilter and its reused
// since we may not be updating every flag in this structure.
this->MinMaxVolumeCache->ShallowCopy(this->SpaceLeapFilter->GetOutput());
// For debugging if necessary, write the min-max-volume components.
//vtkVolumeRayCastSpaceLeapingImageFilter::WriteMinMaxVolume(
// 0,this->MinMaxVolume,this->MinMaxVolumeSize,
// "MinMaxVolumeNewComponent0.mha");
// If the line below is commented out, we get reference counting loops
this->SpaceLeapFilter->SetInput(NULL);
// Regenerate the min max values if necessary
if ( needToUpdate&0x02 )
{
this->SavedMinMaxInput = input;
......
......@@ -482,6 +482,7 @@ protected:
unsigned short *MinMaxVolume;
int MinMaxVolumeSize[4];
vtkImageData *SavedMinMaxInput;
vtkImageData *MinMaxVolumeCache;
vtkVolumeRayCastSpaceLeapingImageFilter * SpaceLeapFilter;
void UpdateMinMaxVolume( vtkVolume *vol );
......
......@@ -20,6 +20,11 @@
#include "vtkObjectFactory.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkDataArray.h"
#include "vtkPointData.h"
#ifdef vtkVolumeRayCastSpaceLeapingImageFilter_DEBUG
#include "vtkMetaImageWriter.h"
#endif
#include <math.h>
......@@ -36,6 +41,7 @@ vtkVolumeRayCastSpaceLeapingImageFilter::vtkVolumeRayCastSpaceLeapingImageFilter
{
this->ComputeMinMax = 0;
this->ComputeGradientOpacity = 0;
this->UpdateGradientOpacityFlags = 0;
this->IndependentComponents = 1;
this->CurrentScalars = NULL;
this->MinNonZeroScalarIndex = NULL;
......@@ -49,6 +55,7 @@ vtkVolumeRayCastSpaceLeapingImageFilter::vtkVolumeRayCastSpaceLeapingImageFilter
this->ScalarOpacityTable[i] = NULL;
this->GradientOpacityTable[i] = NULL;
}
this->Cache = NULL;
}
//----------------------------------------------------------------------------
......@@ -74,6 +81,7 @@ void vtkVolumeRayCastSpaceLeapingImageFilter::PrintSelf(ostream& os, vtkIndent i
os << indent << "ComputeMinMax: " << this->ComputeMinMax << "\n";
os << indent << "ComputeGradientOpacity: " << this->ComputeGradientOpacity << "\n";
os << indent << "UpdateGradientOpacityFlags: " << this->UpdateGradientOpacityFlags << "\n";
os << indent << "IndependentComponents: " << this->IndependentComponents << "\n";
os << indent << "CurrentScalars: " << this->CurrentScalars << "\n";
// this->TableShift
......@@ -103,6 +111,14 @@ int vtkVolumeRayCastSpaceLeapingImageFilter::RequestUpdateExtent (
return 1;
}
//----------------------------------------------------------------------------
void vtkVolumeRayCastSpaceLeapingImageFilter
::SetCache( vtkImageData * cache )
{
// Do not reference count it to avoid reference counting loops
this->Cache = cache;
}
//----------------------------------------------------------------------------
void vtkVolumeRayCastSpaceLeapingImageFilter
::InternalRequestUpdateExtent( int *inExt,
......@@ -783,11 +799,18 @@ void vtkVolumeRayCastSpaceLeapingImageFilter::ThreadedRequestData(
const int components = this->GetCurrentScalars()->GetNumberOfComponents();
const unsigned int nComponents = (this->GetIndependentComponents()) ? components : 1;
vtkVolumeRayCastSpaceLeapingImageFilterClearOutput(
// Clear the output if we are computing the min-max. In other cases, we
// will be re-using the cache. (See the method AllocateOutputData)
if (this->ComputeMinMax)
{
vtkVolumeRayCastSpaceLeapingImageFilterClearOutput(
outData[0], outExt, nComponents );
}
// If only scalar min-max need to be re-computed
if (this->ComputeMinMax && !this->ComputeGradientOpacity)
{
int scalarType = this->CurrentScalars->GetDataType();
......@@ -801,12 +824,10 @@ void vtkVolumeRayCastSpaceLeapingImageFilter::ThreadedRequestData(
vtkErrorMacro("Unknown scalar type");
return;
}
// Process the flags based on the computed min-max volume
this->FillScalarOpacityFlags(outData[0], outExt);
}
// If only gradient max needs to be recomputed
else if (this->ComputeGradientOpacity && !this->ComputeMinMax)
{
int scalarType = this->CurrentScalars->GetDataType();
......@@ -820,13 +841,11 @@ void vtkVolumeRayCastSpaceLeapingImageFilter::ThreadedRequestData(
vtkErrorMacro("Unknown scalar type");
return;
}
// Process the flags based on the computed min-max volume
this->FillScalarAndGradientOpacityFlags(outData[0], outExt);
}
// If both scalar min-max need to be re-computed and gradient max needs to
// be re-computed
else if (this->ComputeGradientOpacity && this->ComputeMinMax)
{
int scalarType = this->CurrentScalars->GetDataType();
......@@ -840,11 +859,22 @@ void vtkVolumeRayCastSpaceLeapingImageFilter::ThreadedRequestData(
vtkErrorMacro("Unknown scalar type");
return;
}
}
// Update the flags now for this extent. There are two specialized methods
// here, depending on what mode we are in, so that we may do the flag update
// in one pass through the data.
if (this->UpdateGradientOpacityFlags)
{
// Process the flags based on the computed min-max volume
this->FillScalarAndGradientOpacityFlags(outData[0], outExt);
}
else
{
this->FillScalarOpacityFlags(outData[0], outExt);
}
}
//----------------------------------------------------------------------------
......@@ -854,16 +884,17 @@ int vtkVolumeRayCastSpaceLeapingImageFilter::RequestData(
vtkInformationVector** inputVector,
vtkInformationVector* outputVector)
{
if (!(this->ComputeGradientOpacity || this->ComputeMinMax))
{
return 1; // Nothing to do. Just pass through
}
#ifdef vtkVolumeRayCastSpaceLeapingImageFilter_DEBUG
cout << "ComputingGradientOpacity: " << ComputeGradientOpacity;
cout << " ComputingMinMax: " << ComputeMinMax << " UpdatingFlags: 1" << endl;
#endif
// Find the first non-zero scalar opacity and gradient opacity points on
// the respective transfer functions
this->ComputeFirstNonZeroOpacityIndices();
// The actual work is done in the line below.
if (this->Superclass::RequestData(request, inputVector, outputVector))
{
......@@ -1043,12 +1074,98 @@ unsigned short * vtkVolumeRayCastSpaceLeapingImageFilter
unsigned long vtkVolumeRayCastSpaceLeapingImageFilter
::ComputeOffset( int ext[6], int wholeExt[6], int nComponents )
{
const unsigned int wDim[3] = { wholeExt[5]-wholeExt[4]+1,
const unsigned int wDim[3] = { wholeExt[1]-wholeExt[0]+1,
wholeExt[3]-wholeExt[2]+1,
wholeExt[1]-wholeExt[0]+1 };
wholeExt[5]-wholeExt[4]+1 };
unsigned long offset = (wDim[1]*wDim[0]*(ext[4]-wholeExt[4]) +
wDim[0]*(ext[2]-wholeExt[2]) +
(ext[0]-wholeExt[0]))
* nComponents;
return offset;
}
//----------------------------------------------------------------------------
// Allocate the output data, caching if necessary. Caching may result in
// invalid outputs and should be turned on, only when this filter is used
// as an internal ivar of the vtkFixedPointVolumeRayCastMapper.
void vtkVolumeRayCastSpaceLeapingImageFilter
::AllocateOutputData(vtkImageData *output, int *uExtent)
{
// set the extent to be the update extent
output->SetExtent(uExtent);
if (this->Cache)
{
int extent[6];
this->Cache->GetExtent(extent);
if (extent[0] == uExtent[0] && extent[1] == uExtent[1] &&
extent[2] == uExtent[2] && extent[3] == uExtent[3] &&
extent[4] == uExtent[4] && extent[5] == uExtent[5] &&
this->Cache->GetNumberOfScalarComponents() ==
output->GetNumberOfScalarComponents())
{
// Reuse the cache since it has the same dimensions. We may not be
// updating all flags
// This is absolutely scary code, if used as a standard VTK imaging filter,
// but since the filter will be used only as an iVar of
// vtkFixedPointVolumeRayCastMapper, we need a caching mechanism to avoid
// reallocation of memory and re-update of certain bits in the Min-max
// structure. In the interest of speed, we resort to a wee bit of ugly
// code.
output->GetPointData()->SetScalars(
this->Cache->GetPointData()->GetScalars() );
return;
}
}
// Otherwise allocate output afresh
output->AllocateScalars();
}
//----------------------------------------------------------------------------
vtkImageData *vtkVolumeRayCastSpaceLeapingImageFilter
::AllocateOutputData(vtkDataObject *output)
{
// Call the superclass method
return vtkImageAlgorithm::AllocateOutputData(output);
}
//----------------------------------------------------------------------------
#ifdef vtkVolumeRayCastSpaceLeapingImageFilter_DEBUG
void vtkVolumeRayCastSpaceLeapingImageFilter
::WriteMinMaxVolume(
int component, unsigned short *minMaxVolume,
int minMaxVolumeSize[4],
const char *filename )
{
vtkImageData *image = vtkImageData::New();
image->SetExtent(0, minMaxVolumeSize[0]-1,
0, minMaxVolumeSize[1]-1,
0, minMaxVolumeSize[2]-1);
image->SetScalarTypeToUnsignedShort();
image->AllocateScalars();
const unsigned int nComponents = static_cast< unsigned int >(
minMaxVolumeSize[3]);
const unsigned long nVoxels =
minMaxVolumeSize[0]*minMaxVolumeSize[1]*minMaxVolumeSize[2];
const unsigned int inc = nComponents * 3;
unsigned short *pSrc = minMaxVolume + component;
unsigned short *pDst = static_cast< unsigned short * >(
image->GetScalarPointer());
for (unsigned long i = 0; i < nVoxels; ++i, pSrc += inc, ++pDst)
{
*pDst = *pSrc;
}
vtkMetaImageWriter *writer = vtkMetaImageWriter::New();
writer->SetFileName(filename);
writer->SetInput(image);
writer->Write();
writer->Delete();
image->Delete();
}
#endif
......@@ -44,7 +44,6 @@ public:
// Description:
// Set the scalars.
// FIXME Rename to Scalars ?
virtual void SetCurrentScalars( vtkDataArray * );
vtkGetObjectMacro( CurrentScalars, vtkDataArray );
......@@ -60,12 +59,18 @@ public:
vtkBooleanMacro( ComputeGradientOpacity, int );
// Description:
// Compute the min max structure ?. At least ComputeGradientOpacity or
// ComputeMinMax or both have to enabled for the filter to execute.
// Compute the min max structure ?.
vtkSetMacro( ComputeMinMax, int );
vtkGetMacro( ComputeMinMax, int );
vtkBooleanMacro( ComputeMinMax, int );
// Description:
// Update the gradient opacity flags. (The scalar opacity flags are always
// updated upon execution of this filter.)
vtkSetMacro( UpdateGradientOpacityFlags, int );
vtkGetMacro( UpdateGradientOpacityFlags, int );
vtkBooleanMacro( UpdateGradientOpacityFlags, int );
// Description:
// Get the last execution time. This is updated every
// time the scalars or the gradient opacity values are computed
......@@ -106,6 +111,12 @@ public:
// GetNumberOfIndependentComponents())
unsigned short * GetMinMaxVolume( int dims[4] );
// Description:
// INTERNAL - Do not use
// Set the last cached min-max volume, as used by
// vtkFixedPointVolumeRayCastMapper.
virtual void SetCache(vtkImageData * imageCache);
// Description:
// Compute the extents and dimensions of the input that's required to
// generate an output min-max structure given by outExt.
......@@ -141,6 +152,13 @@ public:
// the data starting at extents ext.
unsigned long ComputeOffset(int ext[6], int wholeExt[6], int nComponents);
//BTX
// This method helps debug. It writes out a specific component of the
// computed min-max-volume structure
//static void WriteMinMaxVolume( int component, unsigned short *minMaxVolume,
// int minMaxVolumeSize[4], const char *filename );
//ETX
protected:
vtkVolumeRayCastSpaceLeapingImageFilter();
~vtkVolumeRayCastSpaceLeapingImageFilter();
......@@ -154,45 +172,61 @@ protected:
int TableSize[4];
int ComputeGradientOpacity;
int ComputeMinMax;
int UpdateGradientOpacityFlags;
unsigned short * MinNonZeroScalarIndex;
unsigned char * MinNonZeroGradientMagnitudeIndex;
unsigned char ** GradientMagnitude;
unsigned short * ScalarOpacityTable[4];
unsigned short * GradientOpacityTable[4];
vtkImageData * Cache;
void InternalRequestUpdateExtent(int *, int*);
// Description:
// See superclass for details
virtual int RequestUpdateExtent(vtkInformation *,
vtkInformationVector **,
vtkInformationVector *);
void InternalRequestUpdateExtent(int *, int*);
void ThreadedRequestData(vtkInformation *request,
vtkInformationVector **inputVector,
vtkInformationVector *outputVector,
vtkImageData ***inData, vtkImageData **outData,
int outExt[6], int id);
virtual int RequestData( vtkInformation* request,
vtkInformationVector** inputVector,
vtkInformationVector* outputVector);
void ThreadedRequestData( vtkInformation *request,
vtkInformationVector **inputVector,
vtkInformationVector *outputVector,
vtkImageData ***inData,
vtkImageData **outData,
int outExt[6], int id);
virtual int RequestData( vtkInformation* request,
vtkInformationVector** inputVector,
vtkInformationVector* outputVector);
virtual int RequestInformation( vtkInformation *,
vtkInformationVector**,
vtkInformationVector *);
// Description:
// Compute the first non-zero scalar opacity and gradient opacity values
// that are encountered when marching from the beginning of the transfer
// function tables.
void ComputeFirstNonZeroOpacityIndices();
// Description:
// Fill the flags after processing the min/max/gradient structure. This
// optimized version is invoked when only scalar opacity table is needed.
void FillScalarOpacityFlags(
vtkImageData *minMaxVolume, int outExt[6] );
// Description:
// Fill the flags after processing the min/max/gradient structure. This
// optimized version is invoked when both scalar and gradient opacity
// tables need to be visited.
void FillScalarAndGradientOpacityFlags(
vtkImageData *minMaxVolume, int outExt[6] );
void FillScalarOpacityFlags(
vtkImageData *minMaxVolume, int outExt[6] );
// Description:
// Allocate the output data. If we have a cache with the same metadata as
// the output we are going to generate, re-use the cache as we may not be
// updating all data in the min-max structure.
virtual void AllocateOutputData( vtkImageData *out, int *uExtent );
virtual vtkImageData *AllocateOutputData(vtkDataObject *out);
private:
vtkVolumeRayCastSpaceLeapingImageFilter(const vtkVolumeRayCastSpaceLeapingImageFilter&); // Not implemented.
void operator=(const vtkVolumeRayCastSpaceLeapingImageFilter&); // Not implemented.
......
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