Class BvvUtils

java.lang.Object
sc.fiji.snt.viewer.BvvUtils

public final class BvvUtils extends Object
Package-private utility methods shared across BVV-related classes (Bvv, ChannelUnmixingCard, etc.).
  • Field Details

    • MAX_CACHE_SIZE_MB

      public static final int MAX_CACHE_SIZE_MB
      Hard ceiling (MB) for the GPU tile cache
      See Also:
    • CACHE_MB_PER_CHANNEL

      public static final int CACHE_MB_PER_CHANNEL
      Rough GPU tile budget (MB) a streamed pyramid needs per channel for a full-screen view without thrashing
      See Also:
    • MAX_SINGLE_TEXTURE_VOXELS

      public static final long MAX_SINGLE_TEXTURE_VOXELS
      Largest per-channel voxel count BVV's single-texture path can handle: its buffer is sized as voxels * 2 with a 32-bit signed int. Independent of GL_MAX_3D_TEXTURE_SIZE
      See Also:
    • GPU_BLOCK_SIZE

      public static final int GPU_BLOCK_SIZE
      Edge length (voxels) of the tiles BVV uploads to the GPU cache (padded by +2 on each axis internally). 64 gave fewer, larger uploads and less padding overhead (~10% vs ~20% for 32). Override with -Dsnt.bvv.gpuBlock=32|64|128
    • CELL_SIZE

      public static final int CELL_SIZE
      Edge length (voxels) of the cells BVV streams from disk. Also the chunk size of exported OME-Zarr arrays, so that loading a cell reads exactly one chunk. Must be a multiple of GPU_BLOCK_SIZE.
      See Also:
  • Method Details

    • getRenderQuality

      public static BvvUtils.RenderQuality getRenderQuality()
      Returns:
      the persisted render quality preset (applies the next time a BVV window is opened)
    • setRenderQuality

      public static void setRenderQuality(BvvUtils.RenderQuality quality)
    • getCachePrefMB

      public static int getCachePrefMB()
      Returns:
      the user's GPU tile cache preference (MB). 0 means automatic
    • setCachePrefMB

      public static void setCachePrefMB(int mb)
      Persists the GPU tile cache preference (MB). 0 means automatic. Applies next time a BVV window is opened
    • setSessionCacheMB

      public static void setSessionCacheMB(int mb)
      Overrides the preference for the current session only (0 clears the override)
    • isCachePromptSuppressed

      public static boolean isCachePromptSuppressed()
    • setCachePromptSuppressed

      public static void setCachePromptSuppressed(boolean suppress)
    • maxCacheMB

      public static int maxCacheMB()
      Returns:
      the largest GPU tile cache (MB) we allow: MAX_CACHE_SIZE_MB or 25% of the JVM max heap
    • recommendedCacheMB

      public static int recommendedCacheMB(int nChannels)
      Returns:
      the automatic GPU tile cache size (MB) for a streamed pyramid with nChannels channels
    • effectiveCacheMB

      public static int effectiveCacheMB(int nChannels, boolean streaming)
      Parameters:
      nChannels - number of channels to be displayed
      streaming - whether the data is a streamed pyramid (N5/Zarr/IMS). Plain images ignore the cache
      Returns:
      the GPU tile cache size (MB) to use: session override, else user preference, else automatic
    • getFetchersPref

      public static int getFetchersPref()
      Returns:
      the user's tile fetcher thread preference. 0 means automatic
    • setFetchersPref

      public static void setFetchersPref(int n)
      Persists the tile fetcher thread preference. 0 means automatic. Applies when a dataset is next opened
    • fetcherThreads

      public static int fetcherThreads(boolean remote)
      Parameters:
      remote - whether the data is read over the network
      Returns:
      the number of tile fetcher threads: the snt.bvv.fetchers system property, else the user preference, else 64 for remote data or one less than the number of CPU cores
    • synthesizeMipmapPyramid

      public static <T extends net.imglib2.type.numeric.NumericType<T> & net.imglib2.type.NativeType<T>> bdv.viewer.Source<T> synthesizeMipmapPyramid(bdv.viewer.Source<T> source, int nLevels, boolean copyLocally)
      Wraps a single-resolution Source in a synthetic mipmap pyramid. BVV's VolumeRenderer requires multiple resolution levels to pick a LOD; without one it throws on every repaint. Use this for non-pyramidal N5/Zarr sources that cannot be re-exported with a real pyramid.

      Level 0 is either a local, cell-based copy of the source (see ImgUtils.materializeTiled(net.imglib2.RandomAccessibleInterval<T>, int)), or, if copyLocally is false, the source itself (as a zero-min view: a non-zero min of the source is moved into the returned source's transform, so its position in world space is unchanged). Each extra level doubles the previous step size along X/Y/Z, matching how a real N5/Zarr multiscale pyramid is laid out. Extra levels are averaged (2x2x2) from the previous level (see averagedLevel(net.imglib2.RandomAccessibleInterval<T>)) and computed lazily, on demand, so they cost no heap up front

      Type Parameters:
      T - pixel type
      Parameters:
      source - the single-level source to wrap
      nLevels - number of extra downsampled levels to synthesize
      copyLocally - whether to copy level 0 into memory. Worth it for small remote sources (avoids repeated network reads), but needs the full volume in heap (see estimateLocalCopyBytes(bdv.viewer.Source<?>))
      Returns:
      a multi-resolution Source wrapping source, or source unchanged if it already has more than one level
    • estimateLocalCopyBytes

      public static long estimateLocalCopyBytes(bdv.viewer.Source<?> source)
      Estimates the heap needed to copy level 0 of source into memory (see synthesizeMipmapPyramid(bdv.viewer.Source<T>, int, boolean))
      Parameters:
      source - the source to estimate
      Returns:
      the estimated size in bytes
    • reportChunking

      public static void reportChunking(String name, int[] chunkShape)
      Logs how the cells streamed to BVV relate to the source chunks and, if the chunk layout is poorly suited to interactive 3D viewing, queues a notice for the notification center. Does nothing if the chunk shape is unknown
      Parameters:
      name - the display name of the data
      chunkShape - the chunk shape of the full-resolution array in N5 axis order (x, y, z, ...), or null
    • estimateTiledPyramidBytes

      public static long estimateTiledPyramidBytes(net.imagej.ImgPlus<?> img)
      Estimates the heap needed by buildTiledPyramid(net.imagej.ImgPlus<T>, int): the full XYZ volume of every channel, plus ~15% for the coarser levels. Doubled if img is not lazily loaded, since it stays in memory during the copy
      Parameters:
      img - an image with X, Y, Z leading, and optionally a channel axis
      Returns:
      the estimated size in bytes
    • buildTiledPyramid

      public static <T extends net.imglib2.type.numeric.RealType<T> & net.imglib2.type.NativeType<T>> SpimDataUtils.N5Sources buildTiledPyramid(net.imagej.ImgPlus<T> img, int nLevels)
      Builds one pyramid-backed Source per channel of an in-memory image, such that BVV renders it with its tile-streaming path (MultiResolutionStack3D) instead of uploading the whole volume as a single 3D texture, which is capped by GL_MAX_3D_TEXTURE_SIZE. Level 0 and every coarser level is copied into its own CellImg, because BVV only streams sources backed by cell images. Level 0 is copied in memory (see ImgUtils.materializeTiled(net.imglib2.RandomAccessibleInterval<T>, int)), which costs the full volume in heap (see estimateTiledPyramidBytes(net.imagej.ImgPlus<?>)). Coarser levels (step 2, 4, 8...) are averaged from the previous level (see averagedLevel(net.imglib2.RandomAccessibleInterval<T>)) and computed lazily, on demand

      Meant for view-only sessions: the returned sources are independent copies, so an image also held for tracing would be stored twice

      Type Parameters:
      T - pixel type
      Parameters:
      img - image with X, Y, Z leading, an optional channel axis, and no time axis (see ImgUtils.normalizeToXYZ(net.imagej.ImgPlus<T>))
      nLevels - number of extra coarser levels
      Returns:
      one source per channel, ready for Bvv.show(SpimDataUtils.N5Sources)
      Throws:
      IllegalArgumentException - if the axes layout is not XYZ[C]
    • prefetchForShow

      public static <T> void prefetchForShow(bdv.viewer.Source<T> source, int timepoint)
      Fetches and locally caches whichever mipmap level BVV will actually render first for source, by touching every pixel on the calling thread. Call preferMultiResolutionIfSafe(bdv.viewer.Source<?>, int) first so the stack type is already decided when this runs

      SimpleStack3D always uploads level 0 (full resolution) as a single texture on first paint (see bvv.core.render.DefaultSimpleStackManager) so that upload is what must be warmed for it. MultiResolutionStack3D streams blocks progressively and never blocks the EDT regardless of what is cached, so warming its coarsest level here is only a courtesy (a faster first frame).

      Call this on a background thread before bvv.show(...) so the EDT only ever sees already-cached data

      Type Parameters:
      T - pixel type
      Parameters:
      source - the source to warm up
      timepoint - the timepoint to warm up
    • prefetchForShow

      public static <T> void prefetchForShow(bdv.viewer.Source<T> source, int timepoint, DoubleConsumer progress)
      As prefetchForShow(Source, int), reporting progress as the data is fetched
      Type Parameters:
      T - pixel type
      Parameters:
      source - the source to warm up
      timepoint - the timepoint to warm up
      progress - receives the fraction (0-1) of the data fetched so far. It is called from several threads and can be null, in which case the data is fetched without any reporting
    • ensureCellBacked

      public static bdv.viewer.SourceAndConverter<?> ensureCellBacked(bdv.viewer.SourceAndConverter<?> soc)
      Makes sure BVV can stream soc tile by tile. BVV only does so if level 0 of the source is backed by an AbstractCellImg (see preferMultiResolutionIfSafe(bdv.viewer.Source<?>, int)). Otherwise it uploads the entire full-resolution volume as a single texture, which fails outright for volumes beyond the texture limits. A common offender is a multichannel OME-Zarr: each channel is a hyperSlice (a plain view) of one 4D array. In that case every level is re-exposed as a lazily-filled AbstractCellImg that copies tiles from the original on demand, so the memory footprint stays bounded.
      Parameters:
      soc - the source about to be shown in BVV
      Returns:
      soc itself if no wrapping is needed (or possible), or an equivalent, cell-backed one
    • ensureCellBacked

      public static bdv.viewer.SourceAndConverter<?> ensureCellBacked(bdv.viewer.SourceAndConverter<?> soc, int[] chunkShape)
    • ensureCellBacked

      public static bdv.viewer.SourceAndConverter<?> ensureCellBacked(bdv.viewer.SourceAndConverter<?> soc, int[] chunkShape, boolean remote)
      As ensureCellBacked(SourceAndConverter), sizing the cells after the chunks of the underlying data
      Parameters:
      soc - the source about to be shown in BVV
      chunkShape - the chunk shape of the data in N5 axis order (x, y, z, ...), or null if unknown, in which case cells have CELL_SIZE voxels per side
      remote - whether the data is read over the network (sizes the tile fetcher pool)
      Returns:
      soc itself if no wrapping is needed (or possible), or an equivalent, cell-backed one
    • clearCaches

      public static long clearCaches()
      Releases the in-memory cell caches of all streamed (N5/Zarr/IMS) sources shown in BVV. Open viewers keep working: tiles are simply reloaded on demand. The GPU tile cache is not affected (it is freed when its window closes)
      Returns:
      the number of cells that were resident (loaded) and have now been released
    • releaseSources

      public static void releaseSources(Collection<? extends bdv.viewer.SourceAndConverter<?>> sources)
      Releases the in-memory cell caches of the given sources (those not backed by BvvUtils.CellBackedSource are ignored) and forgets them. Meant for when the viewer showing them is disposed. The pinned-heap budget they hold is returned right away. That of levels not owned by a source (synthesized or tiled pyramids) is returned once they are garbage-collected
      Parameters:
      sources - the sources to release
    • trackQueue

      public static void trackQueue(bdv.cache.CacheControl queue)
      Makes syncQueuesWithViewer(bvv.vistools.BvvHandle) aware of a fetch queue that feeds BVV
    • syncQueuesWithViewer

      public static void syncQueuesWithViewer(bvv.vistools.BvvHandle handle)
      Registers all tracked fetch queues with the cache controls of handle, so that BVV advances them on every new frame (see CacheControl.prepareNextFrame()). Idempotent. Opt out with -Dsnt.bvv.noQueueSync=true
    • preferMultiResolutionIfSafe

      public static void preferMultiResolutionIfSafe(bdv.viewer.Source<?> source, int timepoint)
      Forces BVV to render source with its pyramid-aware, block-streaming path (bvv.core.multires.MultiResolutionStack3D) instead of the naive single-texture path (bvv.core.multires.SimpleStack3D), when it is safe to do so.

      BVV auto-detects which path to use (bvv.core.multires.SourceStacks#inferSourceStackType): it only picks the multi-resolution path when source's pixel type is TileAccess-supported AND source.getSource(timepoint, 0) is (or wraps, via VolatileView) an AbstractCellImg. Many BDV/N5 source builders wrap their levels in a plain Views-based interval (not an AbstractCellImg), which makes BVV fall back to SimpleStack3D even for a genuinely multi-resolution, remote source. SimpleStack3D uploads the entire full-resolution volume as one texture on first paint, fetching all of it synchronously

      This mirrors BVV's own inferSourceStackType check before overriding it, so it never forces multi-resolution rendering on a source that would actually fail it (which would throw UnsupportedOperationException from TileAccess.create on the render thread). If the check fails, this method does nothing and BVV falls back to its own (slower) default

      Parameters:
      source - the source about to be shown in BVV
      timepoint - the timepoint to inspect
    • warnIfLikelySimpleStack

      public static void warnIfLikelySimpleStack(bdv.viewer.Source<?> source, int timepoint)
      Read-only counterpart to preferMultiResolutionIfSafe(bdv.viewer.Source<?>, int) for AbstractSpimData sources (BDV-XML/HDF5, IMS): logs a warning if source looks likely to fall back to BVV's non-pyramid-aware SimpleStack3D renderer, without attempting to prevent it.

      Unlike the SpimDataUtils.N5Sources path, BvvFunctions.show(AbstractSpimData, BvvOptions) builds its own Source instances internally (via BigDataViewer#initSetups), so there is no hook to call preferMultiResolutionIfSafe(bdv.viewer.Source<?>, int) on the actual instance before it first renders. inferSourceStackType's check is a pure function of the source's structural properties (pixel type, whether level 0 is an AbstractCellImg), not of instance identity or any per-instance cached state, so running the same check here - on the Source SNT already has a handle to after show() returns - still gives an accurate answer; it just can't change the outcome

      This is diagnostic only: it neither prefetches nor forces a stack type, so it carries none of preferMultiResolutionIfSafe(bdv.viewer.Source<?>, int)/prefetchForShow(bdv.viewer.Source<T>, int)'s risk of misbehaving on a source shape this hasn't been exercised against - it only makes a slow first paint traceable in the log after the fact, for whichever AbstractSpimData backend produced it

      Parameters:
      source - the (already-shown) source to inspect
      timepoint - the timepoint to inspect (0 is fine for this purely structural check)
    • warnIfLikelyRemoteImgPlus

      public static void warnIfLikelyRemoteImgPlus(net.imagej.ImgPlus<?> img, String pathOrUrl)
      Diagnostic-only warning for the plain ImgPlus fallback path (see SpimDataUtils.resolvePathToSource(String)). Unlike preferMultiResolutionIfSafe(bdv.viewer.Source<?>, int)/ warnIfLikelySimpleStack(bdv.viewer.Source<?>, int), an ImgPlus always has a single mipmap level, so BVV always renders it via the non-pyramid-aware SimpleStack3D path regardless of pixel type or backing storage - there is no "is it structurally eligible for MULTIRESOLUTION" question to ask here the way there is for AbstractSpimData/N5Sources.

      resolvePathToSource already knows this at resolution time - a remote ImgPlus is only ever produced by its own URL fallback branch (ImgUtils.open(url)) - so this simply carries that signal forward rather than trying to re-derive it by introspecting the RAI (which, for a lazily-opened remote image, may not even be a recognizable cache type)

      Parameters:
      img - the resolved ImgPlus about to be shown in BVV
      pathOrUrl - the original path or URL img was resolved from