Explorar el Código

wip, renderItem/representation in-place update

Alexander Rose hace 6 años
padre
commit
13f6ada72d

+ 31 - 0
src/mol-app/ui/transform/spacefill.tsx

@@ -30,6 +30,8 @@ export type ColorThemeInfo = keyof typeof ColorThemeInfo
 
 interface SpacefillState {
     doubleSided: boolean
+    flipSided: boolean
+    flatShaded: boolean
     detail: number
     colorTheme: ColorTheme
     colorValue: Color
@@ -41,6 +43,8 @@ interface SpacefillState {
 export class Spacefill extends View<Controller<any>, SpacefillState, { transform: SpacefillUpdate, entity: SpacefillEntity, ctx: StateContext }> {
     state = {
         doubleSided: true,
+        flipSided: false,
+        flatShaded: false,
         detail: 2,
         colorTheme: { name: 'element-symbol' } as ColorTheme,
         colorValue: 0x000000,
@@ -160,6 +164,33 @@ export class Spacefill extends View<Controller<any>, SpacefillState, { transform
                                 />
                             </div>
                         </div>
+                        <div className='molstar-control-row molstar-options-group'>
+                            <div>
+                                <Toggle
+                                    value={this.state.doubleSided}
+                                    label='Double sided'
+                                    onChange={value => this.update({ doubleSided: value })}
+                                />
+                            </div>
+                        </div>
+                        <div className='molstar-control-row molstar-options-group'>
+                            <div>
+                                <Toggle
+                                    value={this.state.flipSided}
+                                    label='Flip sided'
+                                    onChange={value => this.update({ flipSided: value })}
+                                />
+                            </div>
+                        </div>
+                        <div className='molstar-control-row molstar-options-group'>
+                            <div>
+                                <Toggle
+                                    value={this.state.flatShaded}
+                                    label='Flat shaded'
+                                    onChange={value => this.update({ flatShaded: value })}
+                                />
+                            </div>
+                        </div>
                         <div className='molstar-control-row molstar-options-group'>
                             <div>
                                 <Slider

+ 1 - 0
src/mol-geo/representation/structure/index.ts

@@ -71,6 +71,7 @@ export function StructureRepresentation<P extends StructureProps>(reprCtor: () =
                     const { repr, group } = groupRepr
                     const state = { message: 'Updating structure unit representations...', current: i, max: il };
                     if (!await repr.update(props).runAsChild(ctx, state)) {
+                        console.log('update failed, need to rebuild')
                         await repr.create(group, props).runAsChild(ctx, state)
                     }
                     renderObjects.push(...repr.renderObjects)

+ 37 - 16
src/mol-geo/representation/structure/spacefill.ts

@@ -25,16 +25,18 @@ import { Mesh } from '../../shape/mesh';
 
 export const DefaultSpacefillProps = {
     ...DefaultStructureProps,
+    flipSided: false,
+    flatShaded: false,
     detail: 0,
 }
 export type SpacefillProps = Partial<typeof DefaultSpacefillProps>
 
-function createSpacefillMesh(unit: Unit, detail: number) {
+function createSpacefillMesh(unit: Unit, detail: number, mesh?: Mesh) {
     return Task.create('Sphere mesh', async ctx => {
         const { elements } = unit;
         const elementCount = elements.length;
         const vertexCount = elementCount * icosahedronVertexCount(detail)
-        const meshBuilder = MeshBuilder.create(vertexCount)
+        const meshBuilder = MeshBuilder.create(vertexCount, vertexCount / 2, mesh)
 
         let radius: Element.Property<number>
         if (Unit.isAtomic(unit)) {
@@ -68,7 +70,9 @@ function createSpacefillMesh(unit: Unit, detail: number) {
             }
         }
 
-        return meshBuilder.getMesh()
+        const _mesh = meshBuilder.getMesh()
+        console.log(_mesh)
+        return _mesh
     })
 }
 
@@ -78,6 +82,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
     let currentProps: typeof DefaultSpacefillProps
     let mesh: Mesh
     let currentGroup: Unit.SymmetryGroup
+    let vertexMap: VertexMap
 
     return {
         renderObjects,
@@ -92,8 +97,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
 
                 mesh = await createSpacefillMesh(group.units[0], detail).runAsChild(ctx, 'Computing spacefill mesh')
                 // console.log(mesh)
-
-                const vertexMap = VertexMap.fromMesh(mesh)
+                vertexMap = VertexMap.fromMesh(mesh)
 
                 await ctx.update('Computing spacefill transforms');
                 const transforms = createTransforms(group)
@@ -120,8 +124,8 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
                     instanceCount: ValueCell.create(instanceCount),
 
                     dDoubleSided: ValueCell.create(defaults(props.doubleSided, true)),
-                    dFlatShaded: ValueCell.create(false),
-                    dFlipSided: ValueCell.create(false),
+                    dFlatShaded: ValueCell.create(defaults(props.flatShaded, false)),
+                    dFlipSided: ValueCell.create(defaults(props.flipSided, false)),
                 }
                 const state: RenderableState = {
                     depthMask: defaults(props.depthMask, true),
@@ -137,22 +141,39 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
 
             return Task.create('Spacefill.update', async ctx => {
                 if (!spheres) return false
-                // if (newProps.detail !== currentProps.detail) return false
-                if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) return false
+
+                let updateColor = false
 
                 if (newProps.detail !== currentProps.detail) {
-                    await createSpacefillMesh(currentGroup.units[0], newProps.detail).runAsChild(ctx, 'Computing spacefill mesh')
-                    const vertexMap = VertexMap.fromMesh(mesh)
+                    mesh = await createSpacefillMesh(currentGroup.units[0], newProps.detail, mesh).runAsChild(ctx, 'Computing spacefill mesh')
+                    ValueCell.update(spheres.values.drawCount, mesh.triangleCount * 3)
+                    // TODO update in-place
+                    vertexMap = VertexMap.fromMesh(mesh)
+                    updateColor = true
+                }
 
-                    await ctx.update('Computing spacefill transforms');
-                    createTransforms(currentGroup)
+                if (!deepEqual(newProps.colorTheme, currentProps.colorTheme)) {
+                    updateColor = true
+                }
 
+                if (updateColor) {
                     await ctx.update('Computing spacefill colors');
-                    createColors(currentGroup, vertexMap, newProps.colorTheme)
+                    createColors(currentGroup, vertexMap, newProps.colorTheme, spheres.values)
                 }
 
-                ValueCell.update(spheres.values.uAlpha, newProps.alpha)
-                ValueCell.update(spheres.values.dDoubleSided, newProps.doubleSided)
+                // TODO handle in a generic way
+                if (spheres.values.uAlpha.ref.value !== newProps.alpha) {
+                    ValueCell.update(spheres.values.uAlpha, newProps.alpha)
+                }
+                if (spheres.values.dDoubleSided.ref.value !== newProps.doubleSided) {
+                    ValueCell.update(spheres.values.dDoubleSided, newProps.doubleSided)
+                }
+                if (spheres.values.dFlipSided.ref.value !== newProps.flipSided) {
+                    ValueCell.update(spheres.values.dFlipSided, newProps.flipSided)
+                }
+                if (spheres.values.dFlatShaded.ref.value !== newProps.flatShaded) {
+                    ValueCell.update(spheres.values.dFlatShaded, newProps.flatShaded)
+                }
                 spheres.state.visible = newProps.visible
                 spheres.state.depthMask = newProps.depthMask
 

+ 11 - 7
src/mol-geo/shape/mesh-builder.ts

@@ -92,19 +92,23 @@ export namespace MeshBuilder {
             },
             getMesh: () => {
                 ChunkedArray.add(offsets, vertices.elementCount)
-                const mesh = {
+                const vb = ChunkedArray.compact(vertices, true) as Float32Array
+                const ib = ChunkedArray.compact(indices, true) as Uint32Array
+                const nb = ChunkedArray.compact(normals, true) as Float32Array
+                const idb = ChunkedArray.compact(ids, true) as Float32Array
+                const ob = ChunkedArray.compact(offsets, true) as Uint32Array
+                return {
                     vertexCount: vertices.elementCount,
                     triangleCount: indices.elementCount,
                     offsetCount: offsets.elementCount,
-                    vertexBuffer: ValueCell.create(ChunkedArray.compact(vertices, true) as Float32Array),
-                    indexBuffer: ValueCell.create(ChunkedArray.compact(indices, true) as Uint32Array),
-                    normalBuffer: ValueCell.create(ChunkedArray.compact(normals, true) as Float32Array),
-                    idBuffer: ValueCell.create(ChunkedArray.compact(ids, true) as Float32Array),
-                    offsetBuffer: ValueCell.create(ChunkedArray.compact(offsets, true) as Uint32Array),
+                    vertexBuffer: mesh ? ValueCell.update(mesh.vertexBuffer, vb) : ValueCell.create(vb),
+                    indexBuffer: mesh ? ValueCell.update(mesh.indexBuffer, ib) : ValueCell.create(ib),
+                    normalBuffer: mesh ? ValueCell.update(mesh.normalBuffer, nb) : ValueCell.create(nb),
+                    idBuffer: mesh ? ValueCell.update(mesh.idBuffer, idb) : ValueCell.create(idb),
+                    offsetBuffer: mesh ? ValueCell.update(mesh.offsetBuffer, ob) : ValueCell.create(ob),
                     normalsComputed: true,
                     offsetsComputed: true,
                 }
-                return mesh
             }
         }
     }

+ 14 - 6
src/mol-geo/util/color-data.ts

@@ -28,7 +28,9 @@ export interface UniformColorProps {
 export function createUniformColor(props: UniformColorProps, colorData?: ColorData): ColorData {
     if (colorData) {
         ValueCell.update(colorData.uColor, Color.toRgbNormalized(props.value) as Vec3)
-        ValueCell.update(colorData.dColorType, 'uniform')
+        if (colorData.dColorType.ref.value !== 'uniform') {
+            ValueCell.update(colorData.dColorType, 'uniform')
+        }
         return colorData
     } else {
         return {
@@ -60,8 +62,11 @@ export function createAttributeColor(props: AttributeColorProps, colorData?: Col
         }
     }
     if (colorData) {
+        console.log('update colordata attribute')
         ValueCell.update(colorData.aColor, colors)
-        ValueCell.update(colorData.dColorType, 'attribute')
+        if (colorData.dColorType.ref.value !== 'attribute') {
+            ValueCell.update(colorData.dColorType, 'attribute')
+        }
         return colorData
     } else {
         return {
@@ -76,9 +81,12 @@ export function createAttributeColor(props: AttributeColorProps, colorData?: Col
 
 export function createTextureColor(colors: TextureImage, type: ColorType, colorData?: ColorData): ColorData {
     if (colorData) {
+        console.log('update colordata texture')
         ValueCell.update(colorData.tColor, colors)
         ValueCell.update(colorData.uColorTexSize, Vec2.create(colors.width, colors.height))
-        ValueCell.update(colorData.dColorType, type)
+        if (colorData.dColorType.ref.value !== type) {
+            ValueCell.update(colorData.dColorType, type)
+        }
         return colorData
     } else {
         return {
@@ -103,7 +111,7 @@ export function createInstanceColor(props: InstanceColorProps, colorData?: Color
     for (let i = 0; i < instanceCount; i++) {
         Color.toArray(colorFn(i), colors.array, i * 3)
     }
-    return createTextureColor(colors, 'instance')
+    return createTextureColor(colors, 'instance', colorData)
 }
 
 export interface ElementColorProps {
@@ -119,7 +127,7 @@ export function createElementColor(props: ElementColorProps, colorData?: ColorDa
     for (let i = 0, il = elementCount; i < il; ++i) {
         Color.toArray(colorFn(i), colors.array, i * 3)
     }
-    return createTextureColor(colors, 'element')
+    return createTextureColor(colors, 'element', colorData)
 }
 
 export interface ElementInstanceColorProps {
@@ -141,7 +149,7 @@ export function createElementInstanceColor(props: ElementInstanceColorProps, col
             colorOffset += 3
         }
     }
-    return createTextureColor(colors, 'elementInstance')
+    return createTextureColor(colors, 'elementInstance', colorData)
 }
 
 /** Create color attribute or texture, depending on the vertexMap */

+ 7 - 0
src/mol-gl/webgl/buffer.ts

@@ -7,6 +7,9 @@
 import { Context } from './context'
 import { ValueCell } from 'mol-util';
 import { RenderableSchema } from '../renderable/schema';
+import { idFactory } from 'mol-util/id-factory';
+
+const getNextBufferId = idFactory()
 
 export type UsageHint = 'static' | 'dynamic' | 'stream'
 export type DataType = 'uint8' | 'int8' | 'uint16' | 'int16' | 'uint32' | 'int32' | 'float32'
@@ -78,6 +81,8 @@ export function getBufferType(ctx: Context, bufferType: BufferType) {
 }
 
 export interface Buffer {
+    readonly id: number
+
     readonly _buffer: WebGLBuffer
     readonly _usageHint: number
     readonly _bufferType: number
@@ -117,6 +122,8 @@ export function createBuffer(ctx: Context, array: ArrayType, itemSize: BufferIte
     ctx.bufferCount += 1
 
     return {
+        id: getNextBufferId(),
+
         _buffer,
         _usageHint,
         _bufferType,

+ 124 - 38
src/mol-gl/webgl/render-item.ts

@@ -5,12 +5,15 @@
  */
 
 import { UniformValues } from './uniform';
-import { AttributeValues, createAttributeBuffers, createElementsBuffer, ElementsBuffer } from './buffer';
+import { AttributeValues, createAttributeBuffers, createElementsBuffer, ElementsBuffer, createAttributeBuffer, ArrayKind, AttributeBuffers } from './buffer';
 import { TextureValues, createTextures } from './texture';
 import { Context } from './context';
 import { ShaderCode, addShaderDefines, DefineValues } from '../shader-code';
 import { Program } from './program';
-import { RenderableSchema, RenderableValues } from '../renderable/schema';
+import { RenderableSchema, RenderableValues, AttributeSpec } from '../renderable/schema';
+import { idFactory } from 'mol-util/id-factory';
+
+const getNextRenderItemId = idFactory()
 
 export type DrawMode = 'points' | 'lines' | 'line-strip' | 'line-loop' | 'triangles' | 'triangle-strip' | 'triangle-fan'
 
@@ -41,7 +44,39 @@ function splitValues(schema: RenderableSchema, values: RenderableValues) {
     return { attributeValues, defineValues, textureValues, uniformValues }
 }
 
+type Versions<T extends RenderableValues> = { [k in keyof T]: number }
+function getValueVersions<T extends RenderableValues>(values: T) {
+    const versions: Versions<any> = {}
+    Object.keys(values).forEach(k => {
+        versions[k] = values[k].ref.version
+    })
+    return versions as Versions<T>
+}
+
+function createVertexArray(ctx: Context, program: Program, attributeBuffers: AttributeBuffers, elementsBuffer?: ElementsBuffer) {
+    const { oesVertexArrayObject } = ctx.extensions
+    let vertexArray: WebGLVertexArrayObjectOES | undefined = undefined
+    if (oesVertexArrayObject) {
+        vertexArray = oesVertexArrayObject.createVertexArrayOES()
+        oesVertexArrayObject.bindVertexArrayOES(vertexArray)
+        program.bindAttributes(attributeBuffers)
+        if (elementsBuffer) elementsBuffer.bind()
+        ctx.vaoCount += 1
+        oesVertexArrayObject.bindVertexArrayOES(null!)
+    }
+    return vertexArray
+}
+
+function deleteVertexArray(ctx: Context, vertexArray?: WebGLVertexArrayObjectOES) {
+    const { oesVertexArrayObject } = ctx.extensions
+    if (oesVertexArrayObject && vertexArray) {
+        oesVertexArrayObject.deleteVertexArrayOES(vertexArray)
+        ctx.vaoCount -= 1
+    }
+}
+
 export interface RenderItem {
+    readonly id: number
     readonly programId: number
     readonly program: Program
 
@@ -51,40 +86,30 @@ export interface RenderItem {
 }
 
 export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: ShaderCode, schema: RenderableSchema, values: RenderableValues): RenderItem {
+    const id = getNextRenderItemId()
     const { programCache } = ctx
     const { angleInstancedArrays, oesVertexArrayObject } = ctx.extensions
 
     const { attributeValues, defineValues, textureValues, uniformValues } = splitValues(schema, values)
+    const versions = getValueVersions(values)
 
     const glDrawMode = getDrawMode(ctx, drawMode)
-    const programRef = programCache.get(ctx, {
+    let programRef = programCache.get(ctx, {
         shaderCode: addShaderDefines(defineValues, shaderCode),
         schema
     })
-    const program = programRef.value
+    let program = programRef.value
 
     const textures = createTextures(ctx, schema, textureValues)
     const attributeBuffers = createAttributeBuffers(ctx, schema, attributeValues)
 
+    let elementsBuffer: ElementsBuffer | undefined
     const elements = values.elements
-
-    let vertexArray: WebGLVertexArrayObjectOES
-    if (oesVertexArrayObject) {
-        vertexArray = oesVertexArrayObject.createVertexArrayOES()
-        oesVertexArrayObject.bindVertexArrayOES(vertexArray)
-        program.bindAttributes(attributeBuffers)
-        ctx.vaoCount += 1
-    }
-
-    let elementsBuffer: ElementsBuffer
     if (elements && elements.ref.value) {
         elementsBuffer = createElementsBuffer(ctx, elements.ref.value)
     }
 
-    // needs to come after elements buffer creation to include it in the vao
-    if (oesVertexArrayObject) {
-        oesVertexArrayObject.bindVertexArrayOES(null!)
-    }
+    let vertexArray: WebGLVertexArrayObjectOES | undefined = createVertexArray(ctx, program, attributeBuffers, elementsBuffer)
 
     let drawCount = values.drawCount.ref
     let instanceCount = values.instanceCount.ref
@@ -92,16 +117,17 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S
     let destroyed = false
 
     return {
-        programId: program.id,
-        program,
+        id,
+        get programId () { return program.id },
+        get program () { return program },
 
         draw: () => {
             program.setUniforms(uniformValues)
-            if (oesVertexArrayObject) {
+            if (oesVertexArrayObject && vertexArray) {
                 oesVertexArrayObject.bindVertexArrayOES(vertexArray)
             } else {
                 program.bindAttributes(attributeBuffers)
-                elementsBuffer.bind()
+                if (elementsBuffer) elementsBuffer.bind()
             }
             program.bindTextures(textures)
             if (elementsBuffer) {
@@ -111,6 +137,31 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S
             }
         },
         update: () => {
+            let defineChange = false
+            Object.keys(defineValues).forEach(k => {
+                const value = defineValues[k]
+                if (value.ref.version === versions[k]) {
+                    // console.log('define version unchanged', k)
+                } else {
+                    console.log('define version changed', k)
+                    defineChange = true
+                    versions[k] = value.ref.version
+                }
+            })
+
+            if (defineChange) {
+                console.log('some defines changed, need to rebuild program')
+                programRef.free()
+                // programCache.clear()
+                // console.log('programCache.count', programCache.count)
+                programRef = programCache.get(ctx, {
+                    shaderCode: addShaderDefines(defineValues, shaderCode),
+                    schema
+                })
+                program = programRef.value
+            }
+
+            console.log('RenderItem.update', id, values)
             if (values.drawCount.ref.version !== drawCount.version) {
                 console.log('drawCount version changed')
                 drawCount = values.drawCount.ref
@@ -120,29 +171,64 @@ export function createRenderItem(ctx: Context, drawMode: DrawMode, shaderCode: S
                 instanceCount = values.instanceCount.ref
             }
 
-            // Object.keys(attributeValues).forEach(k => {
-            //     const value = attributeValues[k]
-            //     if (value === undefined) return
-            //     const buffer = attributeBuffers[k]
-            //     if (buffer.length >= value.length) {
-            //         attributeBuffers[k].updateData(value)
-            //     } else {
+            let bufferChange = false
+
+            Object.keys(attributeValues).forEach(k => {
+                const value = attributeValues[k]
+                if (value.ref.version === versions[k]) {
+                    // console.log('attribute version unchanged', k)
+                    return
+                }
+                const buffer = attributeBuffers[k]
+                if (buffer.length >= value.ref.value.length) {
+                    console.log('attribute array large enough to update', k)
+                    attributeBuffers[k].updateData(value.ref.value)
+                } else {
+                    console.log('attribute array to small, need to create new attribute', k)
+                    attributeBuffers[k].destroy()
+                    const spec = schema[k] as AttributeSpec<ArrayKind>
+                    attributeBuffers[k] = createAttributeBuffer(ctx, value.ref.value, spec.itemSize, spec.divisor)
+                    bufferChange = true
+                }
+                versions[k] = value.ref.version
+            })
+
+            if (elementsBuffer && values.elements.ref.version !== versions.elements) {
+                if (elementsBuffer.length >= values.elements.ref.value.length) {
+                    console.log('elements array large enough to update')
+                    elementsBuffer.updateData(values.elements.ref.value)
+                } else {
+                    console.log('elements array to small, need to create new elements')
+                    elementsBuffer.destroy()
+                    elementsBuffer = createElementsBuffer(ctx, values.elements.ref.value)
+                    bufferChange = true
+                }
+                versions.elements = values.elements.ref.version
+            }
+
+            if (defineChange || bufferChange) {
+                console.log('program/defines or buffers changed, rebuild vao')
+                deleteVertexArray(ctx, vertexArray)
+                vertexArray = createVertexArray(ctx, program, attributeBuffers, elementsBuffer)
+            }
 
-            //     }
-            // })
+            Object.keys(textureValues).forEach(k => {
+                const value = textureValues[k]
+                if (value.ref.version === versions[k]) {
+                    // console.log('texture version unchanged', k)
+                    return
+                }
+                console.log('texture version changed, uploading image', k)
+                textures[k].load(value.ref.value)
+            })
         },
         destroy: () => {
             if (destroyed) return
             programRef.free()
             Object.keys(textures).forEach(k => textures[k].destroy())
             Object.keys(attributeBuffers).forEach(k => attributeBuffers[k].destroy())
-            if (elements) {
-                elementsBuffer.destroy()
-            }
-            if (oesVertexArrayObject) {
-                oesVertexArrayObject.deleteVertexArrayOES(vertexArray)
-                ctx.vaoCount -= 1
-            }
+            if (elementsBuffer) elementsBuffer.destroy()
+            deleteVertexArray(ctx, vertexArray)
             destroyed = true
         }
     }

+ 5 - 0
src/mol-gl/webgl/shader.ts

@@ -6,6 +6,9 @@
 
 import { createReferenceCache, ReferenceCache } from 'mol-util/reference-cache';
 import { Context } from './context';
+import { idFactory } from 'mol-util/id-factory';
+
+const getNextShaderId = idFactory()
 
 function addLineNumbers(source: string) {
     const lines = source.split('\n')
@@ -18,6 +21,7 @@ function addLineNumbers(source: string) {
 export type ShaderType = 'vert' | 'frag'
 export interface ShaderProps { type: ShaderType, source: string }
 export interface Shader {
+    readonly id: number
     attach: (program: WebGLProgram) => void
     destroy: () => void
 }
@@ -40,6 +44,7 @@ function createShader(ctx: Context, props: ShaderProps): Shader {
     }
 
     return {
+        id: getNextShaderId(),
         attach: (program: WebGLProgram) => {
             gl.attachShader(program, shader)
         },

+ 3 - 2
src/mol-gl/webgl/texture.ts

@@ -12,7 +12,7 @@ import { idFactory } from 'mol-util/id-factory';
 
 const getNextTextureId = idFactory()
 export interface Texture {
-    id: number
+    readonly id: number
     load: (image: TextureImage) => void
     bind: (id: TextureId) => void
     unbind: (id: TextureId) => void
@@ -25,6 +25,7 @@ export type TextureValues = { [k: string]: ValueCell<TextureImage> }
 export type Textures = { [k: string]: Texture }
 
 export function createTexture(ctx: Context): Texture {
+    const id = getNextTextureId()
     const { gl } = ctx
     const texture = gl.createTexture()
     if (texture === null) {
@@ -41,7 +42,7 @@ export function createTexture(ctx: Context): Texture {
     ctx.textureCount += 1
 
     return {
-        id: getNextTextureId(),
+        id,
         load: (image: TextureImage) => {
             const { array, width, height } = image
             gl.bindTexture(_textureType, texture)

+ 1 - 1
src/mol-view/state/transform.ts

@@ -116,7 +116,7 @@ export const SpacefillUpdate: SpacefillUpdate = StateTransform.create('spacefill
         const spacefillRepr = spacefillEntity.value
         await spacefillRepr.update(props).run(ctx.log)
         ctx.viewer.add(spacefillRepr)
-        ctx.viewer.update()
+        // ctx.viewer.update()
         ctx.viewer.requestDraw()
         console.log('stats', ctx.viewer.stats)
         return NullEntity