Browse Source

Volume Streaming all types use MonoQueue

Adam Midlik 2 years ago
parent
commit
b6be871a21

+ 8 - 8
src/mol-plugin-ui/custom/volume.tsx

@@ -246,14 +246,14 @@ export class VolumeStreamingCustomControls extends PluginUIComponent<StateTransf
         const entry = (this.props.info.params as VolumeStreaming.ParamDefinition)
             .entry.map(params.entry.name) as PD.Group<VolumeStreaming.EntryParamDefinition>;
         const detailLevel = entry.params.detailLevel;
-        const dynamicDetailLevel = (params.entry.params.view.params as any).dynamicDetailLevel;
-        const selectionDetailLevel = (params.entry.params.view.params as any).selectionDetailLevel;
-        console.log('params:', params);
-        console.log('entry.params:', entry.params);
-        console.log('entry:', (this.props.info.params as VolumeStreaming.ParamDefinition).entry);
-        console.log('detailLevel:', detailLevel);
-        console.log('dynamicDetailLevel:', dynamicDetailLevel);
-        console.log('selectionDetailLevel:', selectionDetailLevel);
+        // const dynamicDetailLevel = (params.entry.params.view.params as any).dynamicDetailLevel;
+        // const selectionDetailLevel = (params.entry.params.view.params as any).selectionDetailLevel;
+        // console.log('params:', params);
+        // console.log('entry.params:', entry.params);
+        // console.log('entry:', (this.props.info.params as VolumeStreaming.ParamDefinition).entry);
+        // console.log('detailLevel:', detailLevel);
+        // console.log('dynamicDetailLevel:', dynamicDetailLevel);
+        // console.log('selectionDetailLevel:', selectionDetailLevel);
         // TODO Adam: somehow try to get the correct mdfkn dynamicDetailLevel value
         const isRelative = ((params.entry.params.channels as any)[pivot].isoValue as Volume.IsoValue).kind === 'relative';
 

+ 47 - 64
src/mol-plugin/behavior/dynamic/volume-streaming/behavior.ts

@@ -159,10 +159,9 @@ export namespace VolumeStreaming {
         private lastLoci: StructureElement.Loci | EmptyLoci = EmptyLoci;
         private ref: string = '';
         public infoMap: Map<string, VolumeServerInfo.EntryData>;
-        private debugCounter: number = 0;
-        private queue: MonoQueue;
+        private updateQueue: MonoQueue;
         private cameraTargetObservable = this.plugin.canvas3d!.didDraw!.pipe(
-            // throttleTime(500, undefined, { 'leading': true, 'trailing': true }),  // debug TODO throttle
+            throttleTime(500, undefined, { 'leading': true, 'trailing': true }),
             map(() => this.plugin.canvas3d?.camera.getSnapshot()),
             distinctUntilChanged((a, b) => this.isCameraTargetSame(a, b)),
             filter(a => a !== undefined),
@@ -255,10 +254,14 @@ export namespace VolumeStreaming {
             };
             const update = state.build().to(this.ref).update(newParams);
 
+            // const task = state.updateTree(update, { doNotUpdateCurrent: true });
+            // const promise = this.plugin.runTask(task);
+            // setTimeout(() => this.plugin.managers.task.requestAbort(task.id), 200);
+            // await promise;
             await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotUpdateCurrent: true } });
         }
 
-        private updateCameraTargetParams(box: Box3D | undefined) {
+        private async updateCameraTargetParams(box: Box3D | undefined) {
             if (this.params.entry.params.view.name !== 'camera-target') return;
 
             const state = this.plugin.state.data;
@@ -282,10 +285,10 @@ export namespace VolumeStreaming {
             };
             const update = state.build().to(this.ref).update(newParams);
 
-            PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotUpdateCurrent: true } });
+            await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotUpdateCurrent: true } });
         }
 
-        private updateAutoParams(box: Box3D | undefined, isSelection: boolean) {
+        private async updateAutoParams(box: Box3D | undefined, isSelection: boolean) {
             if (this.params.entry.params.view.name !== 'auto') return;
 
             const state = this.plugin.state.data;
@@ -310,7 +313,7 @@ export namespace VolumeStreaming {
             };
             const update = state.build().to(this.ref).update(newParams);
 
-            PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotUpdateCurrent: true } });
+            await PluginCommands.State.Update(this.plugin, { state, tree: update, options: { doNotUpdateCurrent: true } });
             // TODO QUESTION is there a reason for this much code repetition? updateSelectionBoxParams vs updateAutoParams (and now also updateCameraTargetParams)
         }
 
@@ -320,7 +323,6 @@ export namespace VolumeStreaming {
 
         register(ref: string): void {
             this.ref = ref;
-            this.queue = new MonoQueue();
 
             this.subscribeObservable(this.plugin.state.events.object.removed, o => {
                 if (!PluginStateObject.Molecule.Structure.is(o.obj) || !StructureElement.Loci.is(this.lastLoci)) return;
@@ -400,27 +402,27 @@ export namespace VolumeStreaming {
         }
 
         private updateAuto(loci: StructureElement.Loci | EmptyLoci) {
-            // if (Loci.areEqual(this.lastLoci, loci)) {
-            //     this.lastLoci = EmptyLoci;
-            //     this.updateSelectionBoxParams(Box3D.empty());
-            //     return;
-            // }
+            this.updateQueue.enqueue(async () => {
+                // if (Loci.areEqual(this.lastLoci, loci)) {
+                //     this.lastLoci = EmptyLoci;
+                //     this.updateSelectionBoxParams(Box3D.empty());
+                //     return;
+                // }
 
-            this.lastLoci = loci;
+                this.lastLoci = loci;
 
-            if (isEmptyLoci(loci)) {
-                this.updateAutoParams(this.info.kind === 'x-ray' ? this.data.structure.boundary.box : void 0, false);
-                return;
-            }
+                if (isEmptyLoci(loci)) {
+                    this.updateAutoParams(this.info.kind === 'x-ray' ? this.data.structure.boundary.box : void 0, false);
+                    return;
+                }
 
-            const box = this.getBoxFromLoci(loci);
-            this.updateAutoParams(box, true);
+                const box = this.getBoxFromLoci(loci);
+                await this.updateAutoParams(box, true);
+            });
         }
 
         private updateSelectionBox(loci: StructureElement.Loci | EmptyLoci) {
-            console.log('Trigger', this.debugCounter++);
-
-            this.queue.enqueue(async () => {
+            this.updateQueue.enqueue(async () => {
                 if (Loci.areEqual(this.lastLoci, loci)) {
                     this.lastLoci = EmptyLoci;
                     this.updateSelectionBoxParams(Box3D());
@@ -437,34 +439,13 @@ export namespace VolumeStreaming {
                 const box = this.getBoxFromLoci(loci);
                 await this.updateSelectionBoxParams(box);
             });
-            
-            // if (Loci.areEqual(this.lastLoci, loci)) {
-            //     this.lastLoci = EmptyLoci;
-            //     this.updateSelectionBoxParams(Box3D());
-            //     return;
-            // }
-
-            // this.lastLoci = loci;
-
-            // if (isEmptyLoci(loci)) {
-            //     this.updateSelectionBoxParams(Box3D());
-            //     return;
-            // }
-
-            // const box = this.getBoxFromLoci(loci);
-            // this.updateSelectionBoxParams(box);
         }
 
         private updateCameraTarget(snapshot: Camera.Snapshot) {
-            console.log('Trigger', this.debugCounter++);
-            const box = this.boxFromCameraTarget(snapshot, true);
-            this.updateCameraTargetParams(box);
-            // new MonoQueue().enqueue(async () => {
-            //     const box = this.boxFromCameraTarget(snapshot, true);
-            //     console.log('Subscribed:', 'distance:', this.cameraTargetDistance(snapshot), snapshot, 'box:', box);
-            //     await this.updateCameraTargetParams(box);
-            // });
-            // TODO Adam each new update should cancel the previous?
+            this.updateQueue.enqueue(async () => {
+                const box = this.boxFromCameraTarget(snapshot, true);
+                await this.updateCameraTargetParams(box);
+            });
         }
 
         private boxFromCameraTarget(snapshot: Camera.Snapshot, boundByBoundarySize: boolean): Box3D {
@@ -496,17 +477,17 @@ export namespace VolumeStreaming {
 
         private decideDetail(box: Box3D, baseDetail: number): number {
             const cellVolume = this.info.kind === 'x-ray'
-                ? Box3D.volume(this.data.structure.boundary.box) 
-                : this.info.header.spacegroup.size.reduce((a, b) => a*b, 1);
+                ? Box3D.volume(this.data.structure.boundary.box)
+                : this.info.header.spacegroup.size.reduce((a, b) => a * b, 1);
             const boxVolume = Box3D.volume(box);
-            let ratio = boxVolume/cellVolume;
+            let ratio = boxVolume / cellVolume;
             const maxDetail = this.info.header.availablePrecisions.length - 1;
             let detail = baseDetail;
             while (ratio <= 0.5 && detail < maxDetail) {
                 ratio *= 2;
                 detail += 1;
             }
-            // console.log(`decided dynamic detail: ${detail}, (baseDetail: ${baseDetail}, box/cell volume ratio: ${boxVolume/cellVolume})`);
+            console.log(`decided dynamic detail: ${detail}, (baseDetail: ${baseDetail}, box/cell volume ratio: ${boxVolume/cellVolume})`);
             return detail;
         }
 
@@ -516,7 +497,7 @@ export namespace VolumeStreaming {
             this.params = params;
             let box: Box3D | undefined = void 0, emptyData = false;
 
-            if (params.entry.params.view.name !== 'camera-target' && this.cameraTargetSubscription){
+            if (params.entry.params.view.name !== 'camera-target' && this.cameraTargetSubscription) {
                 this.cameraTargetSubscription.unsubscribe();
                 this.cameraTargetSubscription = undefined;
             }
@@ -541,7 +522,7 @@ export namespace VolumeStreaming {
                     break;
                 }
                 case 'camera-target':
-                    if (!this.cameraTargetSubscription){
+                    if (!this.cameraTargetSubscription) {
                         this.cameraTargetSubscription = this.subscribeObservable(this.cameraTargetObservable, (e) => this.updateCameraTarget(e));
                     }
                     // TODO QUESTION why should I subscribe here in `update`, when all other views subscribe in `register`?
@@ -610,25 +591,27 @@ export namespace VolumeStreaming {
 
             this.infoMap = new Map<string, VolumeServerInfo.EntryData>();
             this.data.entries.forEach(info => this.infoMap.set(info.dataId, info));
+            this.updateQueue = new MonoQueue();
         }
     }
 }
 
 /** Job queue that allows at most one running and one pending job. 
- * A newly enqueued job will cancel any other pending job.
- * TODO find a more appropriate place for this util class
- */
+ * A newly enqueued job will cancel any other pending jobs. */
 class MonoQueue {
     private isRunning: boolean;
-    private queue: {id:number, func:() => any}[];
-    private counter = 0;
-    constructor() {
+    private queue: { id: number, func: () => any }[];
+    private counter: number;
+    private log: boolean;
+    constructor(log: boolean = false) {
         this.isRunning = false;
         this.queue = [];
+        this.counter = 0;
+        this.log = log;
     }
     enqueue(job: () => any) {
-        console.log('MonoQueue enqueue', this.counter);
-        this.queue[0] = {id:this.counter, func:job};
+        if (this.log) console.log('MonoQueue enqueue', this.counter);
+        this.queue[0] = { id: this.counter, func: job };
         this.counter++;
         this.run();  // do not await
     }
@@ -638,9 +621,9 @@ class MonoQueue {
         if (!job) return;
         this.isRunning = true;
         try {
-            console.log('MonoQueue run', job.id);
+            if (this.log) console.log('MonoQueue run', job.id);
             await job.func();
-            console.log('MonoQueue complete', job.id);
+            if (this.log) console.log('MonoQueue complete', job.id);
         } finally {
             this.isRunning = false;
             this.run();