Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Allow bypassing range calculations for Volume Mapper #3110

Open
sedghi opened this issue Aug 2, 2024 · 6 comments
Open

[Feature] Allow bypassing range calculations for Volume Mapper #3110

sedghi opened this issue Aug 2, 2024 · 6 comments
Labels
type: feature request 💡 Desired capabilities / enhancements

Comments

@sedghi
Copy link
Contributor

sedghi commented Aug 2, 2024

Feature Request

For the volume mapper, there is an intrinsic assumption that the full volume is available with the current usage of halfFloat texture is as follows:

  1. It calculates the range of the volume (min, max).
  2. If the data is float and its range allows for exact representation of the intensities via half float (range is between -2048 and 2048), it automatically uses half float.
  3. If the range does not allow and the user has specified preferSizeOverAccuracy, it will override it for the sake of memory and use half float.

Here is the relevant code:

function setUseHalfFloat(dataType, offset, scale, preferSizeOverAccuracy) {
  publicAPI.getOpenGLDataType(dataType);

  let useHalfFloat = false;
  if (model._openGLRenderWindow.getWebgl2()) {
    useHalfFloat = model.openGLDataType === model.context.HALF_FLOAT;
  } else {
    const halfFloatExt = model.context.getExtension('OES_texture_half_float');
    useHalfFloat =
      halfFloatExt && model.openGLDataType === halfFloatExt.HALF_FLOAT_OES;
  }

  // Don't consider halfFloat and convert back to Float when the range of data does not generate an accurate halfFloat
  // AND it is not preferable to have a smaller texture than an exact texture.
  const isHalfFloat =
    useHalfFloat &&
    (hasExactHalfFloat(offset, scale) || preferSizeOverAccuracy);
  model.useHalfFloat = isHalfFloat;
}

The issue lies with hasExactHalfFloat, which requires the full volume data to calculate the range.

There are many situations where the full volume is not available, and it is not desirable to wait until it becomes available to calculate this range. This assumption currently causes issues since it is presumed to be available. At present, vtk.js falls back to halfFloat even if the volume is empty at start (all 0) and the range is 0, which might later be filled with data that is out of range later. Essentially, this is not a feature request but rather a bug fix.

We can introduce a flag that maintains the current default behavior but can be used to bypass all the range-related calculations in streaming situations where data streams in.

Motivation and Detailed Description

The usage of the VolumeMapper is bound to calculating the range on the volume for half-float default fallback when the range allows. This limitation requires the application to always provide the full data for range consideration, which can be slow and sometimes wrong since data might stream inside.

We can introduce a feature flag that allows skipping the range calculation if the application doesn't need it. By default, it would use the current approach, but it can be overridden by a flag like streamingData or similar.

What do you think? @finetjul

@sedghi sedghi added the type: feature request 💡 Desired capabilities / enhancements label Aug 2, 2024
@finetjul
Copy link
Member

finetjul commented Aug 5, 2024

@bruyeret worked with half-float lately. Maybe he has some ideas...

@bruyeret
Copy link
Contributor

bruyeret commented Aug 5, 2024

You can use setRange on the data array to make sure it does not use half float. It is halfway between the feature and the workaround.
If you change the data inside the buffer, you should call dataChange. This will clear the computed ranges, and recompute it when needed (it is literally model.ranges = null and publicAPI.modified();).

@sedghi
Copy link
Contributor Author

sedghi commented Aug 5, 2024

Are you suggesting I create a "fake" setRange to prevent fallback to half float? Perhaps something above 2048? I want to avoid a dataChange since it's not performant after each slice update.

I believe at the end of streaming, I just need to call the dataChange method (which method is this, by the way?). This ensures that the next time I call getRange, it returns the correct result. Am I right?

@bruyeret
Copy link
Contributor

bruyeret commented Aug 6, 2024

The method setRange on vtkDataArray already exists and it is an intended behavior that one is able to specify the range this way.
Calling dataChange on a vtkDataArray simply clears the range (that has been computed or manually set), which causes the data array to recompute the range later when calling getRange.

So something equivalent to having "no range" would be to have an "infinite range", which means that you could try setting the range to [-Infinity, +Infinity] when creating the data array and never call dataChange on it.
I didn't try but I don't see a reason for it not to work.

@sedghi
Copy link
Contributor Author

sedghi commented Aug 6, 2024

@bruyeret Thanks for your comment,
The issue is that I don't want to create a vtkDataArray at all, since that is in CPU, Instead, I'd like to go straight to GPU using create3DFromRaw. I'm going to create a PR to discuss what I mean.

@sedghi
Copy link
Contributor Author

sedghi commented Aug 6, 2024

I have a PR to skip those calculations by directly using create3DFromRaw #3109

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature request 💡 Desired capabilities / enhancements
Projects
None yet
Development

No branches or pull requests

3 participants