From d722d17a022af918abc8adae6789a273a55f66b3 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Wed, 19 Feb 2020 22:19:17 +0100 Subject: [PATCH] mol-gl: partial scene commit support --- src/mol-canvas3d/canvas3d.ts | 11 ++-- .../helper/bounding-sphere-helper.ts | 2 +- src/mol-gl/_spec/renderer.spec.ts | 4 +- src/mol-gl/commit-queue.ts | 55 ++++++++++++++++ src/mol-gl/scene.ts | 63 ++++++++++++------- 5 files changed, 101 insertions(+), 34 deletions(-) create mode 100644 src/mol-gl/commit-queue.ts diff --git a/src/mol-canvas3d/canvas3d.ts b/src/mol-canvas3d/canvas3d.ts index a8b7b4f3d..9b2e5a380 100644 --- a/src/mol-canvas3d/canvas3d.ts +++ b/src/mol-canvas3d/canvas3d.ts @@ -294,14 +294,13 @@ namespace Canvas3D { cameraResetRequested = false; } - let isDirty = false; + const sceneCommitTimeoutMs = 250; function commitScene() { - if (!isDirty) return; + if (!scene.needsCommit) return; - scene.syncCommit(); + const allCommited = scene.commit(sceneCommitTimeoutMs); if (debugHelper.isEnabled) debugHelper.update(); - reprCount.next(reprRenderObjects.size); - isDirty = false; + if (allCommited) reprCount.next(reprRenderObjects.size); } function add(repr: Representation.Any) { @@ -322,7 +321,6 @@ namespace Canvas3D { reprRenderObjects.set(repr, newRO) scene.update(repr.renderObjects, false) - isDirty = true; } function remove(repr: Representation.Any) { @@ -333,7 +331,6 @@ namespace Canvas3D { renderObjects.forEach(o => scene.remove(o)) reprRenderObjects.delete(repr) scene.update(repr.renderObjects, false, true) - isDirty = true; } } diff --git a/src/mol-canvas3d/helper/bounding-sphere-helper.ts b/src/mol-canvas3d/helper/bounding-sphere-helper.ts index 4d0d18e2c..69f691941 100644 --- a/src/mol-canvas3d/helper/bounding-sphere-helper.ts +++ b/src/mol-canvas3d/helper/bounding-sphere-helper.ts @@ -82,7 +82,7 @@ export class BoundingSphereHelper { }) this.scene.update(void 0, false) - this.scene.syncCommit() + this.scene.commit() } syncVisibility() { diff --git a/src/mol-gl/_spec/renderer.spec.ts b/src/mol-gl/_spec/renderer.spec.ts index 9de20a89e..c9fdde6dd 100644 --- a/src/mol-gl/_spec/renderer.spec.ts +++ b/src/mol-gl/_spec/renderer.spec.ts @@ -121,7 +121,7 @@ describe('renderer', () => { const points = createPoints() scene.add(points) - scene.syncCommit() + scene.commit() expect(ctx.stats.resourceCounts.attribute).toBe(4); expect(ctx.stats.resourceCounts.texture).toBe(5); expect(ctx.stats.resourceCounts.vertexArray).toBe(5); @@ -129,7 +129,7 @@ describe('renderer', () => { expect(ctx.stats.resourceCounts.shader).toBe(10); scene.remove(points) - scene.syncCommit() + scene.commit() expect(ctx.stats.resourceCounts.attribute).toBe(0); expect(ctx.stats.resourceCounts.texture).toBe(0); expect(ctx.stats.resourceCounts.vertexArray).toBe(0); diff --git a/src/mol-gl/commit-queue.ts b/src/mol-gl/commit-queue.ts new file mode 100644 index 000000000..2cfd37d62 --- /dev/null +++ b/src/mol-gl/commit-queue.ts @@ -0,0 +1,55 @@ +/** + * Copyright (c) 2020 mol* contributors, licensed under MIT, See LICENSE file for more info. + * + * @author David Sehnal <david.sehnal@gmail.com> + */ + +import { LinkedList } from '../mol-data/generic' +import { GraphicsRenderObject } from './render-object' + +type N = LinkedList.Node<GraphicsRenderObject> + +export class CommitQueue { + private removeList = LinkedList<GraphicsRenderObject>(); + private removeMap = new Map<GraphicsRenderObject, N>(); + private addList = LinkedList<GraphicsRenderObject>(); + private addMap = new Map<GraphicsRenderObject, N>(); + + get isEmpty() { + return this.removeList.count === 0 && this.addList.count === 0; + } + + add(o: GraphicsRenderObject) { + if (this.removeMap.has(o)) { + const a = this.removeMap.get(o)!; + this.removeMap.delete(o); + this.removeList.remove(a); + } + if (this.addMap.has(o)) return; + const b = this.addList.addLast(o); + this.addMap.set(o, b); + } + + remove(o: GraphicsRenderObject) { + if (this.addMap.has(o)) { + const a = this.addMap.get(o)!; + this.addMap.delete(o); + this.addList.remove(a); + } + if (this.removeMap.has(o)) return; + const b = this.removeList.addLast(o); + this.removeMap.set(o, b); + } + + tryGetRemove() { + const o = this.removeList.removeFirst(); + if (o) this.removeMap.delete(o); + return o; + } + + tryGetAdd() { + const o = this.addList.removeFirst(); + if (o) this.addMap.delete(o); + return o; + } +} diff --git a/src/mol-gl/scene.ts b/src/mol-gl/scene.ts index 2eba53378..d4760194b 100644 --- a/src/mol-gl/scene.ts +++ b/src/mol-gl/scene.ts @@ -13,7 +13,9 @@ import { Object3D } from './object3d'; import { Sphere3D } from '../mol-math/geometry'; import { Vec3 } from '../mol-math/linear-algebra'; import { BoundaryHelper } from '../mol-math/geometry/boundary-helper'; -import { arraySetAdd, arraySetRemove } from '../mol-util/array'; +import { CommitQueue } from './commit-queue'; +import { now } from '../mol-util/now'; +import { arraySetRemove } from '../mol-util/array'; const boundaryHelper = new BoundaryHelper(); function calculateBoundingSphere(renderables: Renderable<RenderableValues & BaseValues>[], boundingSphere: Sphere3D): Sphere3D { @@ -56,13 +58,12 @@ interface Scene extends Object3D { readonly count: number readonly renderables: ReadonlyArray<Renderable<RenderableValues & BaseValues>> readonly boundingSphere: Sphere3D - // readonly isCommiting: boolean update: (objects: ArrayLike<GraphicsRenderObject> | undefined, keepBoundingSphere: boolean, isRemoving?: boolean) => void add: (o: GraphicsRenderObject) => void // Renderable<any> remove: (o: GraphicsRenderObject) => void - syncCommit: () => void - // commit: () => Task<void> + commit: (maxTimeMs?: number) => boolean + readonly needsCommit: boolean has: (o: GraphicsRenderObject) => boolean clear: () => void forEach: (callbackFn: (value: Renderable<RenderableValues & BaseValues>, key: GraphicsRenderObject) => void) => void @@ -78,7 +79,7 @@ namespace Scene { const object3d = Object3D.create() - const add = (o: GraphicsRenderObject) => { + function add(o: GraphicsRenderObject) { if (!renderableMap.has(o)) { const renderable = createRenderable(ctx, o) renderables.push(renderable) @@ -91,18 +92,43 @@ namespace Scene { } } - const remove = (o: GraphicsRenderObject) => { + function remove(o: GraphicsRenderObject) { const renderable = renderableMap.get(o) if (renderable) { renderable.dispose() - renderables.splice(renderables.indexOf(renderable), 1) + arraySetRemove(renderables, renderable); renderableMap.delete(o) boundingSphereDirty = true } } - const toAdd: GraphicsRenderObject[] = [] - const toRemove: GraphicsRenderObject[] = [] + const commitBulkSize = 100; + function commit(maxTimeMs: number) { + const start = now(); + + let i = 0; + + while (true) { + const o = commitQueue.tryGetRemove(); + if (!o) break; + remove(o); + if (++i % commitBulkSize === 0 && now() - start > maxTimeMs) return false; + } + + while (true) { + const o = commitQueue.tryGetAdd(); + if (!o) break; + add(o); + if (++i % commitBulkSize === 0 && now() - start > maxTimeMs) return false; + } + + renderables.sort(renderableSort) + return true; + } + + // const toAdd: GraphicsRenderObject[] = [] + // const toRemove: GraphicsRenderObject[] = [] + const commitQueue = new CommitQueue(); return { get view () { return object3d.view }, @@ -126,21 +152,10 @@ namespace Scene { } if (!keepBoundingSphere) boundingSphereDirty = true }, - add: (o: GraphicsRenderObject) => { - arraySetAdd(toAdd, o); - arraySetRemove(toRemove, o); - }, - remove: (o: GraphicsRenderObject) => { - arraySetAdd(toRemove, o); - arraySetRemove(toAdd, o); - }, - syncCommit: () => { - for (let i = 0, il = toRemove.length; i < il; ++i) remove(toRemove[i]) - toRemove.length = 0 - for (let i = 0, il = toAdd.length; i < il; ++i) add(toAdd[i]) - toAdd.length = 0 - renderables.sort(renderableSort) - }, + add: (o: GraphicsRenderObject) => commitQueue.add(o), + remove: (o: GraphicsRenderObject) => commitQueue.remove(o), + commit: (maxTime = Number.MAX_VALUE) => commit(maxTime), + get needsCommit() { return !commitQueue.isEmpty; }, has: (o: GraphicsRenderObject) => { return renderableMap.has(o) }, -- GitLab