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

Misc sector loading optimizations #3674

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions viewer/packages/cad-geometry-loaders/src/CadManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import * as THREE from 'three';

import { Subscription, Observable } from 'rxjs';
import { Subscription, Observable, auditTime, buffer } from 'rxjs';

import { LevelOfDetail, ConsumedSector, CadModelMetadata } from '@reveal/cad-parsers';
import { CadModelUpdateHandler } from './CadModelUpdateHandler';
Expand Down Expand Up @@ -35,6 +35,8 @@ export class CadManager {
private readonly _markNeedsRedrawBound = this.markNeedsRedraw.bind(this);
private readonly _materialsChangedListener = this.handleMaterialsChanged.bind(this);

private readonly _sectorBufferTime = 350;

get materialManager(): CadMaterialManager {
return this._materialManager;
}
Expand Down Expand Up @@ -107,9 +109,18 @@ export class CadManager {
this.updateTreeIndexToSectorsMap(cadModel, sector);
};

const consumeNextSectors = (sectors: ConsumedSector[]) => {
for (const sector of sectors) {
consumeNextSector(sector);
}
this._cadModelUpdateHandler.reportNewSectorsLoaded(sectors.length);
};

const consumedSectorsObservable = this._cadModelUpdateHandler.consumedSectorObservable();
const flushAt = consumedSectorsObservable.pipe(auditTime(this._sectorBufferTime));
this._subscription.add(
this._cadModelUpdateHandler.consumedSectorObservable().subscribe({
next: consumeNextSector,
consumedSectorsObservable.pipe(buffer(flushAt)).subscribe({
next: consumeNextSectors,
error: error => {
MetricsLogger.trackError(error, {
moduleName: 'CadManager',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class CadModelUpdateHandler {
private readonly _modelStateHandler: ModelStateHandler;
private _budget: CadModelBudget;
private _lastSpent: SectorLoadingSpent;
private readonly _determineSectorsHandler: SectorLoader;

private readonly _cameraSubject: Subject<CameraInput> = new Subject();
private readonly _clippingPlaneSubject: Subject<THREE.Plane[]> = new Subject();
Expand Down Expand Up @@ -106,15 +107,15 @@ export class CadModelUpdateHandler {
};
this._progressSubject.next(state);
};
const determineSectorsHandler = new SectorLoader(
this._determineSectorsHandler = new SectorLoader(
sectorCuller,
this._modelStateHandler,
collectStatisticsCallback,
reportProgressCallback,
continuousModelStreaming
);

async function* loadSectors(input: DetermineSectorsPayload) {
async function* loadSectors(input: DetermineSectorsPayload, determineSectorsHandler: SectorLoader) {
for await (const sector of determineSectorsHandler.loadSectors(input)) {
yield sector;
}
Expand All @@ -124,7 +125,7 @@ export class CadModelUpdateHandler {
observeOn(asyncScheduler), // Schedule tasks on macro task queue (setInterval)
map(createDetermineSectorsInput), // Map from array to interface (enables destructuring)
filter(loadingEnabled), // should we load?
mergeMap(async x => loadSectors(x)),
mergeMap(async x => loadSectors(x, this._determineSectorsHandler)),
mergeMap(x => x)
);
}
Expand Down Expand Up @@ -182,6 +183,10 @@ export class CadModelUpdateHandler {
return this._progressSubject;
}

reportNewSectorsLoaded(loadedCountChange: number): void {
this._determineSectorsHandler.reportNewSectorsLoaded(loadedCountChange);
}

/**
* When loading hints of a CAD model changes, propagate the event down to the stream and either add or remove
* the {@link CadModelMetadata} from the array and push the new array down stream
Expand Down
28 changes: 18 additions & 10 deletions viewer/packages/cad-geometry-loaders/src/sector/SectorLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const SectorLoadingBatchSize = 20;
*/
export class SectorLoader {
private readonly _modelStateHandler: ModelStateHandler;
private readonly _progressCallback: (sectorsLoaded: number, sectorsScheduled: number, sectorsCulled: number) => void;
private readonly _progressHelper: ProgressReportHelper;
private readonly _collectStatisticsCallback: (spent: SectorLoadingSpent) => void;
private readonly _sectorCuller: SectorCuller;
private readonly _continuousModelStreaming: boolean;
Expand All @@ -52,7 +52,7 @@ export class SectorLoader {

this._modelStateHandler = modelStateHandler;
this._collectStatisticsCallback = collectStatisticsCallback;
this._progressCallback = progressCallback;
this._progressHelper = new ProgressReportHelper(progressCallback);
this._continuousModelStreaming = continuousModelStreaming;
}

Expand Down Expand Up @@ -86,15 +86,18 @@ export class SectorLoader {
hasSectorChanged(sector.modelIdentifier, sector.metadata.id, sector.levelOfDetail)
);

const progressHelper = new ProgressReportHelper(this._progressCallback);
progressHelper.start(changedSectors.length);
this._progressHelper.reset(changedSectors.length);

this._batchId++;
const currentBatchId = this._batchId;

for (const batch of chunk(changedSectors, SectorLoadingBatchSize)) {
if (currentBatchId !== this._batchId) break; // Stop processing this batch as a new batch has started, and will discard results from old batches.
const filteredSectors = await this.filterSectors(sectorCullerInput, batch, sectorCuller, progressHelper);
if (currentBatchId !== this._batchId) {
// Stop processing this batch as a new batch has started, and will discard results from old batches.
this._progressHelper.reportNewSectorsLoaded(batch.length);
continue;
}
const filteredSectors = await this.filterSectors(sectorCullerInput, batch, sectorCuller, this._progressHelper);
const consumedPromises = this.startLoadingBatch(filteredSectors, cadModels);
for await (const consumed of PromiseUtils.raceUntilAllCompleted(consumedPromises)) {
const resolvedSector = consumed.result;
Expand All @@ -104,13 +107,18 @@ export class SectorLoader {
resolvedSector.metadata.id,
resolvedSector.levelOfDetail
);
yield resolvedSector;
yield resolvedSector; // progress will be reported when sector is loaded by CadManager
} else {
this._progressHelper.reportNewSectorsLoaded(1);
}
progressHelper.reportNewSectorsLoaded(1);
}
}
}

reportNewSectorsLoaded(loadedCountChange: number): void {
this._progressHelper.reportNewSectorsLoaded(loadedCountChange);
}

private shouldLoad(input: DetermineSectorsPayload) {
if (input.models.length == 0) {
return false;
Expand Down Expand Up @@ -157,8 +165,8 @@ class ProgressReportHelper {
this._progressCallback = reportCb;
}

start(sectorsScheduled: number) {
this._sectorsScheduled = sectorsScheduled;
reset(sectorsScheduledChange: number) {
this._sectorsScheduled += sectorsScheduledChange - this._sectorsLoaded;
this._sectorsLoaded = 0;
this._sectorsCulled = 0;
this.triggerCallback();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@

import { TypedArray, TypedArrayConstructor } from '@reveal/utilities';
import assert from 'assert';
import { BufferAttribute, InterleavedBufferAttribute, BufferGeometry, InstancedInterleavedBuffer } from 'three';
import {
BufferAttribute,
InterleavedBufferAttribute,
BufferGeometry,
InstancedInterleavedBuffer,
DynamicDrawUsage
} from 'three';

export class GeometryBufferUtils {
private static readonly TypedArrayViews = new Map<number, TypedArrayConstructor>([
Expand Down Expand Up @@ -51,6 +57,7 @@ export class GeometryBufferUtils {

const ComponentType = GeometryBufferUtils.TypedArrayViews.get(componentSize)!;
const interleavedAttributesBuffer = new InstancedInterleavedBuffer(new ComponentType(backingBuffer), stride);
interleavedAttributesBuffer.setUsage(DynamicDrawUsage);

bufferGeometry.setAttribute(
name,
Expand Down
8 changes: 4 additions & 4 deletions viewer/packages/rendering/src/utilities/renderUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,14 +265,14 @@ export function setModelRenderLayers(rootNode: THREE.Object3D, stylingSets: Styl
return;
}

if (visible.hasIntersectionWith(objectTreeIndices)) {
if (back.hasIntersectionWith(objectTreeIndices)) {
if (visible.hasIntersectionWithMap(objectTreeIndices)) {
if (back.hasIntersectionWithMap(objectTreeIndices)) {
node.layers.enable(RenderLayer.Back);
}
if (ghost.hasIntersectionWith(objectTreeIndices)) {
if (ghost.hasIntersectionWithMap(objectTreeIndices)) {
node.layers.enable(RenderLayer.Ghost);
}
if (inFront.hasIntersectionWith(objectTreeIndices)) {
if (inFront.hasIntersectionWithMap(objectTreeIndices)) {
node.layers.enable(RenderLayer.InFront);
}
}
Expand Down
16 changes: 16 additions & 0 deletions viewer/packages/utilities/src/indexset/IndexSet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ describe('IndexSet', () => {
expect(set1.hasIntersectionWith(set2)).toBeFalse();
});

test('hasIntersectionWithMap returns true if there is any overlap', () => {
const set = new IndexSet();
set.addRange(new NumericRange(1, 5));
const map = new Map<number, number>();
map.set(3, 0);
expect(set.hasIntersectionWithMap(map)).toBeTrue();
});

test('hasIntersectionWithMap returns false if there is no overlap', () => {
const set = new IndexSet();
set.addRange(new NumericRange(1, 5));
const map = new Map<number, number>();
map.set(6, 0);
expect(set.hasIntersectionWithMap(map)).toBeFalse();
});

test('differenceWith removes overlapping elements', () => {
const set1 = new IndexSet();
set1.addRange(new NumericRange(1, 5));
Expand Down
24 changes: 15 additions & 9 deletions viewer/packages/utilities/src/indexset/IndexSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,21 +142,13 @@ export class IndexSet {
return this;
}

hasIntersectionWith(otherSet: IndexSet | Map<number, number> | Set<number>): boolean {
hasIntersectionWith(otherSet: IndexSet | Set<number>): boolean {
if (otherSet instanceof IndexSet) {
if (this.rootNode === undefined || otherSet.rootNode === undefined) {
return false;
}

return this.rootNode.hasIntersectionWith(otherSet.rootNode);
} else if (otherSet instanceof Map) {
for (const index of otherSet.keys()) {
if (this.contains(index)) {
return true;
}
}

return false;
} else {
for (const index of otherSet) {
if (this.contains(index)) {
Expand All @@ -168,6 +160,20 @@ export class IndexSet {
}
}

hasIntersectionWithMap(otherMap: Map<number, number>): boolean {
if (!this.rootNode) {
return false;
}

for (const index of otherMap.keys()) {
if (this.rootNode.contains(index)) {
return true;
}
}

return false;
}

intersectWith(otherSet: IndexSet): IndexSet {
if (this.rootNode && otherSet.rootNode) {
// Tackle endpoints
Expand Down
4 changes: 3 additions & 1 deletion viewer/reveal.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -900,7 +900,9 @@ export class IndexSet {
// (undocumented)
forEachRange(visitor: (range: NumericRange) => void): void;
// (undocumented)
hasIntersectionWith(otherSet: IndexSet | Map<number, number> | Set<number>): boolean;
hasIntersectionWith(otherSet: IndexSet | Set<number>): boolean;
// (undocumented)
hasIntersectionWithMap(otherMap: Map<number, number>): boolean;
// (undocumented)
intersectWith(otherSet: IndexSet): IndexSet;
// (undocumented)
Expand Down
Loading