Ver código fonte

improve scene marker/opacity average calculation

Alexander Rose 2 anos atrás
pai
commit
f824fdcfed

+ 1 - 0
CHANGELOG.md

@@ -9,6 +9,7 @@ Note that since we don't clearly distinguish between a public and private interf
 - Fix issues with marking camera/handle helper (#433)
 - Fix issues with array uniforms when running with headless-gl
 - Fix Polymer Chain Instance coloring
+- Improve performance of scene marker/opacity average calculation
 
 ## [v3.8.0] - 2022-04-30
 

+ 39 - 49
src/mol-canvas3d/passes/draw.ts

@@ -38,15 +38,13 @@ export class DrawPass {
     private readonly drawTarget: RenderTarget;
 
     readonly colorTarget: RenderTarget;
-    readonly depthTexture: Texture;
-    readonly depthTexturePrimitives: Texture;
+    readonly depthTextureTransparent: Texture;
+    readonly depthTextureOpaque: Texture;
 
     readonly packedDepth: boolean;
 
-    private depthTarget: RenderTarget;
-    private depthTargetPrimitives: RenderTarget | null;
-    private depthTargetVolumes: RenderTarget | null;
-    private depthTextureVolumes: Texture;
+    private depthTargetTransparent: RenderTarget;
+    private depthTargetOpaque: RenderTarget | null;
 
     private copyFboTarget: CopyRenderable;
     private copyFboPostprocessing: CopyRenderable;
@@ -68,17 +66,14 @@ export class DrawPass {
         this.colorTarget = webgl.createRenderTarget(width, height, true, 'uint8', 'linear');
         this.packedDepth = !extensions.depthTexture;
 
-        this.depthTarget = webgl.createRenderTarget(width, height);
-        this.depthTexture = this.depthTarget.texture;
+        this.depthTargetTransparent = webgl.createRenderTarget(width, height);
+        this.depthTextureTransparent = this.depthTargetTransparent.texture;
 
-        this.depthTargetPrimitives = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
-        this.depthTargetVolumes = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
+        this.depthTargetOpaque = this.packedDepth ? webgl.createRenderTarget(width, height) : null;
 
-        this.depthTexturePrimitives = this.depthTargetPrimitives ? this.depthTargetPrimitives.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
-        this.depthTextureVolumes = this.depthTargetVolumes ? this.depthTargetVolumes.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
+        this.depthTextureOpaque = this.depthTargetOpaque ? this.depthTargetOpaque.texture : resources.texture('image-depth', 'depth', isWebGL2 ? 'float' : 'ushort', 'nearest');
         if (!this.packedDepth) {
-            this.depthTexturePrimitives.define(width, height);
-            this.depthTextureVolumes.define(width, height);
+            this.depthTextureOpaque.define(width, height);
         }
 
         this.wboit = enableWboit ? new WboitPass(webgl, width, height) : undefined;
@@ -100,18 +95,12 @@ export class DrawPass {
 
         if (width !== w || height !== h) {
             this.colorTarget.setSize(width, height);
-            this.depthTarget.setSize(width, height);
+            this.depthTargetTransparent.setSize(width, height);
 
-            if (this.depthTargetPrimitives) {
-                this.depthTargetPrimitives.setSize(width, height);
+            if (this.depthTargetOpaque) {
+                this.depthTargetOpaque.setSize(width, height);
             } else {
-                this.depthTexturePrimitives.define(width, height);
-            }
-
-            if (this.depthTargetVolumes) {
-                this.depthTargetVolumes.setSize(width, height);
-            } else {
-                this.depthTextureVolumes.define(width, height);
+                this.depthTextureOpaque.define(width, height);
             }
 
             ValueCell.update(this.copyFboTarget.values.uTexSize, Vec2.set(this.copyFboTarget.values.uTexSize.ref.value, width, height));
@@ -134,17 +123,17 @@ export class DrawPass {
         renderer.clear(true);
 
         // render opaque primitives
-        this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
+        this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
         this.colorTarget.bind();
         renderer.clearDepth();
         renderer.renderWboitOpaque(scene.primitives, camera, null);
 
         if (PostprocessingPass.isEnabled(postprocessingProps)) {
             if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) {
-                this.depthTarget.bind();
+                this.depthTargetTransparent.bind();
                 renderer.clearDepth(true);
-                if (scene.getOpacityAverage() < 1) {
-                    renderer.renderDepthTransparent(scene.primitives, camera, this.depthTexturePrimitives);
+                if (scene.opacityAverage < 1) {
+                    renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque);
                 }
             }
 
@@ -152,17 +141,19 @@ export class DrawPass {
         }
 
         // render transparent primitives and volumes
-        this.wboit.bind();
-        renderer.renderWboitTransparent(scene.primitives, camera, this.depthTexturePrimitives);
-        renderer.renderWboitTransparent(scene.volumes, camera, this.depthTexturePrimitives);
+        if (scene.opacityAverage < 1 || scene.volumes.renderables.length > 0) {
+            this.wboit.bind();
+            renderer.renderWboitTransparent(scene.primitives, camera, this.depthTextureOpaque);
+            renderer.renderWboitTransparent(scene.volumes, camera, this.depthTextureOpaque);
 
-        // evaluate wboit
-        if (PostprocessingPass.isEnabled(postprocessingProps)) {
-            this.postprocessing.target.bind();
-        } else {
-            this.colorTarget.bind();
+            // evaluate wboit
+            if (PostprocessingPass.isEnabled(postprocessingProps)) {
+                this.postprocessing.target.bind();
+            } else {
+                this.colorTarget.bind();
+            }
+            this.wboit.render();
         }
-        this.wboit.render();
     }
 
     private _renderBlended(renderer: Renderer, camera: ICamera, scene: Scene, toDrawingBuffer: boolean, transparentBackground: boolean, postprocessingProps: PostprocessingProps) {
@@ -171,7 +162,7 @@ export class DrawPass {
         } else {
             this.colorTarget.bind();
             if (!this.packedDepth) {
-                this.depthTexturePrimitives.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
+                this.depthTextureOpaque.attachFramebuffer(this.colorTarget.framebuffer, 'depth');
             }
         }
 
@@ -181,8 +172,8 @@ export class DrawPass {
         if (!toDrawingBuffer) {
             // do a depth pass if not rendering to drawing buffer and
             // extensions.depthTexture is unsupported (i.e. depthTarget is set)
-            if (this.depthTargetPrimitives) {
-                this.depthTargetPrimitives.bind();
+            if (this.depthTargetOpaque) {
+                this.depthTargetOpaque.bind();
                 renderer.clearDepth(true);
                 renderer.renderDepthOpaque(scene.primitives, camera, null);
                 this.colorTarget.bind();
@@ -190,29 +181,29 @@ export class DrawPass {
 
             if (PostprocessingPass.isEnabled(postprocessingProps)) {
                 if (!this.packedDepth) {
-                    this.depthTexturePrimitives.detachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
+                    this.depthTextureOpaque.detachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
                 } else {
                     this.colorTarget.depthRenderbuffer?.detachFramebuffer(this.postprocessing.target.framebuffer);
                 }
 
                 if (PostprocessingPass.isOutlineEnabled(postprocessingProps)) {
-                    this.depthTarget.bind();
+                    this.depthTargetTransparent.bind();
                     renderer.clearDepth(true);
-                    if (scene.getOpacityAverage() < 1) {
-                        renderer.renderDepthTransparent(scene.primitives, camera, this.depthTexturePrimitives);
+                    if (scene.opacityAverage < 1) {
+                        renderer.renderDepthTransparent(scene.primitives, camera, this.depthTextureOpaque);
                     }
                 }
 
                 this.postprocessing.render(camera, false, transparentBackground, renderer.props.backgroundColor, postprocessingProps);
 
                 if (!this.packedDepth) {
-                    this.depthTexturePrimitives.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
+                    this.depthTextureOpaque.attachFramebuffer(this.postprocessing.target.framebuffer, 'depth');
                 } else {
                     this.colorTarget.depthRenderbuffer?.attachFramebuffer(this.postprocessing.target.framebuffer);
                 }
             }
 
-            renderer.renderBlendedVolume(scene.volumes, camera, this.depthTexturePrimitives);
+            renderer.renderBlendedVolume(scene.volumes, camera, this.depthTextureOpaque);
         }
 
         renderer.renderBlendedTransparent(scene.primitives, camera, null);
@@ -248,10 +239,9 @@ export class DrawPass {
         }
 
         if (markingEnabled) {
-            const markerAverage = scene.getMarkerAverage();
-            if (markerAverage > 0) {
+            if (scene.markerAverage > 0) {
                 const markingDepthTest = props.marking.ghostEdgeStrength < 1;
-                if (markingDepthTest && markerAverage !== 1) {
+                if (markingDepthTest && scene.markerAverage !== 1) {
                     this.marking.depthTarget.bind();
                     renderer.clear(false, true);
                     renderer.renderMarkingDepth(scene.primitives, camera, null);

+ 0 - 2
src/mol-canvas3d/passes/pick.ts

@@ -64,12 +64,10 @@ export class PickPass {
     }
 
     private renderVariant(renderer: Renderer, camera: ICamera, scene: Scene, helper: Helper, variant: 'pick' | 'depth', pickType: number) {
-        const depth = this.drawPass.depthTexturePrimitives;
         renderer.clear(false);
 
         renderer.update(camera);
         renderer.renderPick(scene.primitives, camera, variant, null, pickType);
-        renderer.renderPick(scene.volumes, camera, variant, depth, pickType);
         renderer.renderPick(helper.handle.scene, camera, variant, null, pickType);
 
         if (helper.camera.isEnabled) {

+ 2 - 2
src/mol-canvas3d/passes/postprocessing.ts

@@ -318,7 +318,7 @@ export class PostprocessingPass {
     }
 
     constructor(private webgl: WebGLContext, private drawPass: DrawPass) {
-        const { colorTarget, depthTexture: depthTextureTransparent, depthTexturePrimitives: depthTextureOpaque } = drawPass;
+        const { colorTarget, depthTextureTransparent, depthTextureOpaque } = drawPass;
         const width = colorTarget.getWidth();
         const height = colorTarget.getHeight();
 
@@ -471,7 +471,7 @@ export class PostprocessingPass {
                 this.ssaoDepthBlurProxyTexture.define(sw, sh);
 
                 if (this.ssaoScale === 1) {
-                    ValueCell.update(this.ssaoRenderable.values.tDepth, this.drawPass.depthTexture);
+                    ValueCell.update(this.ssaoRenderable.values.tDepth, this.drawPass.depthTextureTransparent);
                 } else {
                     ValueCell.update(this.ssaoRenderable.values.tDepth, this.downsampledDepthTarget.texture);
                 }

+ 41 - 32
src/mol-gl/scene.ts

@@ -80,8 +80,8 @@ interface Scene extends Object3D {
     has: (o: GraphicsRenderObject) => boolean
     clear: () => void
     forEach: (callbackFn: (value: GraphicsRenderable, key: GraphicsRenderObject) => void) => void
-    getMarkerAverage: () => number
-    getOpacityAverage: () => number
+    readonly markerAverage: number
+    readonly opacityAverage: number
 }
 
 namespace Scene {
@@ -101,6 +101,9 @@ namespace Scene {
         let boundingSphereDirty = true;
         let boundingSphereVisibleDirty = true;
 
+        let markerAverage = 0;
+        let opacityAverage = 0;
+
         const object3d = Object3D.create();
         const { view, position, direction, up } = object3d;
 
@@ -157,6 +160,7 @@ namespace Scene {
             }
 
             renderables.sort(renderableSort);
+            opacityAverage = calculateOpacityAverage();
             return true;
         }
 
@@ -178,12 +182,41 @@ namespace Scene {
             const newVisibleHash = computeVisibleHash();
             if (newVisibleHash !== visibleHash) {
                 boundingSphereVisibleDirty = true;
+                opacityAverage = calculateOpacityAverage();
                 return true;
             } else {
                 return false;
             }
         }
 
+        function calculateMarkerAverage() {
+            if (primitives.length === 0) return 0;
+            let count = 0;
+            let markerAverage = 0;
+            for (let i = 0, il = primitives.length; i < il; ++i) {
+                if (!primitives[i].state.visible) continue;
+                markerAverage += primitives[i].values.markerAverage.ref.value;
+                count += 1;
+            }
+            return count > 0 ? markerAverage / count : 0;
+        }
+
+        function calculateOpacityAverage() {
+            if (primitives.length === 0) return 0;
+            let count = 0;
+            let opacityAverage = 0;
+            for (let i = 0, il = primitives.length; i < il; ++i) {
+                const p = primitives[i];
+                if (!p.state.visible) continue;
+                // TODO: simplify, handle in renderable.state???
+                // uAlpha is updated in "render" so we need to recompute it here
+                const alpha = clamp(p.values.alpha.ref.value * p.state.alphaFactor, 0, 1);
+                opacityAverage += (1 - p.values.transparencyAverage.ref.value) * alpha;
+                count += 1;
+            }
+            return count > 0 ? opacityAverage / count : 0;
+        }
+
         return {
             view, position, direction, up,
 
@@ -209,6 +242,8 @@ namespace Scene {
                 } else {
                     syncVisibility();
                 }
+                markerAverage = calculateMarkerAverage();
+                opacityAverage = calculateOpacityAverage();
             },
             add: (o: GraphicsRenderObject) => commitQueue.add(o),
             remove: (o: GraphicsRenderObject) => commitQueue.remove(o),
@@ -247,37 +282,11 @@ namespace Scene {
                 }
                 return boundingSphereVisible;
             },
-            getMarkerAverage() {
-                if (primitives.length === 0 && volumes.length === 0) return 0;
-                let count = 0;
-                let markerAverage = 0;
-                for (let i = 0, il = primitives.length; i < il; ++i) {
-                    if (!primitives[i].state.visible) continue;
-                    markerAverage += primitives[i].values.markerAverage.ref.value;
-                    count += 1;
-                }
-                for (let i = 0, il = volumes.length; i < il; ++i) {
-                    if (!volumes[i].state.visible) continue;
-                    markerAverage += volumes[i].values.markerAverage.ref.value;
-                    count += 1;
-                }
-                return count > 0 ? markerAverage / count : 0;
+            get markerAverage() {
+                return markerAverage;
             },
-            getOpacityAverage() {
-                if (primitives.length === 0) return 0;
-                let count = 0;
-                let opacityAverage = 0;
-                for (let i = 0, il = primitives.length; i < il; ++i) {
-                    const p = primitives[i];
-                    if (!p.state.visible) continue;
-
-                    // TODO: simplify, handle in renderable.state???
-                    // uAlpha is updated in "render" so we need to recompute it here
-                    const alpha = clamp(p.values.alpha.ref.value * p.state.alphaFactor, 0, 1);
-                    opacityAverage += (1 - p.values.transparencyAverage.ref.value) * alpha;
-                    count += 1;
-                }
-                return count > 0 ? opacityAverage / count : 0;
+            get opacityAverage() {
+                return opacityAverage;
             },
         };
     }