You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nmWTAI-Platform/3rd/VTK7.1/source/VTK-7.1.1/Common/ExecutionModel/vtkThreadedImageAlgorithm.cxx

757 lines
22 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkThreadedImageAlgorithm.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkThreadedImageAlgorithm.h"
#include "vtkCellData.h"
#include "vtkCommand.h"
#include "vtkDataArray.h"
#include "vtkImageData.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkMultiThreader.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkSMPTools.h"
// If SMP backend is Sequential then fall back to vtkMultiThreader,
// else enable the newer vtkSMPTools code path by default.
#ifdef VTK_SMP_Sequential
bool vtkThreadedImageAlgorithm::GlobalDefaultEnableSMP = false;
#else
bool vtkThreadedImageAlgorithm::GlobalDefaultEnableSMP = true;
#endif
//----------------------------------------------------------------------------
vtkThreadedImageAlgorithm::vtkThreadedImageAlgorithm()
{
this->Threader = vtkMultiThreader::New();
this->NumberOfThreads = this->Threader->GetNumberOfThreads();
// SMP default settings
this->EnableSMP = vtkThreadedImageAlgorithm::GlobalDefaultEnableSMP;
// Splitting method
this->SplitMode = SLAB;
this->SplitPath[0] = 2;
this->SplitPath[1] = 1;
this->SplitPath[2] = 0;
this->SplitPathLength = 3;
// Minumum block size
this->MinimumPieceSize[0] = 16;
this->MinimumPieceSize[1] = 1;
this->MinimumPieceSize[2] = 1;
// The desired block size in bytes
this->DesiredBytesPerPiece = 65536;
}
//----------------------------------------------------------------------------
vtkThreadedImageAlgorithm::~vtkThreadedImageAlgorithm()
{
this->Threader->Delete();
}
//----------------------------------------------------------------------------
void vtkThreadedImageAlgorithm::SetGlobalDefaultEnableSMP(bool enable)
{
if (enable != vtkThreadedImageAlgorithm::GlobalDefaultEnableSMP)
{
vtkThreadedImageAlgorithm::GlobalDefaultEnableSMP = enable;
}
}
//----------------------------------------------------------------------------
bool vtkThreadedImageAlgorithm::GetGlobalDefaultEnableSMP()
{
return vtkThreadedImageAlgorithm::GlobalDefaultEnableSMP;
}
//----------------------------------------------------------------------------
void vtkThreadedImageAlgorithm::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os,indent);
os << indent << "NumberOfThreads: " << this->NumberOfThreads << "\n";
os << indent << "EnableSMP: " << (this->EnableSMP ? "On\n" : "Off\n");
os << indent << "GlobalDefaultEnableSMP: "
<< (vtkThreadedImageAlgorithm::GlobalDefaultEnableSMP ?
"On\n" : "Off\n");
os << indent << "MinimumPieceSize: "
<< this->MinimumPieceSize[0] << " "
<< this->MinimumPieceSize[1] << " "
<< this->MinimumPieceSize[2] << "\n";
os << indent << "DesiredBytesPerPiece: "
<< this->DesiredBytesPerPiece << "\n";
os << indent << "SplitMode: "
<< (this->SplitMode == SLAB ? "Slab\n" :
(this->SplitMode == BEAM ? "Beam\n" :
(this->SplitMode == BLOCK ? "Block\n" : "Unknown\n")));
}
//----------------------------------------------------------------------------
struct vtkImageThreadStruct
{
vtkThreadedImageAlgorithm *Filter;
vtkInformation *Request;
vtkInformationVector **InputsInfo;
vtkInformationVector *OutputsInfo;
vtkImageData ***Inputs;
vtkImageData **Outputs;
};
//----------------------------------------------------------------------------
// For streaming and threads. Splits output update extent into num pieces.
// This method needs to be called num times. Results must not overlap for
// consistent starting extent. Subclass can override this method.
// This method returns the number of peices resulting from a successful split.
// This can be from 1 to "total".
// If 1 is returned, the extent cannot be split.
int vtkThreadedImageAlgorithm::SplitExtent(int splitExt[6],
int startExt[6],
int num, int total)
{
// split path (the order in which to split the axes)
int pathlen = this->SplitPathLength;
int mode = this->SplitMode;
int axis0 = this->SplitPath[0];
int axis1 = this->SplitPath[1];
int axis2 = this->SplitPath[2];
int path[3] = { axis0, axis1, axis2 };
// divisions
int divs[3] = { 1, 1, 1 };
// this needs 64 bits to avoid overflow in the math below
const vtkTypeInt64 size[3] = {
startExt[1] - startExt[0] + 1,
startExt[3] - startExt[2] + 1,
startExt[5] - startExt[4] + 1 };
// check for valid extent
if (size[0] <= 0 || size[1] <= 0 || size[2] <= 0)
{
return 0;
}
// divide out the minimum block size
int maxdivs[3] = { 1, 1, 1 };
for (int i = 0; i < 3; i++)
{
if (size[i] > this->MinimumPieceSize[i] && this->MinimumPieceSize[i] > 0)
{
maxdivs[i] = size[i]/this->MinimumPieceSize[i];
}
}
// make sure total is not greater than max number of pieces
vtkTypeInt64 maxPieces = maxdivs[axis0];
vtkTypeInt64 maxPieces2D = maxPieces;
if (pathlen > 1)
{
maxPieces *= maxdivs[axis1];
maxPieces2D = maxPieces;
if (pathlen > 2)
{
maxPieces *= maxdivs[axis2];
}
}
if (total > maxPieces)
{
total = maxPieces;
}
if (mode == SLAB || pathlen < 2)
{
// split the axes in the given order
divs[axis0] = maxdivs[axis0];
if (total < maxdivs[axis0])
{
divs[axis0] = total;
}
else if (pathlen > 1)
{
divs[axis1] = maxdivs[axis1];
int q = total/divs[axis0];
if (q < maxdivs[axis1])
{
divs[axis1] = q;
}
else if (pathlen > 2)
{
divs[axis2] = q/divs[axis1];
}
}
}
else if (mode == BEAM || pathlen < 3)
{
// split two of the axes first, leave third axis for last
if (total < maxPieces2D)
{
// split until we get the desired number of pieces
while (divs[axis0]*divs[axis1] < total)
{
axis0 = path[0];
axis1 = path[1];
// if necessary, swap axes to keep a good aspect ratio
if (size[axis0]*divs[axis1] < size[axis1]*divs[axis0])
{
axis0 = path[1];
axis1 = path[0];
}
// compute the new split for this axis
divs[axis0] = divs[axis1]*size[axis0]/size[axis1] + 1;
}
// compute final division
divs[axis0] = total/divs[axis1];
if (divs[axis0] > maxdivs[axis0])
{
divs[axis0] = maxdivs[axis0];
}
divs[axis1] = total/divs[axis0];
if (divs[axis1] > maxdivs[axis1])
{
divs[axis1] = maxdivs[axis1];
divs[axis0] = total/divs[axis1];
}
}
else
{
// maximum split for first two axes
divs[axis0] = maxdivs[axis0];
divs[axis1] = maxdivs[axis1];
if (pathlen > 2)
{
// split the third axis
divs[axis2] = total/(divs[axis0]*divs[axis1]);
}
}
}
else // block mode: keep blocks roughly cube shaped
{
// split until we get the desired number of pieces
while (divs[0]*divs[1]*divs[2] < total)
{
axis0 = path[0];
axis1 = path[1];
axis2 = path[2];
// check whether z or y is best candidate for splitting
if (size[axis0]*divs[axis1] < size[axis1]*divs[axis0])
{
axis1 = axis0;
axis0 = path[1];
}
if (pathlen > 2)
{
// check if x is the best candidate for splitting
if (size[axis0]*divs[path[2]] < size[path[2]]*divs[axis0])
{
axis2 = axis1;
axis1 = axis0;
axis0 = path[2];
}
// now find the second best candidate
if (size[axis1]*divs[axis2] < size[axis2]*divs[axis1])
{
int tmp = axis2;
axis2 = axis1;
axis1 = tmp;
}
}
// compute the new split for this axis
divs[axis0] = divs[axis1]*size[axis0]/size[axis1] + 1;
// if axis0 reached maxdivs, remove it from the split path
if (divs[axis0] >= maxdivs[axis0])
{
divs[axis0] = maxdivs[axis0];
if (--pathlen == 1)
{
break;
}
if (axis0 != path[2])
{
if (axis0 != path[1])
{
path[0] = path[1];
}
path[1] = path[2];
path[2] = axis0;
}
}
}
// compute the final division
divs[axis0] = total/(divs[axis1]*divs[axis2]);
if (divs[axis0] > maxdivs[axis0])
{
divs[axis0] = maxdivs[axis0];
}
divs[axis1] = total/(divs[axis0]*divs[axis2]);
if (divs[axis1] > maxdivs[axis1])
{
divs[axis1] = maxdivs[axis1];
}
divs[axis2] = total/(divs[axis0]*divs[axis1]);
if (divs[axis2] > maxdivs[axis2])
{
divs[axis2] = maxdivs[axis2];
}
}
// compute new total from the chosen divisions
total = divs[0]*divs[1]*divs[2];
if (splitExt)
{
// compute increments
int a = divs[0];
int b = a*divs[1];
// compute 3D block index
int i = num;
int index[3];
index[2] = i/b;
i -= index[2]*b;
index[1] = i/a;
i -= index[1]*a;
index[0] = i;
// compute the extent for the resulting block
for (int j = 0; j < 3; j++)
{
splitExt[2*j] = index[j]*size[j]/divs[j];
splitExt[2*j + 1] = (index[j] + 1)*size[j]/divs[j] - 1;
splitExt[2*j] += startExt[2*j];
splitExt[2*j + 1] += startExt[2*j];
}
}
// return the number of blocks (may be fewer than requested)
return total;
}
//----------------------------------------------------------------------------
// The old way to thread an image filter, before vtkSMPTools existed:
// this mess is really a simple function. All it does is call
// the ThreadedExecute method after setting the correct
// extent for this thread. Its just a pain to calculate
// the correct extent.
static VTK_THREAD_RETURN_TYPE vtkThreadedImageAlgorithmThreadedExecute( void *arg )
{
vtkImageThreadStruct *str;
int ext[6], splitExt[6], total;
int threadId, threadCount;
threadId = static_cast<vtkMultiThreader::ThreadInfo *>(arg)->ThreadID;
threadCount = static_cast<vtkMultiThreader::ThreadInfo *>(arg)->NumberOfThreads;
str = static_cast<vtkImageThreadStruct *>
(static_cast<vtkMultiThreader::ThreadInfo *>(arg)->UserData);
// if we have an output
if (str->Filter->GetNumberOfOutputPorts())
{
// which output port did the request come from
int outputPort =
str->Request->Get(vtkDemandDrivenPipeline::FROM_OUTPUT_PORT());
// if output port is negative then that means this filter is calling the
// update directly, for now an error
if (outputPort == -1)
{
return VTK_THREAD_RETURN_VALUE;
}
// get the update extent from the output port
vtkInformation *outInfo =
str->OutputsInfo->GetInformationObject(outputPort);
int updateExtent[6];
outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(),
updateExtent);
memcpy(ext,updateExtent, sizeof(int)*6);
}
else
{
// if there is no output, then use UE from input, use the first input
int inPort;
bool found = false;
for (inPort = 0; inPort < str->Filter->GetNumberOfInputPorts(); ++inPort)
{
if (str->Filter->GetNumberOfInputConnections(inPort))
{
int updateExtent[6];
str->InputsInfo[inPort]
->GetInformationObject(0)
->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(),
updateExtent);
memcpy(ext,updateExtent, sizeof(int)*6);
found = true;
break;
}
}
if (!found)
{
return VTK_THREAD_RETURN_VALUE;
}
}
// execute the actual method with appropriate extent
// first find out how many pieces extent can be split into.
total = str->Filter->SplitExtent(splitExt, ext, threadId, threadCount);
if (threadId < total)
{
// return if nothing to do
if (splitExt[1] < splitExt[0] ||
splitExt[3] < splitExt[2] ||
splitExt[5] < splitExt[4])
{
return VTK_THREAD_RETURN_VALUE;
}
str->Filter->ThreadedRequestData(str->Request,
str->InputsInfo, str->OutputsInfo,
str->Inputs, str->Outputs,
splitExt, threadId);
}
// else
// {
// otherwise don't use this thread. Sometimes the threads dont
// break up very well and it is just as efficient to leave a
// few threads idle.
// }
return VTK_THREAD_RETURN_VALUE;
}
//----------------------------------------------------------------------------
// This functor is used with vtkSMPTools to execute the algorithm in pieces
// split over the extent of the data.
class vtkThreadedImageAlgorithmFunctor
{
public:
// Create the functor by providing all of the information that will be
// needed by the ThreadedRequestData method that the functor will call.
vtkThreadedImageAlgorithmFunctor(
vtkThreadedImageAlgorithm *algo,
vtkInformation *request,
vtkInformationVector **inputsInfo,
vtkInformationVector *outputsInfo,
vtkImageData ***inputs,
vtkImageData **outputs,
const int extent[6],
vtkIdType pieces)
: Algorithm(algo), Request(request),
InputsInfo(inputsInfo), OutputsInfo(outputsInfo),
Inputs(inputs), Outputs(outputs),
NumberOfPieces(pieces)
{
for (int i = 0; i < 6; i++)
{
this->Extent[i] = extent[i];
}
}
// Called by vtkSMPTools to execute the algorithm over specific pieces.
void operator()(vtkIdType begin, vtkIdType end)
{
this->Algorithm->SMPRequestData(
this->Request, this->InputsInfo, this->OutputsInfo,
this->Inputs, this->Outputs,
begin, end, this->NumberOfPieces, this->Extent);
}
private:
vtkThreadedImageAlgorithmFunctor();
vtkThreadedImageAlgorithm *Algorithm;
vtkInformation *Request;
vtkInformationVector **InputsInfo;
vtkInformationVector *OutputsInfo;
vtkImageData ***Inputs;
vtkImageData **Outputs;
int Extent[6];
vtkIdType NumberOfPieces;
};
//----------------------------------------------------------------------------
// The execute method created by the subclass.
void vtkThreadedImageAlgorithm::SMPRequestData(
vtkInformation* request,
vtkInformationVector** inputVector,
vtkInformationVector* outputVector,
vtkImageData ***inData,
vtkImageData **outData,
vtkIdType begin,
vtkIdType end,
vtkIdType numPieces,
int extent[6])
{
for (vtkIdType piece = begin; piece < end; piece++)
{
int splitExt[6] = { 0, -1, 0, -1, 0, -1 };
vtkIdType total = this->SplitExtent(splitExt, extent, piece, numPieces);
// check for valid piece and extent
if (piece < total &&
splitExt[0] <= splitExt[1] &&
splitExt[2] <= splitExt[3] &&
splitExt[4] <= splitExt[5])
{
this->ThreadedRequestData(
request, inputVector, outputVector, inData, outData, splitExt, piece);
}
}
}
//----------------------------------------------------------------------------
void vtkThreadedImageAlgorithm::PrepareImageData(
vtkInformationVector** inputVector,
vtkInformationVector* outputVector,
vtkImageData*** inDataObjects,
vtkImageData** outDataObjects)
{
vtkImageData* firstInput = 0;
vtkImageData* firstOutput = 0;
// now we must create the output array
int numOutputPorts = this->GetNumberOfOutputPorts();
for (int i = 0; i < numOutputPorts; i++)
{
vtkInformation* info = outputVector->GetInformationObject(i);
vtkImageData *outData = vtkImageData::SafeDownCast(
info->Get(vtkDataObject::DATA_OBJECT()));
if (i == 0)
{
firstOutput = outData;
}
if (outDataObjects)
{
outDataObjects[i] = outData;
}
if (outData)
{
int updateExtent[6];
info->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(),
updateExtent);
// unlike geometry filters, for image filters data is pre-allocated
// in the superclass (which means, in this class)
this->AllocateOutputData(outData, info, updateExtent);
}
}
// now create the inputs array
int numInputPorts = this->GetNumberOfInputPorts();
for (int i = 0; i < numInputPorts; i++)
{
vtkInformationVector* portInfo = inputVector[i];
int numConnections = portInfo->GetNumberOfInformationObjects();
for (int j = 0; j < numConnections; j++)
{
vtkInformation* info = portInfo->GetInformationObject(j);
vtkImageData *inData = vtkImageData::SafeDownCast(
info->Get(vtkDataObject::DATA_OBJECT()));
if (i == 0 && j == 0)
{
firstInput = inData;
}
if (inDataObjects && inDataObjects[i])
{
inDataObjects[i][j] = inData;
}
}
}
// copy other arrays
if (firstInput && firstOutput)
{
this->CopyAttributeData(firstInput, firstOutput, inputVector);
}
}
//----------------------------------------------------------------------------
// This is the superclasses style of Execute method. Convert it into
// an imaging style Execute method.
int vtkThreadedImageAlgorithm::RequestData(
vtkInformation* request,
vtkInformationVector** inputVector,
vtkInformationVector* outputVector)
{
// setup the thread structure
vtkImageThreadStruct str;
str.Filter = this;
str.Request = request;
str.InputsInfo = inputVector;
str.OutputsInfo = outputVector;
str.Inputs = 0;
str.Outputs = 0;
// create an array for input data objects
int numInputPorts = this->GetNumberOfInputPorts();
if (numInputPorts)
{
str.Inputs = new vtkImageData ** [numInputPorts];
for (int i = 0; i < numInputPorts; i++)
{
int numConnections = inputVector[i]->GetNumberOfInformationObjects();
str.Inputs[i] = new vtkImageData * [numConnections];
}
}
// create an array for output data objects
int numOutputPorts = this->GetNumberOfOutputPorts();
if (numOutputPorts)
{
str.Outputs = new vtkImageData * [numOutputPorts];
}
// allocate the output data and call CopyAttributeData
this->PrepareImageData(inputVector, outputVector, str.Inputs, str.Outputs);
if (this->EnableSMP)
{
// SMP is enabled, use vtkSMPTools to thread the filter
int updateExtent[6] = { 0, -1, 0, -1, 0, -1 };
// need bytes per voxel to compute block size
int bytesPerVoxel = 1;
// get the update extent from the output, if there is an output
if (numOutputPorts)
{
vtkImageData *outData = str.Outputs[0];
if (outData)
{
bytesPerVoxel = (outData->GetScalarSize() *
outData->GetNumberOfScalarComponents());
outData->GetExtent(updateExtent);
}
}
else
{
// if no output, get update extent from the first input
for (int inPort = 0; inPort < numInputPorts; inPort++)
{
if (this->GetNumberOfInputConnections(inPort))
{
vtkImageData *inData = str.Inputs[inPort][0];
if (inData)
{
bytesPerVoxel = (inData->GetScalarSize() *
inData->GetNumberOfScalarComponents());
inData->GetExtent(updateExtent);
break;
}
}
}
}
// verify that there is an extent for execution
if (updateExtent[0] <= updateExtent[1] &&
updateExtent[2] <= updateExtent[3] &&
updateExtent[4] <= updateExtent[5])
{
// compute a reasonable number of pieces, this will be a multiple of
// the number of available threads and relative to the data size
vtkTypeInt64 bytesize = (
static_cast<vtkTypeInt64>(updateExtent[1] - updateExtent[0] + 1)*
static_cast<vtkTypeInt64>(updateExtent[3] - updateExtent[2] + 1)*
static_cast<vtkTypeInt64>(updateExtent[5] - updateExtent[4] + 1)*
bytesPerVoxel);
vtkTypeInt64 bytesPerPiece = this->DesiredBytesPerPiece;
vtkIdType pieces = vtkSMPTools::GetEstimatedNumberOfThreads();
if (bytesPerPiece > 0 && bytesPerPiece < bytesize)
{
vtkTypeInt64 b = pieces*bytesPerPiece;
pieces *= (bytesize + b - 1)/b;
}
// do a dummy execution of SplitExtent to compute the number of pieces
int subExtent[6];
pieces = this->SplitExtent(subExtent, updateExtent, 0, pieces);
// always shut off debugging to avoid threading problems with GetMacros
bool debug = this->Debug;
this->Debug = false;
vtkThreadedImageAlgorithmFunctor functor(
this, request, inputVector, outputVector,
str.Inputs, str.Outputs, updateExtent, pieces);
vtkSMPTools::For(0, pieces, functor);
this->Debug = debug;
}
}
else
{
// if SMP is not enabled, use the vtkMultiThreader
this->Threader->SetNumberOfThreads(this->NumberOfThreads);
this->Threader->SetSingleMethod(vtkThreadedImageAlgorithmThreadedExecute, &str);
// always shut off debugging to avoid threading problems with GetMacros
bool debug = this->Debug;
this->Debug = false;
this->Threader->SingleMethodExecute();
this->Debug = debug;
}
// free up the arrays
for (int i = 0; i < numInputPorts; i++)
{
delete [] str.Inputs[i];
}
delete [] str.Inputs;
delete [] str.Outputs;
return 1;
}
//----------------------------------------------------------------------------
// The execute method created by the subclass.
void vtkThreadedImageAlgorithm::ThreadedRequestData(
vtkInformation* vtkNotUsed( request ),
vtkInformationVector** vtkNotUsed( inputVector ),
vtkInformationVector* vtkNotUsed( outputVector ),
vtkImageData ***inData,
vtkImageData **outData,
int extent[6],
int threadId)
{
this->ThreadedExecute(inData[0][0], outData[0], extent, threadId);
}
//----------------------------------------------------------------------------
// The execute method created by the subclass.
void vtkThreadedImageAlgorithm::ThreadedExecute(
vtkImageData * inData,
vtkImageData * outData,
int extent[6],
int threadId)
{
(void)inData;
(void)outData;
(void)extent;
(void)threadId;
vtkErrorMacro("Subclass should override this method!!!");
}