diff --git a/examples/image_2d/main.ts b/examples/image_2d/main.ts index 5e53156d..99be5d65 100644 --- a/examples/image_2d/main.ts +++ b/examples/image_2d/main.ts @@ -322,12 +322,26 @@ function formatDatasetInfo( .replace(//g, ">"); + let totalChunks = 0; + for (let lod = 0; lod < dims.numLods; ++lod) { + const xLod = dims.x.lods[lod]; + const yLod = dims.y.lods[lod]; + const zLod = dims.z?.lods[lod]; + const chunksX = Math.ceil(xLod.size / xLod.chunkSize); + const chunksY = Math.ceil(yLod.size / yLod.chunkSize); + const chunksZ = zLod ? Math.ceil(zLod.size / zLod.chunkSize) : 1; + const chunksT = tL?.size ?? 1; + const chunksC = cL?.size ?? 1; + totalChunks += chunksT * chunksC * chunksZ * chunksY * chunksX; + } + return ( `
${escapedLabel}
` + `
` + [ `shape: ${shape}`, `LODs: ${dims.numLods}`, + `chunks: ${totalChunks.toLocaleString()}`, `voxel: ${scale}${unit ? " " + unit : ""}`, ].join("\n") + `
` diff --git a/src/core/chunk_store.ts b/src/core/chunk_store.ts index c3ce5534..94f2556a 100644 --- a/src/core/chunk_store.ts +++ b/src/core/chunk_store.ts @@ -1,17 +1,12 @@ -import { - Chunk, - SourceDimensionMap, - ChunkLoader, - SliceCoordinates, - coordToIndex, -} from "../data/chunk"; +import { Chunk, SourceDimensionMap, ChunkLoader } from "../data/chunk"; import { almostEqual } from "../utilities/almost_equal"; import { Logger } from "../utilities/logger"; import { ChunkStoreView } from "./chunk_store_view"; import { ImageSourcePolicy } from "./image_source_policy"; export class ChunkStore { - private readonly chunks_: Chunk[][]; + // Chunks indexed as chunks_[lod][t][c][z][y][x]. + private readonly chunks_: Chunk[][][][][][]; private readonly loader_: ChunkLoader; private readonly lowestResLOD_: number; private readonly dimensions_: SourceDimensionMap; @@ -27,34 +22,44 @@ export class ChunkStore { const { size: chunksT } = this.getAndValidateTimeDimension(); const { size: chunksC } = this.getAndValidateChannelDimension(); - this.chunks_ = Array.from({ length: chunksT }, () => []); - for (let t = 0; t < chunksT; ++t) { - const chunksAtT = this.chunks_[t]; - for (let lod = 0; lod < this.dimensions_.numLods; ++lod) { - const xLod = this.dimensions_.x.lods[lod]; - const yLod = this.dimensions_.y.lods[lod]; - const zLod = this.dimensions_.z?.lods[lod]; + const numLods = this.dimensions_.numLods; + this.chunks_ = new Array(numLods); - const chunkWidth = xLod.chunkSize; - const chunkHeight = yLod.chunkSize; - const chunkDepth = zLod?.chunkSize ?? 1; + for (let lod = 0; lod < numLods; ++lod) { + const xLod = this.dimensions_.x.lods[lod]; + const yLod = this.dimensions_.y.lods[lod]; + const zLod = this.dimensions_.z?.lods[lod]; - const chunksX = Math.ceil(xLod.size / chunkWidth); - const chunksY = Math.ceil(yLod.size / chunkHeight); - const chunksZ = Math.ceil((zLod?.size ?? 1) / chunkDepth); + const chunkWidth = xLod.chunkSize; + const chunkHeight = yLod.chunkSize; + const chunkDepth = zLod?.chunkSize ?? 1; + const chunksX = Math.ceil(xLod.size / chunkWidth); + const chunksY = Math.ceil(yLod.size / chunkHeight); + const chunksZ = zLod ? Math.ceil(zLod.size / chunkDepth) : 1; + + const lodArr: Chunk[][][][][] = new Array(chunksT); + this.chunks_[lod] = lodArr; + for (let t = 0; t < chunksT; ++t) { + const tArr: Chunk[][][][] = new Array(chunksC); + lodArr[t] = tArr; for (let c = 0; c < chunksC; ++c) { - for (let x = 0; x < chunksX; ++x) { - const xOffset = xLod.translation + x * xLod.chunkSize * xLod.scale; + const cArr: Chunk[][][] = new Array(chunksZ); + tArr[c] = cArr; + for (let z = 0; z < chunksZ; ++z) { + const zOffset = + zLod !== undefined + ? zLod.translation + z * chunkDepth * zLod.scale + : 0; + const yArr: Chunk[][] = new Array(chunksY); + cArr[z] = yArr; for (let y = 0; y < chunksY; ++y) { - const yOffset = - yLod.translation + y * yLod.chunkSize * yLod.scale; - for (let z = 0; z < chunksZ; ++z) { - const zOffset = - zLod !== undefined - ? zLod.translation + z * chunkDepth * zLod.scale - : 0; - chunksAtT.push({ + const yOffset = yLod.translation + y * chunkHeight * yLod.scale; + const xArr: Chunk[] = new Array(chunksX); + yArr[y] = xArr; + for (let x = 0; x < chunksX; ++x) { + const xOffset = xLod.translation + x * chunkWidth * xLod.scale; + xArr[x] = { state: "unloaded", lod, visible: false, @@ -79,7 +84,7 @@ export class ChunkStore { y: yOffset, z: zOffset, }, - }); + }; } } } @@ -88,14 +93,16 @@ export class ChunkStore { } } - public getChunksAtTime(timeIndex: number): Chunk[] { - return this.chunks_[timeIndex]; + public getChunkGrid( + lod: number, + t: number, + c: number + ): Chunk[][][] | undefined { + return this.chunks_[lod]?.[t]?.[c]; } - public getTimeIndex(sliceCoords: SliceCoordinates): number { - if (sliceCoords.t === undefined) return 0; - if (this.dimensions_.t === undefined) return 0; - return coordToIndex(this.dimensions_.t.lods[0], sliceCoords.t); + public hasChunksAtTime(timeIndex: number): boolean { + return this.chunks_[0]?.[timeIndex] !== undefined; } public get lodCount() { diff --git a/src/core/chunk_store_view.ts b/src/core/chunk_store_view.ts index 52c82978..eadcd8ec 100644 --- a/src/core/chunk_store_view.ts +++ b/src/core/chunk_store_view.ts @@ -1,4 +1,9 @@ -import { Chunk, SliceCoordinates, ChunkViewState } from "../data/chunk"; +import { + Chunk, + SliceCoordinates, + ChunkViewState, + coordToIndex, +} from "../data/chunk"; import type { ChunkStore } from "./chunk_store"; import { Viewport } from "./viewport"; import { OrthographicCamera } from "../objects/cameras/orthographic_camera"; @@ -59,15 +64,6 @@ export class ChunkStoreView { return this.isDisposed_; } - // forwarding methods for chunk stats overlay while keeping store_ private - public getChunksAtTime(timeIndex: number): Chunk[] { - return this.store_.getChunksAtTime(timeIndex); - } - - public getTimeIndex(sliceCoords: SliceCoordinates): number { - return this.store_.getTimeIndex(sliceCoords); - } - public get lodCount(): number { return this.store_.lodCount; } @@ -76,28 +72,24 @@ export class ChunkStoreView { return this.store_.channelCount; } - public getChunksToRender(sliceCoords: SliceCoordinates): Chunk[] { - const currentTimeIndex = this.store_.getTimeIndex(sliceCoords); - const currentTimeChunks = this.store_.getChunksAtTime(currentTimeIndex); - const currentLODChunks = currentTimeChunks.filter( - (chunk) => - chunk.lod === this.currentLOD_ && - this.chunkViewStates_.get(chunk)?.visible === true && - chunk.state === "loaded" - ); - + public getChunksToRender(): Chunk[] { + // Iterates `chunkViewStates_` (only chunks touched by the most recent + // updateChunks*ForRegion) instead of every chunk at the time index, so the + // cost is bounded by visible+prefetch+fallback set size, not dataset size. const fallbackLOD = this.fallbackLOD(); - if (this.currentLOD_ === fallbackLOD) { - return currentLODChunks; + const currentLOD = this.currentLOD_; + const currentLODChunks: Chunk[] = []; + const lowResChunks: Chunk[] = []; + + for (const [chunk, state] of this.chunkViewStates_) { + if (!state.visible || chunk.state !== "loaded") continue; + if (chunk.lod === currentLOD) { + currentLODChunks.push(chunk); + } else if (chunk.lod === fallbackLOD && currentLOD !== fallbackLOD) { + lowResChunks.push(chunk); + } } - const lowResChunks = currentTimeChunks.filter( - (chunk) => - chunk.lod === fallbackLOD && - this.chunkViewStates_.get(chunk)?.visible === true && - chunk.state === "loaded" - ); - return [...currentLODChunks, ...lowResChunks]; } @@ -133,10 +125,8 @@ export class ChunkStoreView { if (!changed) return; - const currentTimeIndex = this.store_.getTimeIndex(sliceCoords); - const currentTimeChunks = this.store_.getChunksAtTime(currentTimeIndex); - - if (currentTimeChunks.length === 0) { + const currentTimeIndex = this.timeIndex(sliceCoords); + if (!this.store_.hasChunksAtTime(currentTimeIndex)) { Logger.warn( "ChunkStoreView", "updateChunkViewStates called with no chunks initialized" @@ -158,22 +148,53 @@ export class ChunkStoreView { // logic below will override this for chunks that are actually visible/prefetch this.chunkViewStates_.forEach(resetChunkViewState); - this.updateChunksAtTimeIndex( + const channels = this.channelsOfInterest(sliceCoords); + const fallbackLOD = this.fallbackLOD(); + const prefetchAabb = this.getPaddedBounds(viewBounds3D); + + // Range-query the prefetch AABB at currentLOD (and fallbackLOD when + // distinct); fallback chunks act as a backdrop while currentLOD loads. + const lodsToVisit = + this.currentLOD_ === fallbackLOD + ? [this.currentLOD_] + : [this.currentLOD_, fallbackLOD]; + for (const lod of lodsToVisit) { + const isCurrent = lod === this.currentLOD_; + const isFallback = lod === fallbackLOD; + this.iterateChunksInBox( + currentTimeIndex, + lod, + channels, + prefetchAabb, + (chunk, chunkBox) => { + const isInBounds = Box3.intersects(chunkBox, viewBounds3D); + const prefetch = isCurrent && !isInBounds; + const priority = this.computePriority( + isFallback, + isCurrent, + isInBounds, + prefetch, + true + ); + if (priority !== null) { + this.chunkViewStates_.set(chunk, { + visible: isInBounds, + prefetch, + priority, + orderKey: this.squareDistance2D(chunk, viewBoundsCenter2D), + }); + } + } + ); + } + + this.markTimeChunksForPrefetchImage( currentTimeIndex, sliceCoords, viewBounds3D, viewBoundsCenter2D ); - if (sliceCoords.t !== undefined) { - this.markTimeChunksForPrefetchImage( - currentTimeIndex, - sliceCoords, - viewBounds3D, - viewBoundsCenter2D - ); - } - this.policyChanged_ = false; this.lastViewBounds2D_ = viewBounds2D.clone(); this.lastZBounds_ = zBounds; @@ -202,10 +223,8 @@ export class ChunkStoreView { if (!changed) return; - const currentTimeIndex = this.store_.getTimeIndex(sliceCoords); - const currentTimeChunks = this.store_.getChunksAtTime(currentTimeIndex); - - if (currentTimeChunks.length === 0) { + const currentTimeIndex = this.timeIndex(sliceCoords); + if (!this.store_.hasChunksAtTime(currentTimeIndex)) { Logger.warn( "ChunkStoreView", "updateChunksForVolume called with no chunks initialized" @@ -221,33 +240,40 @@ export class ChunkStoreView { this.chunkViewStates_.forEach(resetChunkViewState); + const channels = this.channelsOfInterest(sliceCoords); const fallbackLOD = this.fallbackLOD(); - - for (const chunk of currentTimeChunks) { - const isCurrentLOD = chunk.lod === this.currentLOD_; - const isFallbackLOD = chunk.lod === fallbackLOD; - if (!isCurrentLOD && !isFallbackLOD) continue; - - if (!this.isChunkChannelInSlice(chunk, sliceCoords)) continue; + const markVolumeChunkVisible = (chunk: Chunk, isFallbackLOD: boolean) => { const priority = this.computePriority( isFallbackLOD, - isCurrentLOD, - true, // isVisible - false, // isPrefetch - true // isChannelInSlice + !isFallbackLOD, + true, + false, + true ); - + if (priority === null) return; this.chunkViewStates_.set(chunk, { visible: true, prefetch: false, priority, - orderKey: 0, // All chunks have the same ordering for volume rendering + orderKey: 0, }); + }; + this.iterateAllChunksAtLod( + currentTimeIndex, + this.currentLOD_, + channels, + (chunk) => markVolumeChunkVisible(chunk, false) + ); + if (this.currentLOD_ !== fallbackLOD) { + this.iterateAllChunksAtLod( + currentTimeIndex, + fallbackLOD, + channels, + (chunk) => markVolumeChunkVisible(chunk, true) + ); } - if (sliceCoords.t !== undefined) { - this.markTimeChunksForPrefetchVolume(currentTimeIndex, sliceCoords); - } + this.markTimeChunksForPrefetchVolume(currentTimeIndex, sliceCoords); this.policyChanged_ = false; this.lastTCoord_ = sliceCoords.t; @@ -255,17 +281,15 @@ export class ChunkStoreView { this.lastViewProjection_ = viewProjection; } - public allVisibleFallbackLODLoaded(sliceCoords: SliceCoordinates): boolean { - const timeIndex = this.store_.getTimeIndex(sliceCoords); + public allVisibleFallbackLODLoaded(): boolean { const fallbackLOD = this.fallbackLOD(); - const visibleChunks = this.store_ - .getChunksAtTime(timeIndex) - .filter((c) => c.visible && c.lod === fallbackLOD); - // Return false if there are no visible chunks (empty array .every() returns true) - return ( - visibleChunks.length > 0 && - visibleChunks.every((c) => c.state === "loaded") - ); + let foundAny = false; + for (const [chunk, state] of this.chunkViewStates_) { + if (!state.visible || chunk.lod !== fallbackLOD) continue; + foundAny = true; + if (chunk.state !== "loaded") return false; + } + return foundAny; } public get currentLOD(): number { @@ -333,64 +357,6 @@ export class ChunkStoreView { } } - private isChunkChannelInSlice( - chunk: Chunk, - sliceCoords: SliceCoordinates - ): boolean { - if (sliceCoords.c === undefined) return true; - if (sliceCoords.c.length === 0) return false; - return sliceCoords.c.includes(chunk.chunkIndex.c); - } - - private updateChunksAtTimeIndex( - timeIndex: number, - sliceCoords: SliceCoordinates, - viewBounds3D: Box3, - viewBounds2DCenter: ReadonlyVec2 - ): void { - const paddedBounds = this.getPaddedBounds(viewBounds3D); - - const currentTimeChunks = this.store_.getChunksAtTime(timeIndex); - const fallbackLOD = this.fallbackLOD(); - - for (const chunk of currentTimeChunks) { - const isCurrentLOD = chunk.lod === this.currentLOD_; - const isFallbackLOD = chunk.lod === fallbackLOD; - if (!isCurrentLOD && !isFallbackLOD) continue; - - const isChannelInSlice = this.isChunkChannelInSlice(chunk, sliceCoords); - if (!isChannelInSlice) continue; - - const isInBounds = this.isChunkWithinBounds(chunk, viewBounds3D); - - const prefetch = - !isInBounds && - isChannelInSlice && - isCurrentLOD && - this.isChunkWithinBounds(chunk, paddedBounds); - - const visible = isInBounds && isChannelInSlice; - const priority = this.computePriority( - isFallbackLOD, - isCurrentLOD, - isInBounds, - prefetch, - isChannelInSlice - ); - - if (priority !== null) { - const orderKey = this.squareDistance2D(chunk, viewBounds2DCenter); - - this.chunkViewStates_.set(chunk, { - visible, - prefetch, - priority, - orderKey, - }); - } - } - } - private markTimeChunksForPrefetchImage( currentTimeIndex: number, sliceCoords: SliceCoordinates, @@ -404,30 +370,34 @@ export class ChunkStoreView { ); const fallbackLOD = this.fallbackLOD(); const priority = this.policy_.priorityMap["prefetchTime"]; + const channels = this.channelsOfInterest(sliceCoords); for (let t = currentTimeIndex + 1; t <= tEnd; ++t) { - for (const chunk of this.store_.getChunksAtTime(t)) { - if (chunk.lod !== fallbackLOD) continue; - if (!this.isChunkChannelInSlice(chunk, sliceCoords)) continue; - if (!this.isChunkWithinBounds(chunk, viewBounds3D)) continue; - - const squareDistance = this.squareDistance2D(chunk, viewBoundsCenter2D); - const normalizedDistance = clamp( - squareDistance / this.sourceMaxSquareDistance2D_, - 0, - 1 - Number.EPSILON - ); - const orderKey = t - currentTimeIndex + normalizedDistance; - - // Always set priority/orderKey to keep loaded chunks alive - // Only unloaded chunks will be queued for loading - this.chunkViewStates_.set(chunk, { - visible: false, - prefetch: true, - priority, - orderKey, - }); - } + this.iterateChunksInBox( + t, + fallbackLOD, + channels, + viewBounds3D, + (chunk) => { + const squareDistance = this.squareDistance2D( + chunk, + viewBoundsCenter2D + ); + const normalizedDistance = clamp( + squareDistance / this.sourceMaxSquareDistance2D_, + 0, + 1 - Number.EPSILON + ); + const orderKey = t - currentTimeIndex + normalizedDistance; + + this.chunkViewStates_.set(chunk, { + visible: false, + prefetch: true, + priority, + orderKey, + }); + } + ); } } @@ -442,21 +412,18 @@ export class ChunkStoreView { ); const fallbackLOD = this.fallbackLOD(); const priority = this.policy_.priorityMap["prefetchTime"]; + const channels = this.channelsOfInterest(sliceCoords); for (let t = currentTimeIndex + 1; t <= tEnd; ++t) { - for (const chunk of this.store_.getChunksAtTime(t)) { - if (chunk.lod !== fallbackLOD) continue; - if (!this.isChunkChannelInSlice(chunk, sliceCoords)) continue; - + this.iterateAllChunksAtLod(t, fallbackLOD, channels, (chunk) => { const orderKey = t - currentTimeIndex; // nearer future timepoints first - this.chunkViewStates_.set(chunk, { visible: false, prefetch: true, priority, orderKey, }); - } + }); } } @@ -478,8 +445,93 @@ export class ChunkStoreView { return null; } - private isChunkWithinBounds(chunk: Chunk, bounds: Box3): boolean { - const chunkBounds = new Box3( + private channelsOfInterest(sliceCoords: SliceCoordinates): number[] { + return ( + sliceCoords.c ?? + Array.from({ length: this.store_.channelCount }, (_, i) => i) + ); + } + + // Half-open chunk-index range [min, max) at a given LOD that covers `bounds`. + // Returns null if the bounds don't overlap the data grid. + private chunkIndexRange(bounds: Box3, lod: number) { + const dim = this.store_.dimensions; + const xLod = dim.x.lods[lod]; + const yLod = dim.y.lods[lod]; + const zLod = dim.z?.lods[lod]; + + const xCount = Math.ceil(xLod.size / xLod.chunkSize); + const yCount = Math.ceil(yLod.size / yLod.chunkSize); + const zCount = zLod ? Math.ceil(zLod.size / zLod.chunkSize) : 1; + + const xStride = xLod.chunkSize * xLod.scale; + const yStride = yLod.chunkSize * yLod.scale; + const zStride = zLod ? zLod.chunkSize * zLod.scale : 1; + const xTran = xLod.translation; + const yTran = yLod.translation; + const zTran = zLod?.translation ?? 0; + + const xMin = Math.max(0, Math.floor((bounds.min[0] - xTran) / xStride)); + const xMax = Math.min(xCount, Math.ceil((bounds.max[0] - xTran) / xStride)); + const yMin = Math.max(0, Math.floor((bounds.min[1] - yTran) / yStride)); + const yMax = Math.min(yCount, Math.ceil((bounds.max[1] - yTran) / yStride)); + const zMin = zLod + ? Math.max(0, Math.floor((bounds.min[2] - zTran) / zStride)) + : 0; + const zMax = zLod + ? Math.min(zCount, Math.ceil((bounds.max[2] - zTran) / zStride)) + : 1; + + if (xMin >= xMax || yMin >= yMax || zMin >= zMax) return null; + return { xMin, xMax, yMin, yMax, zMin, zMax }; + } + + private iterateChunksInBox( + timeIndex: number, + lod: number, + channels: number[], + bounds: Box3, + callback: (chunk: Chunk, chunkBox: Box3) => void + ): void { + const range = this.chunkIndexRange(bounds, lod); + if (!range) return; + for (const c of channels) { + const grid = this.store_.getChunkGrid(lod, timeIndex, c); + if (!grid) continue; + for (let zi = range.zMin; zi < range.zMax; ++zi) { + const yPlane = grid[zi]; + for (let yi = range.yMin; yi < range.yMax; ++yi) { + const xRow = yPlane[yi]; + for (let xi = range.xMin; xi < range.xMax; ++xi) { + const chunk = xRow[xi]; + callback(chunk, this.getChunkAabb(chunk)); + } + } + } + } + } + + private iterateAllChunksAtLod( + timeIndex: number, + lod: number, + channels: number[], + callback: (chunk: Chunk, chunkBox: Box3) => void + ): void { + for (const c of channels) { + const grid = this.store_.getChunkGrid(lod, timeIndex, c); + if (!grid) continue; + for (const yPlane of grid) { + for (const xRow of yPlane) { + for (const chunk of xRow) { + callback(chunk, this.getChunkAabb(chunk)); + } + } + } + } + } + + private getChunkAabb(chunk: Chunk): Box3 { + return new Box3( vec3.fromValues(chunk.offset.x, chunk.offset.y, chunk.offset.z), vec3.fromValues( chunk.offset.x + chunk.shape.x * chunk.scale.x, @@ -487,13 +539,18 @@ export class ChunkStoreView { chunk.offset.z + chunk.shape.z * chunk.scale.z ) ); - return Box3.intersects(chunkBounds, bounds); } private fallbackLOD(): number { return Math.min(this.policy_.lod.max, this.store_.getLowestResLOD()); } + private timeIndex(sliceCoords: SliceCoordinates): number { + const tDim = this.store_.dimensions.t; + if (sliceCoords.t === undefined || tDim === undefined) return 0; + return coordToIndex(tDim.lods[0], sliceCoords.t); + } + private getZBounds(sliceCoords: SliceCoordinates): [number, number] { const zDim = this.store_.dimensions.z; if (zDim === undefined) return [0, 1]; diff --git a/src/layers/image_layer.ts b/src/layers/image_layer.ts index 589ca560..dc3cc45c 100644 --- a/src/layers/image_layer.ts +++ b/src/layers/image_layer.ts @@ -126,7 +126,7 @@ export class ImageLayer extends Layer implements ChannelsEnabled { if ( this.visibleChunks_.size > 0 && - !this.chunkStoreView_.allVisibleFallbackLODLoaded(this.sliceCoords_) && + !this.chunkStoreView_.allVisibleFallbackLODLoaded() && !this.isPresentationStale() ) { return; @@ -134,9 +134,7 @@ export class ImageLayer extends Layer implements ChannelsEnabled { this.lastPresentationTimeStamp_ = performance.now(); this.lastPresentationTimeCoord_ = this.sliceCoords_.t; - const orderedByLOD = this.chunkStoreView_.getChunksToRender( - this.sliceCoords_ - ); + const orderedByLOD = this.chunkStoreView_.getChunksToRender(); const current = new Set(orderedByLOD); const nonVisibleChunks = Array.from(this.visibleChunks_.keys()).filter( (chunk) => !current.has(chunk) diff --git a/src/layers/label_layer.ts b/src/layers/label_layer.ts index 38a100cc..e68e6cc6 100644 --- a/src/layers/label_layer.ts +++ b/src/layers/label_layer.ts @@ -112,7 +112,7 @@ export class LabelLayer extends Layer { if ( this.visibleChunks_.size > 0 && - !this.chunkStoreView_.allVisibleFallbackLODLoaded(this.sliceCoords_) && + !this.chunkStoreView_.allVisibleFallbackLODLoaded() && !this.isPresentationStale() ) { return; @@ -120,9 +120,7 @@ export class LabelLayer extends Layer { this.lastPresentationTimeStamp_ = performance.now(); this.lastPresentationTimeCoord_ = this.sliceCoords_.t; - const orderedByLOD = this.chunkStoreView_.getChunksToRender( - this.sliceCoords_ - ); + const orderedByLOD = this.chunkStoreView_.getChunksToRender(); const current = new Set(orderedByLOD); const nonVisibleChunks = Array.from(this.visibleChunks_.keys()).filter( (chunk) => !current.has(chunk) diff --git a/src/layers/volume_layer.ts b/src/layers/volume_layer.ts index 54140c4a..51ffb5bd 100644 --- a/src/layers/volume_layer.ts +++ b/src/layers/volume_layer.ts @@ -155,9 +155,7 @@ export class VolumeLayer extends Layer implements ChannelsEnabled { private updateChunks() { if (!this.chunkStoreView_) return; - const chunksToRender = this.chunkStoreView_.getChunksToRender( - this.sliceCoords_ - ); + const chunksToRender = this.chunkStoreView_.getChunksToRender(); const currentTime = this.sliceCoords_.t ?? -1; const groupedChunks = groupBySpatialIndex(chunksToRender);