Skip to content

Add proper implementation for vtkOpenGLGPUVolumeRayCastMapper::GetReductionRatio

Alexy Pellegrini requested to merge alexy.pellegrini/vtk:largevolume into master

Add vtkOpenGLGPUVolumeRayCastMapper::GetReductionRatio implementation.
Previous implementation always returned 1, now it does return a smaller value than 1 if the image is bigger than 512x512x512 (or equivalent).
Also changed vtkSmartVolumeMapper to take this change into account.


This change was originally authored by @LucasGandelKitware

As we already discussed for volumes larger than 512³ the volume rendering is getting instable. It is highly dependent on used GPU and available memory. If we look into the vtkSmartVolumeMapper.cxx, we will find following code segment:

      // Now we need to find out if we need to use a low resolution
      // version of the mapper for interactive rendering. This is true
      // if the GPU mapper cannot hand the size of the volume.
      this->GPUMapper->GetReductionRatio(scale);

      // if any of the scale factors is not 1.0, then we do need
      // to use the low res mapper for interactive rendering
      if (scale[0] != 1.0 || scale[1] != 1.0 || scale[2] != 1.0)
      {
        this->LowResGPUNecessary = 1;
        this->ConnectFilterInput(this->GPUResampleFilter);
        this->GPUResampleFilter->SetInterpolationMode(this->InterpolationMode);
        this->GPUResampleFilter->SetAxisMagnificationFactor(0, scale[0] / 2.0);
        this->GPUResampleFilter->SetAxisMagnificationFactor(1, scale[1] / 2.0);
        this->GPUResampleFilter->SetAxisMagnificationFactor(2, scale[2] / 2.0);

So, the interactive volume rendering is controlled by computation of the reduction ratio, but the issue is that GetReductionRatio() has following implementation in vtkOpenGLGPUVolumeRayCastMapper.h: void GetReductionRatio(double* ratio) override { ratio[0] = ratio[1] = ratio[2] = 1.0; } It means that the reduction or low res will never happen! Following is the trivial implementation, how we currently use it, to handle this case:

void vtkOpenGLGPUVolumeRayCastMapper::GetReductionRatio(double* ratio)
{
  ratio[0] = ratio[1] = ratio[2] = 1.0;  // default
  vtkImageData* input = this->GetInput();
  if (!input)
  {
    return;
  }
  int* dims = input->GetDimensions();
  // We apply the ratio change only for volumes > 512^3.
  constexpr size_t maxSize = static_cast<size_t>(512) * 512 * 512;
  const size_t currentSize = static_cast<size_t>(dims[0]) * dims[1] * dims[2];

  if (currentSize > maxSize)
  {
    // For the ratio computation we are looking of the longest volume side.
    int maxDim = dims[0];
    for (int i = 1; i < 3; ++i)
    {
      if (maxDim < dims[i])
      {
        maxDim = dims[i];
      }
    }

    ratio[0] = ratio[1] = ratio[2] = static_cast<double>(512) / maxDim;
  }
}

In this case we also need to adjust following code in vtkSmartVolumeMapper.cxx, because we can use scale directly:

    this->GPUResampleFilter->SetAxisMagnificationFactor(0, scale[0] / 2.0);
    this->GPUResampleFilter->SetAxisMagnificationFactor(1, scale[1] / 2.0);
    this->GPUResampleFilter->SetAxisMagnificationFactor(2, scale[2] / 2.0);

to:

    this->GPUResampleFilter->SetAxisMagnificationFactor(0, scale[0]);
    this->GPUResampleFilter->SetAxisMagnificationFactor(1, scale[1]);
    this->GPUResampleFilter->SetAxisMagnificationFactor(2, scale[2]);

As you can see, this is very trivial implementation of the reduction ratio computation for the fixed 512³ threshold. We think this can be enhanced by querying the available GPU memory via DirectX or similar and adjusting the reduction rate accordantly to currently available GPU memory. This will be probably difficult to implement portable, that’s why we would like to propose to make it configurable via API. This way the client code can decide or configure the reduction rate as required.


Merge request reports