Bladeren bron

mol-gl: partial scene commit support

David Sehnal 5 jaren geleden
bovenliggende
commit
d722d17a02

+ 4 - 7
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;
             }
         }
 

+ 1 - 1
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() {

+ 2 - 2
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);

+ 55 - 0
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;
+    }
+}

+ 39 - 24
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)
             },