Bladeren bron

render loop improvements, sort renderables

Alexander Rose 6 jaren geleden
bovenliggende
commit
203e71522c
3 gewijzigde bestanden met toevoegingen van 51 en 15 verwijderingen
  1. 9 0
      src/mol-gl/renderable.ts
  2. 25 14
      src/mol-gl/renderer.ts
  3. 17 1
      src/mol-gl/scene.ts

+ 9 - 0
src/mol-gl/renderable.ts

@@ -10,6 +10,9 @@ import { RenderVariant, RenderItem } from './webgl/render-item';
 import { Sphere3D } from 'mol-math/geometry';
 import { Vec3 } from 'mol-math/linear-algebra';
 import { ValueCell } from 'mol-util';
+import { idFactory } from 'mol-util/id-factory';
+
+const getNextRenderableId = idFactory()
 
 export type RenderableState = {
     visible: boolean
@@ -18,10 +21,12 @@ export type RenderableState = {
 }
 
 export interface Renderable<T extends RenderableValues> {
+    readonly id: number
     readonly values: T
     readonly state: RenderableState
     readonly boundingSphere: Sphere3D
     readonly invariantBoundingSphere: Sphere3D
+    readonly z: number
 
     render: (variant: RenderVariant) => void
     getProgram: (variant: RenderVariant) => Program
@@ -34,6 +39,7 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem:
     const invariantBoundingSphere: Sphere3D = Sphere3D.create(Vec3.zero(), 0)
 
     return {
+        id: getNextRenderableId(),
         values,
         state,
         get boundingSphere () {
@@ -48,6 +54,9 @@ export function createRenderable<T extends Values<RenderableSchema>>(renderItem:
             }
             return invariantBoundingSphere
         },
+        get z () {
+            return boundingSphere.center[2]
+        },
 
         render: (variant: RenderVariant) => {
             if (values.uPickable) {

+ 25 - 14
src/mol-gl/renderer.ts

@@ -107,10 +107,9 @@ namespace Renderer {
         }
 
         let globalUniformsNeedUpdate = true
-        const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: RenderVariant, opaque: boolean) => {
-            if (r.state.opaque !== opaque) return
+        const renderObject = (r: Renderable<RenderableValues & BaseValues>, variant: RenderVariant) => {
             const program = r.getProgram(variant)
-            if (r.state.visible) {                
+            if (r.state.visible) {
                 if (ctx.currentProgramId !== program.id) {
                     globalUniformsNeedUpdate = true
                 }
@@ -146,8 +145,6 @@ namespace Renderer {
                     gl.cullFace(gl.BACK)
                 }
 
-                gl.depthMask(r.state.opaque)
-
                 r.render(variant)
             }
         }
@@ -170,16 +167,30 @@ namespace Renderer {
 
             const { renderables } = scene
 
-            gl.disable(gl.BLEND)
-            gl.enable(gl.DEPTH_TEST)
-            for (let i = 0, il = renderables.length; i < il; ++i) {
-                renderObject(renderables[i], variant, true)
-            }
+            if (variant === 'draw') {
+                gl.disable(gl.BLEND)
+                gl.enable(gl.DEPTH_TEST)
+                gl.depthMask(true)
+                for (let i = 0, il = renderables.length; i < il; ++i) {
+                    const r = renderables[i]
+                    if (r.state.opaque) renderObject(r, variant)
+                }
 
-            gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
-            gl.enable(gl.BLEND)
-            for (let i = 0, il = renderables.length; i < il; ++i) {
-                renderObject(renderables[i], variant, false)
+                gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
+                gl.enable(gl.BLEND)
+                gl.depthMask(false)
+                for (let i = 0, il = renderables.length; i < il; ++i) {
+                    const r = renderables[i]
+                    if (!r.state.opaque) renderObject(r, variant)
+                }
+            } else {
+                // picking
+                gl.disable(gl.BLEND)
+                gl.enable(gl.DEPTH_TEST)
+                gl.depthMask(true)
+                for (let i = 0, il = renderables.length; i < il; ++i) {
+                    renderObject(renderables[i], variant)
+                }
             }
 
             gl.finish()

+ 17 - 1
src/mol-gl/scene.ts

@@ -35,6 +35,21 @@ function calculateBoundingSphere(renderables: Renderable<RenderableValues & Base
     return boundingSphere;
 }
 
+function renderableSort(a: Renderable<any>, b: Renderable<any>) {
+    const drawProgramIdA = a.getProgram('draw').id
+    const drawProgramIdB = b.getProgram('draw').id
+
+    if (drawProgramIdA !== drawProgramIdB) {
+        return drawProgramIdA - drawProgramIdB; // sort by program id to minimize gl state changes
+    } else if (a.z !== b.z) {
+        return a.state.opaque
+            ? a.z - b.z // when opaque draw closer elements first to minimize overdraw
+            : b.z - a.z // when transparent draw elements last to maximize partial visibility
+    } else {
+        return a.id - b.id;
+    }
+}
+
 interface Scene extends Object3D {
     readonly count: number
     readonly renderables: ReadonlyArray<Renderable<RenderableValues & BaseValues>>
@@ -70,11 +85,11 @@ namespace Scene {
                 }
                 if (!keepBoundingSphere) boundingSphereDirty = true
             },
-
             add: (o: RenderObject) => {
                 if (!renderableMap.has(o)) {
                     const renderable = createRenderable(ctx, o)
                     renderables.push(renderable)
+                    renderables.sort(renderableSort)
                     renderableMap.set(o, renderable)
                     boundingSphereDirty = true
                 } else {
@@ -86,6 +101,7 @@ namespace Scene {
                 if (renderable) {
                     renderable.dispose()
                     renderables.splice(renderables.indexOf(renderable), 1)
+                    renderables.sort(renderableSort)
                     renderableMap.delete(o)
                     boundingSphereDirty = true
                 }