arose 7 gadi atpakaļ
vecāks
revīzija
55257e953b

+ 7 - 7
src/mol-geo/util/color-data.ts

@@ -5,15 +5,15 @@
  */
 
 import { ValueCell } from 'mol-util';
-import { Texture, createColorTexture } from 'mol-gl/renderable/util';
+import { TextureImage, createColorTexture } from 'mol-gl/renderable/util';
 import { Color } from 'mol-util/color';
 import VertexMap from '../shape/vertex-map';
 
 export type UniformColor = { type: 'uniform', value: number[] }
 export type AttributeColor = { type: 'attribute', value: ValueCell<Float32Array> }
-export type InstanceColor = { type: 'instance', value: ValueCell<Texture> }
-export type ElementColor = { type: 'element', value: ValueCell<Texture> }
-export type ElementInstanceColor = { type: 'element-instance', value: ValueCell<Texture> }
+export type InstanceColor = { type: 'instance', value: ValueCell<TextureImage> }
+export type ElementColor = { type: 'element', value: ValueCell<TextureImage> }
+export type ElementInstanceColor = { type: 'element-instance', value: ValueCell<TextureImage> }
 export type ColorData = UniformColor | AttributeColor | InstanceColor | ElementColor | ElementInstanceColor
 
 export interface UniformColorProps {
@@ -56,7 +56,7 @@ export function createInstanceColor(props: InstanceColorProps): InstanceColor {
     const { colorFn, instanceCount} = props
     const colors = createColorTexture(instanceCount)
     for (let i = 0; i < instanceCount; i++) {
-        Color.toArray(colorFn(i), colors, i * 3)
+        Color.toArray(colorFn(i), colors.array, i * 3)
     }
     return { type: 'instance', value: ValueCell.create(colors) }
 }
@@ -72,7 +72,7 @@ export function createElementColor(props: ElementColorProps): ElementColor {
     const elementCount = vertexMap.offsetCount - 1
     const colors = createColorTexture(elementCount)
     for (let i = 0, il = elementCount; i < il; ++i) {
-        Color.toArray(colorFn(i), colors, i * 3)
+        Color.toArray(colorFn(i), colors.array, i * 3)
     }
     return { type: 'element', value: ValueCell.create(colors) }
 }
@@ -92,7 +92,7 @@ export function createElementInstanceColor(props: ElementInstanceColorProps): El
     let colorOffset = 0
     for (let i = 0; i < instanceCount; i++) {
         for (let j = 0, jl = elementCount; j < jl; ++j) {
-            Color.toArray(colorFn(i, j), colors, colorOffset)
+            Color.toArray(colorFn(i, j), colors.array, colorOffset)
             colorOffset += 3
         }
     }

+ 3 - 1
src/mol-gl/_spec/renderer.spec.ts

@@ -15,6 +15,7 @@ import { createPointRenderObject } from '../scene';
 import { fillSerial } from '../renderable/util';
 import { createUniformColor } from 'mol-geo/util/color-data';
 import { createUniformSize } from 'mol-geo/util/size-data';
+import { createContext } from '../webgl/context';
 
 function writeImage(gl: WebGLRenderingContext, width: number, height: number) {
     const pixels = new Uint8Array(width * height * 4)
@@ -28,12 +29,13 @@ function writeImage(gl: WebGLRenderingContext, width: number, height: number) {
 }
 
 function createRenderer(gl: WebGLRenderingContext) {
+    const ctx = createContext(gl)
     const camera = PerspectiveCamera.create({
         near: 0.01,
         far: 10000,
         position: Vec3.create(0, 0, 50)
     })
-    return Renderer.create(gl, camera)
+    return Renderer.create(ctx, camera)
 }
 
 function createPoints() {

+ 0 - 12
src/mol-gl/context.ts

@@ -1,12 +0,0 @@
-/**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Alexander Rose <alexander.rose@weirdbyte.de>
- */
-
-import REGL = require('regl');
-import { InitializationOptions } from 'regl'
-
-export function create(params: InitializationOptions) {
-    return REGL(params)
-}

+ 3 - 59
src/mol-gl/renderable.ts

@@ -6,68 +6,12 @@
 
 import PointRenderable from './renderable/point'
 import MeshRenderable from './renderable/mesh'
-import { Shaders } from './shaders';
-import { UniformDefs, UniformValues } from './webgl/uniform';
-import { AttributeDefs, AttributeValues, createAttributeBuffers } from './webgl/buffer';
-import { TextureDefs, TextureValues, createTextures } from './webgl/texture';
-import { Context } from './webgl/context';
-import { createProgram } from './webgl/program';
-
-export type RenderableProps = {
-    shaders: Shaders
-    uniform: UniformDefs
-    attribute: AttributeDefs
-    texture: TextureDefs
-}
-
-export type RenderableState<T extends RenderableProps> = {
-    uniform: UniformValues<T['uniform']>
-    attribute: AttributeValues<T['attribute']>
-    texture: TextureValues<T['texture']>
-
-    drawCount: number
-}
-
-export interface Renderable<T extends RenderableProps> {
-    readonly hash: string
-    readonly programId: number
-
-    loadAttributes: (state: Partial<AttributeValues<T['attribute']>>) => void
 
+export interface Renderable<T> {
     draw: () => void
+    name: string
+    update: (newProps: T) => void
     dispose: () => void
 }
 
-export function createRenderable<T extends RenderableProps>(ctx: Context, props: T, state: RenderableState<T>): Renderable<T> {
-    const { gl } = ctx
-    const hash = JSON.stringify(props)
-    const program = createProgram(ctx, props.shaders, props.uniform, props.attribute, props.texture)
-    const attributeBuffers = createAttributeBuffers(ctx, props.attribute, state.attribute)
-    const textures = createTextures(gl, props.texture, state.texture)
-
-    function loadAttributes(state: Partial<AttributeValues<T['attribute']>>) {
-        Object.keys(state).forEach(k => {
-            const value = state[k]
-            if (value !== undefined) attributeBuffers[k].updateData(value)
-        })
-    }
-
-    return {
-        hash,
-        programId: program.id,
-
-        loadAttributes,
-
-        draw: () => {
-            program.setUniforms(state.uniform)
-            program.bindAttributes(attributeBuffers)
-            program.bindTextures(textures)
-            gl.drawArrays(gl.TRIANGLES, 0, state.drawCount);
-        },
-        dispose: () => {
-            // TODO
-        }
-    }
-}
-
 export { PointRenderable, MeshRenderable }

+ 22 - 29
src/mol-gl/renderable/mesh.ts

@@ -4,18 +4,19 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import REGL = require('regl');
 import { ValueCell } from 'mol-util/value-cell'
 import { ColorData } from 'mol-geo/util/color-data';
 
 import { Renderable } from '../renderable'
-import { createBaseDefines, createBaseUniforms, createBaseAttributes, destroyAttributes, destroyUniforms } from './util'
-import { MeshShaders, addDefines } from '../shaders'
+import { getBaseDefs, getBaseValues, getBaseDefines } from './util'
+import { MeshShaderCode, addShaderDefines } from '../shader-code'
+import { Context } from '../webgl/context';
+import { createRenderItem, RenderItemProps, RenderItemState } from '../webgl/render-item';
 
 type Mesh = 'mesh'
 
 namespace Mesh {
-    export type Data = {
+    export type Props = {
         objectId: number
 
         position: ValueCell<Float32Array>
@@ -32,39 +33,31 @@ namespace Mesh {
         positionCount: number
     }
 
-    export function create(regl: REGL.Regl, props: Data): Renderable {
-        const defines = createBaseDefines(regl, props)
-        const uniforms = createBaseUniforms(regl, props)
-        const attributes = createBaseAttributes(regl, props)
-        const elements = regl.elements({
-            data: props.index.ref.value,
-            primitive: 'triangles',
-            type: 'uint32',
-            count: props.indexCount * 3
-        })
+    export function create(ctx: Context, props: Props): Renderable<Props> {
+        const defs: RenderItemProps = {
+            ...getBaseDefs(props),
+            shaderCode: addShaderDefines(getBaseDefines(props), MeshShaderCode),
+            drawMode: 'triangles'
+        }
+        const values: RenderItemState = {
+            ...getBaseValues(props),
+            drawCount: props.indexCount * 3,
+            instanceCount: props.instanceCount
+        }
+
+        let renderItem = createRenderItem(ctx, defs, values)
+        // let curProps = props
 
-        const command = regl({
-            ...addDefines(defines, MeshShaders),
-            uniforms,
-            attributes,
-            elements,
-            instances: props.instanceCount,
-        })
         return {
             draw: () => {
-                command()
-            },
-            get stats() {
-                return command.stats
+                renderItem.draw()
             },
             name: 'mesh',
-            update: (newProps: Data) => {
+            update: (newProps: Props) => {
                 console.log('Updating mesh renderable')
             },
             dispose: () => {
-                destroyAttributes(attributes)
-                destroyUniforms(uniforms)
-                elements.destroy()
+                renderItem.dispose()
             }
         }
     }

+ 27 - 27
src/mol-gl/renderable/point.ts

@@ -4,19 +4,20 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import REGL = require('regl');
 import { ValueCell } from 'mol-util/value-cell'
 
 import { Renderable } from '../renderable'
-import { createBaseDefines, createBaseUniforms, createBaseAttributes, destroyUniforms, destroyAttributes, updateBaseUniforms } from './util'
-import { PointShaders, addDefines } from '../shaders'
+import { getBaseValues, getBaseDefs, getBaseDefines } from './util'
+import { PointShaderCode, addShaderDefines } from '../shader-code'
 import { ColorData } from 'mol-geo/util/color-data';
 import { SizeData } from 'mol-geo/util/size-data';
+import { Context } from '../webgl/context';
+import { createRenderItem, RenderItemState, RenderItemProps } from '../webgl/render-item';
 
 type Point = 'point'
 
 namespace Point {
-    export type Data = {
+    export type Props = {
         objectId: number
 
         position: ValueCell<Float32Array>
@@ -33,39 +34,38 @@ namespace Point {
         usePointSizeAttenuation?: boolean
     }
 
-    export function create(regl: REGL.Regl, props: Data): Renderable {
-        let curProps = props
+    export function create<T = Props>(ctx: Context, props: Props): Renderable<Props> {
+        const defines = getBaseDefines(props)
+        if (props.usePointSizeAttenuation) defines.POINT_SIZE_ATTENUATION = ''
 
-        const defines = createBaseDefines(regl, props)
-        const uniforms = createBaseUniforms(regl, props)
-        const attributes = createBaseAttributes(regl, props)
+        const defs: RenderItemProps = {
+            ...getBaseDefs(props),
+            shaderCode: addShaderDefines(defines, PointShaderCode),
+            drawMode: 'points'
+        }
+        const values: RenderItemState = {
+            ...getBaseValues(props),
+            drawCount: props.positionCount,
+            instanceCount: props.instanceCount
+        }
 
-        if (props.usePointSizeAttenuation) defines.POINT_SIZE_ATTENUATION = ''
+        let renderItem = createRenderItem(ctx, defs, values)
+        // let curProps = props
 
-        const command = regl({
-            ...addDefines(defines, PointShaders),
-            uniforms,
-            attributes,
-            count: props.positionCount,
-            instances: props.instanceCount,
-            primitive: 'points'
-        })
         return {
-            draw: () => command(),
-            get stats() {
-                return command.stats
+            draw: () => {
+                renderItem.draw()
             },
-            name: 'point',
-            update: (newProps: Data) => {
+            name: 'mesh',
+            update: (newProps: Props) => {
                 console.log('Updating point renderable')
                 // const newUniforms = updateBaseUniforms(regl, uniforms, newProps, curProps)
-                const newUniforms = { ...uniforms, color: 0xFF4411 }
-                console.log(newUniforms)
+                // const newUniforms = { ...uniforms, color: 0xFF4411 }
+                // console.log(newUniforms)
                 // command({ uniforms: newUniforms })
             },
             dispose: () => {
-                destroyAttributes(attributes)
-                destroyUniforms(uniforms)
+                renderItem.dispose()
             }
         }
     }

+ 64 - 96
src/mol-gl/renderable/util.ts

@@ -4,20 +4,15 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import REGL = require('regl');
 import { ValueCell } from 'mol-util/value-cell'
 import { ColorData } from 'mol-geo/util/color-data';
 import { SizeData } from 'mol-geo/util/size-data';
 
-import { Attributes, AttributesData, AttributesBuffers } from '../renderable'
-import Attribute from '../attribute'
-import { ShaderDefines } from '../shaders';
+import { ShaderDefines } from '../shader-code';
 import { UniformDefs, UniformValues } from '../webgl/uniform';
-import { AttributeDefs } from '../webgl/buffer';
-
-
-export type ReglUniforms = { [k: string]: REGL.Uniform | REGL.Texture }
-export type ReglAttributes = { [k: string]: REGL.AttributeConfig }
+import { AttributeDefs, AttributeValues } from '../webgl/buffer';
+import { Vec3, Vec2 } from 'mol-math/linear-algebra';
+import { TextureDefs, TextureValues } from '../webgl/texture';
 
 export function calculateTextureInfo (n: number, itemSize: number) {
     const sqN = Math.sqrt(n * itemSize)
@@ -27,47 +22,15 @@ export function calculateTextureInfo (n: number, itemSize: number) {
     return { width, height, length: width * height * itemSize }
 }
 
-export interface Texture extends Uint8Array {
-    width: number,
+export interface TextureImage {
+    array: Uint8Array
+    width: number
     height: number
 }
 
-export function createColorTexture (n: number): Texture {
-    const colorTexInfo = calculateTextureInfo(n, 3)
-    const colorTexture = new Uint8Array(colorTexInfo.length)
-    return Object.assign(colorTexture, {
-        width: colorTexInfo.width,
-        height: colorTexInfo.height
-    })
-}
-
-export function createTransformAttributes (regl: REGL.Regl, transform: ValueCell<Float32Array>, count: number) {
-    const size = 4
-    const divisor = 1
-    const bpe = transform.ref.value.BYTES_PER_ELEMENT
-    const stride = 16 * bpe
-    return {
-        transformColumn0: Attribute.create(regl, transform, count, { size, divisor, offset: 0, stride }),
-        transformColumn1: Attribute.create(regl, transform, count, { size, divisor, offset: 4 * bpe, stride }),
-        transformColumn2: Attribute.create(regl, transform, count, { size, divisor, offset: 8 * bpe, stride }),
-        transformColumn3: Attribute.create(regl, transform, count, { size, divisor, offset: 12 * bpe, stride })
-    }
-}
-
-export function createColorUniforms (regl: REGL.Regl, color: ValueCell<Texture>) {
-    const colorTex = regl.texture({
-        width: color.ref.value.width,
-        height: color.ref.value.height,
-        format: 'rgb',
-        type: 'uint8',
-        wrapS: 'clamp',
-        wrapT: 'clamp',
-        data: color.ref.value
-    })
-    return {
-        colorTex,
-        colorTexSize: [ color.ref.value.width, color.ref.value.height ]
-    }
+export function createColorTexture (n: number): TextureImage {
+    const { length, width, height } = calculateTextureInfo(n, 3)
+    return { array: new Uint8Array(length), width, height }
 }
 
 export function getColorDefines(color: ValueCell<ColorData>) {
@@ -91,14 +54,6 @@ export function getSizeDefines(size: ValueCell<SizeData>) {
     return defines
 }
 
-export function getBuffers<T extends AttributesData>(attributes: Attributes<T>): AttributesBuffers<T> {
-    const buffers: AttributesBuffers<any> = {}
-    for (const k of Object.keys(attributes)) {
-        buffers[k] = attributes[k].buffer
-    }
-    return buffers as AttributesBuffers<T>
-}
-
 export function fillSerial<T extends Helpers.NumberArray> (array: T) {
     const n = array.length
     for (let i = 0; i < n; ++i) array[ i ] = i
@@ -146,16 +101,16 @@ export function getBaseUniformDefs(props: BaseProps) {
 
 export function getBaseUniformValues(props: BaseProps) {
     const { objectId, instanceCount, elementCount } = props
-    const uniformValues: UniformValues<any> = {
+    const uniformValues: UniformValues = {
         objectId, instanceCount, elementCount
     }
     const color = props.color.ref.value
     if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') {
         const { width, height } = color.value.ref.value
-        uniformValues.colorTex = new ImageData(new Uint8ClampedArray(color.value.ref.value), width, height)
-        uniformValues.colorTexSize = [ width, height ]
+        uniformValues.colorTex = new ImageData(new Uint8ClampedArray(color.value.ref.value.array), width, height)
+        uniformValues.colorTexSize = Vec2.create(width, height)
     } else if (color.type === 'uniform') {
-        uniformValues.color = color.value
+        uniformValues.color = color.value as Vec3
     }
     const size = props.size ? props.size.ref.value : undefined
     if (size && size.type === 'uniform') {
@@ -169,69 +124,82 @@ export function getBaseAttributeDefs(props: BaseProps) {
         instanceId: { kind: 'float32', itemSize: 1, divisor: 1 },
         position: { kind: 'float32', itemSize: 1, divisor: 0 },
         elementId: { kind: 'float32', itemSize: 1, divisor: 0 },
-        transformColumn0: { kind: 'float32', itemSize: 4, divisor: 1 },
-        transformColumn1: { kind: 'float32', itemSize: 4, divisor: 1 },
-        transformColumn2: { kind: 'float32', itemSize: 4, divisor: 1 },
-        transformColumn3: { kind: 'float32', itemSize: 4, divisor: 1 },
+        transform: { kind: 'float32', itemSize: 16, divisor: 1 },
+    }
+    if (props.normal) {
+        attributeDefs.normal = { kind: 'float32', itemSize: 3, divisor:0 }
     }
     const color = props.color.ref.value
-    if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') {
-        uniformDefs.colorTexSize = 'v2'
-        uniformDefs.colorTex = 't2'
-    } else if (color.type === 'uniform') {
-        uniformDefs.color = 'v3'
+    if (color.type === 'attribute') {
+        attributeDefs.color = { kind: 'float32', itemSize: 3, divisor:0 }
     }
     const size = props.size ? props.size.ref.value : undefined
-    if (size && size.type === 'uniform') {
-        uniformDefs.size = 'f'
+    if (size && size.type === 'attribute') {
+        attributeDefs.size = { kind: 'float32', itemSize: 1, divisor:0 }
     }
     return attributeDefs
 }
 
-export function createBaseAttributes(regl: REGL.Regl, props: BaseProps): ReglAttributes {
-    const { instanceCount, positionCount, position, id, normal, transform } = props
+export function getBaseAttributeValues(props: BaseProps) {
+    const { instanceCount, position, id, normal, transform } = props
     const instanceId = ValueCell.create(fillSerial(new Float32Array(instanceCount)))
-    const attributes = getBuffers({
-        instanceId: Attribute.create(regl, instanceId, instanceCount, { size: 1, divisor: 1 }),
-        position: Attribute.create(regl, position, positionCount, { size: 3 }),
-        elementId: Attribute.create(regl, id, positionCount, { size: 1 }),
-        ...createTransformAttributes(regl, transform, instanceCount)
-    })
+    const attributeValues: AttributeValues = {
+        instanceId: instanceId.ref.value,
+        position: position.ref.value,
+        elementId: id.ref.value,
+        transform: transform.ref.value
+    }
     if (normal) {
-        attributes.normal = Attribute.create(regl, normal as any, positionCount, { size: 3 }).buffer
+        attributeValues.normal = normal.ref.value
     }
     const color = props.color.ref.value
     if (color.type === 'attribute') {
-        attributes.color = Attribute.create(regl, color.value, positionCount, { size: 3 }).buffer
+        attributeValues.color = color.value.ref.value
     }
     const size = props.size ? props.size.ref.value : undefined
     if (size && size.type === 'attribute') {
-        attributes.size = Attribute.create(regl, size.value, positionCount, { size: 1 }).buffer
+        attributeValues.size = size.value.ref.value
+    }
+    return attributeValues
+}
+
+export function getBaseTextureDefs(props: BaseProps) {
+    const textureDefs: TextureDefs = {}
+    const color = props.color.ref.value
+    if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') {
+        textureDefs.colorTex = true
     }
-    return attributes
+    return textureDefs
 }
 
-export function createBaseDefines(regl: REGL.Regl, props: BaseProps): ShaderDefines {
+export function getBaseTextureValues(props: BaseProps) {
+    const textureValues: TextureValues = {}
+    const color = props.color.ref.value
+    if (color.type === 'instance' || color.type === 'element' || color.type === 'element-instance') {
+        textureValues.colorTex = color.value.ref.value
+    }
+    return textureValues
+}
+
+export function getBaseDefines(props: BaseProps): ShaderDefines {
     return {
         ...getColorDefines(props.color),
         ...(props.size ? getSizeDefines(props.size) : undefined)
     }
 }
 
-export function destroyAttributes(attributes: ReglAttributes) {
-    for (const k in attributes) {
-        const buffer = attributes[k].buffer
-        if (buffer) {
-            buffer.destroy()
-        }
+export function getBaseDefs(props: BaseProps) {
+    return {
+        uniformDefs: getBaseUniformDefs(props),
+        attributeDefs: getBaseAttributeDefs(props),
+        textureDefs: getBaseTextureDefs(props),
     }
 }
 
-export function destroyUniforms(uniforms: ReglUniforms) {
-    for (const k in uniforms) {
-        const uniform = uniforms[k]
-        if ((uniform as any).destroy) {
-            (uniform as any).destroy()
-        }
+export function getBaseValues(props: BaseProps) {
+    return {
+        uniformValues: getBaseUniformValues(props),
+        attributeValues: getBaseAttributeValues(props),
+        textureValues: getBaseTextureValues(props),
     }
-}
+}

+ 19 - 70
src/mol-gl/renderer.ts

@@ -4,14 +4,12 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Vec3, Mat4 } from 'mol-math/linear-algebra'
+// import { Vec3, Mat4 } from 'mol-math/linear-algebra'
 import { Viewport } from 'mol-view/camera/util';
 import { Camera } from 'mol-view/camera/base';
 
 import Scene, { RenderObject } from './scene';
-import { createContext } from './webgl/context';
-import { SimpleShaders } from './shaders';
-import { createRenderable, RenderableProps, RenderableState } from './renderable';
+import { Context } from './webgl/context';
 
 export interface RendererStats {
     elementsCount: number
@@ -34,58 +32,14 @@ interface Renderer {
     dispose: () => void
 }
 
-const extensions = [
-    'OES_element_index_uint',
-    'ANGLE_instanced_arrays'
-]
-const optionalExtensions = [
-    'EXT_disjoint_timer_query'
-]
-
-function getPixelRatio() {
-    return (typeof window !== 'undefined') ? window.devicePixelRatio : 1
-}
+// function getPixelRatio() {
+//     return (typeof window !== 'undefined') ? window.devicePixelRatio : 1
+// }
 
 namespace Renderer {
-    export function create(gl: WebGLRenderingContext, camera: Camera): Renderer {
-
-        const ctx = createContext(gl)
-
-        const renderableProps: RenderableProps = {
-            shaders: SimpleShaders,
-            uniform: {
-                model: 'm4',
-                view: 'm4',
-                projection: 'm4'
-            },
-            attribute: {
-                position: { kind: 'float32', itemSize: 3, divisor: 0 }
-            },
-            texture: {
-
-            }
-        }
-
-        const renderableState: RenderableState<typeof renderableProps> = {
-            uniform: {
-                model: Mat4.identity(),
-                view: camera.view,
-                projection: camera.projection
-            },
-            attribute: {
-                position: new Float32Array([0, 0, 0, 10, 10, 0, -10, 0, 0])
-            },
-            texture: {
-
-            },
-
-            drawCount: 3
-        }
-
-        const renderable = createRenderable(ctx, renderableProps, renderableState)
-
-        // const regl = glContext.create({ gl, extensions, optionalExtensions, profile: false })
-        // const scene = Scene.create(regl)
+    export function create(ctx: Context, camera: Camera): Renderer {
+        const { gl } = ctx
+        const scene = Scene.create(ctx)
 
         // const baseContext = regl({
         //     context: {
@@ -112,36 +66,31 @@ namespace Renderer {
         // })
 
         const draw = () => {
-            // regl.poll() // updates timers and viewport
-            // baseContext(state => {
-            //     regl.clear({ color: [0, 0, 0, 1] })
-            //     // TODO painters sort, filter visible, filter picking, visibility culling?
-            //     scene.forEach((r, o) => {
-            //         if (o.visible) r.draw()
-            //     })
-            // })
-
+            // TODO clear color
             gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
-            renderable.draw()
+
+            // TODO painters sort, filter visible, filter picking, visibility culling?
+            scene.forEach((r, o) => {
+                if (o.visible) r.draw()
+            })
         }
 
         return {
             add: (o: RenderObject) => {
-                // scene.add(o)
+                scene.add(o)
             },
             remove: (o: RenderObject) => {
-                // scene.remove(o)
+                scene.remove(o)
             },
             update: () => {
-                // scene.forEach((r, o) => r.update(o))
+                scene.forEach((r, o) => r.update(o))
             },
             clear: () => {
-                // scene.clear()
+                scene.clear()
             },
             draw,
             setViewport: (viewport: Viewport) => {
                 gl.viewport(viewport.x, viewport.y, viewport.width, viewport.height)
-                // regl({ viewport })
             },
             get stats() {
                 return {
@@ -153,7 +102,7 @@ namespace Renderer {
                 } as any
             },
             dispose: () => {
-                // regl.destroy()
+                scene.clear()
             }
         }
     }

+ 4 - 4
src/mol-gl/scene.ts

@@ -17,14 +17,14 @@ function getNextId() {
 export type RenderData = { [k: string]: ValueCell<Helpers.TypedArray> }
 
 export interface BaseRenderObject { id: number, type: string, props: {}, visible: boolean }
-export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', props: MeshRenderable.Data }
-export interface PointRenderObject extends BaseRenderObject { type: 'point', props: PointRenderable.Data }
+export interface MeshRenderObject extends BaseRenderObject { type: 'mesh', props: MeshRenderable.Props }
+export interface PointRenderObject extends BaseRenderObject { type: 'point', props: PointRenderable.Props }
 export type RenderObject = MeshRenderObject | PointRenderObject
 
-export function createMeshRenderObject(props: MeshRenderable.Data): MeshRenderObject {
+export function createMeshRenderObject(props: MeshRenderable.Props): MeshRenderObject {
     return { id: getNextId(), type: 'mesh', props, visible: true }
 }
-export function createPointRenderObject(props: PointRenderable.Data): PointRenderObject {
+export function createPointRenderObject(props: PointRenderable.Props): PointRenderObject {
     return { id: getNextId(), type: 'point', props, visible: true }
 }
 

+ 6 - 11
src/mol-gl/shaders.ts → src/mol-gl/shader-code.ts

@@ -5,26 +5,21 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-export interface Shaders {
+export interface ShaderCode {
     vert: string
     frag: string
 }
 
-export const PointShaders = {
+export const PointShaderCode: ShaderCode = {
     vert: require('mol-gl/shader/point.vert'),
     frag: require('mol-gl/shader/point.frag')
 }
 
-export const MeshShaders = {
+export const MeshShaderCode: ShaderCode = {
     vert: require('mol-gl/shader/mesh.vert'),
     frag: require('mol-gl/shader/mesh.frag')
 }
 
-export const SimpleShaders = {
-    vert: require('mol-gl/shader/simple.vert'),
-    frag: require('mol-gl/shader/simple.frag')
-}
-
 type ShaderDefine = (
     'UNIFORM_COLOR' | 'ATTRIBUTE_COLOR' | 'INSTANCE_COLOR' | 'ELEMENT_COLOR' | 'ELEMENT_INSTANCE_COLOR' |
     'UNIFORM_SIZE' | 'ATTRIBUTE_SIZE' |
@@ -34,7 +29,7 @@ export type ShaderDefines = {
     [k in ShaderDefine]?: number|string
 }
 
-function getDefines (defines: ShaderDefines) {
+function getDefinesCode (defines: ShaderDefines) {
     if (defines === undefined) return ''
     const lines = []
     for (const name in defines) {
@@ -48,8 +43,8 @@ function getDefines (defines: ShaderDefines) {
     return lines.join('\n') + '\n'
 }
 
-export function addDefines(defines: ShaderDefines, shaders: Shaders) {
-    const header = getDefines(defines)
+export function addShaderDefines(defines: ShaderDefines, shaders: ShaderCode) {
+    const header = getDefinesCode(defines)
     return {
         vert: `${header}${shaders.vert}`,
         frag: `${header}${shaders.frag}`

+ 1 - 3
src/mol-gl/shader/mesh.vert

@@ -15,7 +15,7 @@ uniform int elementCount;
 #pragma glslify: import('./chunks/color-vert-params.glsl')
 
 attribute vec3 position;
-attribute vec4 transformColumn0, transformColumn1, transformColumn2, transformColumn3;
+attribute mat4 transform;
 attribute float instanceId;
 attribute float elementId;
 
@@ -30,9 +30,7 @@ varying vec3 vViewPosition;
 void main(){
     #pragma glslify: import('./chunks/color-assign-varying.glsl')
 
-    mat4 transform = mat4(transformColumn0, transformColumn1, transformColumn2, transformColumn3);
     mat4 modelView = view * model * transform;
-
     vec4 mvPosition = modelView * vec4(position, 1.0);
     vViewPosition = mvPosition.xyz;
     gl_Position = projection * mvPosition;

+ 1 - 2
src/mol-gl/shader/point.vert

@@ -24,14 +24,13 @@ uniform float viewportHeight;
 #endif
 
 attribute vec3 position;
-attribute vec4 transformColumn0, transformColumn1, transformColumn2, transformColumn3;
+attribute mat4 transform;
 attribute float instanceId;
 attribute float elementId;
 
 void main(){
     #pragma glslify: import('./chunks/color-assign-varying.glsl')
 
-    mat4 transform = mat4(transformColumn0, transformColumn1, transformColumn2, transformColumn3);
     mat4 modelView = view * model * transform;
     vec4 mvPosition = modelView * vec4(position, 1.0);
 

+ 0 - 5
src/mol-gl/shader/simple.frag

@@ -1,5 +0,0 @@
-precision highp float;
-
-void main(void) {
-    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
-}

+ 0 - 8
src/mol-gl/shader/simple.vert

@@ -1,8 +0,0 @@
-precision highp float;
-
-attribute vec3 position;
-uniform mat4 model, view, projection;
-
-void main(void) {
-    gl_Position = projection * view * model * vec4(position, 1.0);
-}

+ 82 - 45
src/mol-gl/webgl/buffer.ts

@@ -8,7 +8,7 @@ import { Context } from './context'
 
 export type UsageHint = 'static' | 'dynamic' | 'stream'
 export type DataType = 'uint8' | 'int8' | 'uint16' | 'int16' | 'uint32' | 'int32' | 'float32'
-export type BufferType = 'attribute' | 'element'
+export type BufferType = 'attribute' | 'elements'
 
 export type DataTypeArrayType = {
     'uint8': Uint8Array
@@ -22,9 +22,10 @@ export type DataTypeArrayType = {
 export type ArrayType = Helpers.ValueOf<DataTypeArrayType>
 export type ArrayKind = keyof DataTypeArrayType
 
-export type BufferItemSize = 1 | 2 | 3 | 4
+export type BufferItemSize = 1 | 2 | 3 | 4 | 16
 
-export function getUsageHint(gl: WebGLRenderingContext, usageHint: UsageHint) {
+export function getUsageHint(ctx: Context, usageHint: UsageHint) {
+    const { gl } = ctx
     switch (usageHint) {
         case 'static': return gl.STATIC_DRAW
         case 'dynamic': return gl.DYNAMIC_DRAW
@@ -32,7 +33,8 @@ export function getUsageHint(gl: WebGLRenderingContext, usageHint: UsageHint) {
     }
 }
 
-export function getDataType(gl: WebGLRenderingContext, dataType: DataType) {
+export function getDataType(ctx: Context, dataType: DataType) {
+    const { gl } = ctx
     switch (dataType) {
         case 'uint8': return gl.UNSIGNED_BYTE
         case 'int8': return gl.BYTE
@@ -44,7 +46,8 @@ export function getDataType(gl: WebGLRenderingContext, dataType: DataType) {
     }
 }
 
-function dataTypeFromArray(gl: WebGLRenderingContext, array: ArrayType) {
+function dataTypeFromArray(ctx: Context, array: ArrayType) {
+    const { gl } = ctx
     if (array instanceof Uint8Array) {
         return gl.UNSIGNED_BYTE
     } else if (array instanceof Int8Array) {
@@ -64,91 +67,125 @@ function dataTypeFromArray(gl: WebGLRenderingContext, array: ArrayType) {
     }
 }
 
-export function getBufferType(gl: WebGLRenderingContext, bufferType: BufferType) {
+export function getBufferType(ctx: Context, bufferType: BufferType) {
+    const { gl } = ctx
     switch (bufferType) {
         case 'attribute': return gl.ARRAY_BUFFER
-        case 'element': return gl.ELEMENT_ARRAY_BUFFER
+        case 'elements': return gl.ELEMENT_ARRAY_BUFFER
     }
 }
 
-export interface Buffer<T extends ArrayType, S extends BufferItemSize, B extends BufferType> {
-    updateData: (array: T) => void
-    updateSubData: (array: T, offset: number, count: number) => void
-    bind: (location: number, stride: number, offset: number) => void
+export interface Buffer {
+    readonly _buffer: WebGLBuffer
+    readonly _usageHint: number
+    readonly _bufferType: number
+    readonly _dataType: number
+    readonly _bpe: number
+
+    updateData: (array: ArrayType) => void
+    updateSubData: (array: ArrayType, offset: number, count: number) => void
     destroy: () => void
 }
 
-export function createBuffer<T extends ArrayType, S extends BufferItemSize, B extends BufferType>(ctx: Context, array: T, itemSize: S, usageHint: UsageHint, bufferType: B): Buffer<T, S, B> {
+export function createBuffer(ctx: Context, array: ArrayType, itemSize: BufferItemSize, usageHint: UsageHint, bufferType: BufferType): Buffer {
     const { gl } = ctx
-    const buffer = gl.createBuffer()
-    if (buffer === null) {
+    const _buffer = gl.createBuffer()
+    if (_buffer === null) {
         throw new Error('Could not create WebGL buffer')
     }
 
-    const _usageHint = getUsageHint(gl, usageHint)
-    const _bufferType = getBufferType(gl, bufferType)
-    const _dataType = dataTypeFromArray(gl, array)
+    const _usageHint = getUsageHint(ctx, usageHint)
+    const _bufferType = getBufferType(ctx, bufferType)
+    const _dataType = dataTypeFromArray(ctx, array)
+    const _bpe = array.BYTES_PER_ELEMENT
 
-    function updateData(array: T) {
-        gl.bindBuffer(_bufferType, buffer)
+    function updateData(array: ArrayType) {
+        gl.bindBuffer(_bufferType, _buffer)
         gl.bufferData(_bufferType, array, _usageHint)
     }
     updateData(array)
 
     return {
+        _buffer,
+        _usageHint,
+        _bufferType,
+        _dataType,
+        _bpe,
+
         updateData,
-        updateSubData: (array: T, offset: number, count: number) => {
-            gl.bindBuffer(_bufferType, buffer)
-            gl.bufferSubData(_bufferType, offset * array.BYTES_PER_ELEMENT, array.subarray(offset, offset + count))
-        },
-        bind: (location: number, stride: number, offset: number) => {
-            gl.bindBuffer(_bufferType, buffer);
-            gl.enableVertexAttribArray(location);
-            gl.vertexAttribPointer(location, itemSize, _dataType, false, stride, offset);
+        updateSubData: (array: ArrayType, offset: number, count: number) => {
+            gl.bindBuffer(_bufferType, _buffer)
+            gl.bufferSubData(_bufferType, offset * _bpe, array.subarray(offset, offset + count))
         },
+        
         destroy: () => {
-            gl.deleteBuffer(buffer)
+            gl.bindBuffer(_bufferType, _buffer)
+            // set size to 1 before deleting
+            gl.bufferData(_bufferType, 1, _usageHint)
+            gl.deleteBuffer(_buffer)
         }
     }
 }
 
-export type AttributeDefs = { [k: string]: { kind: ArrayKind, itemSize: BufferItemSize, divisor: number } }
-export type AttributeValues<T extends AttributeDefs> = { [K in keyof T]: ArrayType }
-export type AttributeBuffers<T extends AttributeDefs> = {
-    [K in keyof T]: AttributeBuffer<DataTypeArrayType[T[K]['kind']], T[K]['itemSize']>
+export type AttributeDefs = {
+    [k: string]: { kind: ArrayKind, itemSize: BufferItemSize, divisor: number } 
+}
+export type AttributeValues = { [k: string]: ArrayType }
+export type AttributeBuffers = { [k: string]: AttributeBuffer }
+
+export interface AttributeBuffer extends Buffer {
+    bind: (location: number) => void
 }
 
-export interface AttributeBuffer<T extends ArrayType, S extends BufferItemSize> extends Buffer<T, S, 'attribute'> {}
+export function createAttributeBuffer<T extends ArrayType, S extends BufferItemSize>(ctx: Context, array: ArrayType, itemSize: S, divisor: number, usageHint: UsageHint = 'dynamic'): AttributeBuffer {
+    const { gl } = ctx
+    const { angleInstancedArrays } = ctx.extensions
 
-export function createAttributeBuffer<T extends ArrayType, S extends BufferItemSize>(ctx: Context, array: T, itemSize: S, divisor: number, usageHint: UsageHint = 'dynamic'): AttributeBuffer<T, S> {
     const buffer = createBuffer(ctx, array, itemSize, usageHint, 'attribute')
-    const { angleInstancedArrays } = ctx.extensions
+    const { _buffer, _bufferType, _dataType, _bpe } = buffer
 
     return {
         ...buffer,
-        bind: (location: number, stride: number, offset: number) => {
-            buffer.bind(location, stride, offset)
+        bind: (location: number) => {
+            gl.bindBuffer(_bufferType, _buffer)
+            if (itemSize === 16) {
+                for (let i = 0; i < 4; ++i) {
+                    gl.enableVertexAttribArray(location + i)
+                    gl.vertexAttribPointer(location + i, 4, _dataType, false, 4 * _bpe, i * _bpe)
+                }
+            } else {
+                gl.enableVertexAttribArray(location)
+                gl.vertexAttribPointer(location, itemSize, _dataType, false, 0, 0)
+            }
             angleInstancedArrays.vertexAttribDivisorANGLE(location, divisor)
         }
     }
 }
 
-export function createAttributeBuffers<T extends AttributeDefs>(ctx: Context, props: T, state: AttributeValues<T>) {
-    const buffers: Partial<AttributeBuffers<T>> = {}
+export function createAttributeBuffers<T extends AttributeDefs>(ctx: Context, props: T, state: AttributeValues) {
+    const buffers: AttributeBuffers = {}
     Object.keys(props).forEach(k => {
         buffers[k] = createAttributeBuffer(ctx, state[k], props[k].itemSize, props[k].divisor)
     })
-    return buffers as AttributeBuffers<T>
+    return buffers as AttributeBuffers
 }
 
-export type ElementType = Uint16Array | Uint32Array
+export type ElementsType = Uint16Array | Uint32Array
+export type ElementsKind = 'uint16' | 'unit32'
 
-export interface ElementBuffer<T extends ElementType> extends Buffer<T, 3, 'element'> {}
+export interface ElementsBuffer extends Buffer {
+    bind: () => void
+}
 
-export function createElementBuffer<T extends ElementType>(ctx: Context, array: T, usageHint: UsageHint = 'static'): ElementBuffer<T> {
-    const buffer = createBuffer(ctx, array, 3, usageHint, 'element')
+export function createElementsBuffer(ctx: Context, array: ElementsType, usageHint: UsageHint = 'static'): ElementsBuffer {
+    const { gl } = ctx
+    const buffer = createBuffer(ctx, array, 1, usageHint, 'elements')
+    const { _buffer } = buffer
 
     return {
-        ...buffer
+        ...buffer,
+        bind: () => {
+            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, _buffer);
+        }
     }
 }

+ 50 - 8
src/mol-gl/webgl/context.ts

@@ -4,15 +4,52 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-interface Reference<T> { usageCount: number, value: T }
+import { createProgramCache, ProgramCache } from './program'
+import { createShaderCache, ShaderCache } from './shader'
+
+// const extensions = [
+//     'OES_element_index_uint',
+//     'ANGLE_instanced_arrays'
+// ]
+// const optionalExtensions = [
+//     'EXT_disjoint_timer_query'
+// ]
+
+function unbindResources (gl: WebGLRenderingContext) {
+    // bind null to all texture units
+    const maxTextureImageUnits = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS)
+    for (let i = 0; i < maxTextureImageUnits; ++i) {
+        gl.activeTexture(gl.TEXTURE0 + i)
+        gl.bindTexture(gl.TEXTURE_2D, null)
+        gl.bindTexture(gl.TEXTURE_CUBE_MAP, null)
+    }
+
+    // assign the smallest possible buffer to all attributes
+    const buf = gl.createBuffer();
+    gl.bindBuffer(gl.ARRAY_BUFFER, buf);
+    const maxVertexAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS);
+    for (let i = 0; i < maxVertexAttribs; ++i) {
+        gl.vertexAttribPointer(i, 1, gl.FLOAT, false, 0, 0);
+    }
+
+    // bind null to all buffers
+    gl.bindBuffer(gl.ARRAY_BUFFER, null)
+    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null)
+    gl.bindRenderbuffer(gl.RENDERBUFFER, null)
+    gl.bindFramebuffer(gl.FRAMEBUFFER, null)
+}
+
+type RequiredExtensions = {
+    angleInstancedArrays: ANGLE_instanced_arrays
+    oesElementIndexUint: OES_element_index_uint
+}
 
 export interface Context {
     gl: WebGLRenderingContext
-    shaderCache: Map<string, Reference<WebGLShader>>
-    extensions: {
-        angleInstancedArrays: ANGLE_instanced_arrays
-        oesElementIndexUint: OES_element_index_uint
-    }
+    extensions: RequiredExtensions
+    shaderCache: ShaderCache
+    programCache: ProgramCache
+    destroy: () => void
 }
 
 export function createContext(gl: WebGLRenderingContext): Context {
@@ -26,7 +63,12 @@ export function createContext(gl: WebGLRenderingContext): Context {
     }
     return {
         gl,
-        shaderCache: new Map(),
-        extensions: { angleInstancedArrays, oesElementIndexUint }
+        extensions: { angleInstancedArrays, oesElementIndexUint },
+        shaderCache: createShaderCache(),
+        programCache: createProgramCache(),
+        destroy: () => {
+            unbindResources(gl)
+            // TODO destroy buffers and textures
+        }
     }
 }

+ 48 - 29
src/mol-gl/webgl/program.ts

@@ -4,79 +4,88 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Shaders } from '../shaders'
-import { getShader } from './shader'
+import { ShaderCode } from '../shader-code'
 import { Context } from './context';
 import { getUniformSetters, UniformDefs, UniformValues } from './uniform';
 import {AttributeDefs, AttributeBuffers } from './buffer';
 import { TextureId, TextureDefs, TextureUniforms, Textures } from './texture';
+import { createReferenceCache, ReferenceCache } from 'mol-util/reference-cache';
 
-export interface Program<U extends UniformDefs, A extends AttributeDefs, T extends TextureDefs> {
+export interface Program {
     readonly id: number
 
-    setUniforms: (uniformValues: Partial<UniformValues<U>>) => void
-    bindAttributes: (attribueBuffers: AttributeBuffers<A>) => void
-    bindTextures: (textures: Textures<T>) => void
+    setUniforms: (uniformValues: UniformValues) => void
+    bindAttributes: (attribueBuffers: AttributeBuffers) => void
+    bindTextures: (textures: Textures) => void
 
     destroy: () => void
 }
 
-type AttributeLocations<T extends AttributeDefs> = { [K in keyof T]: number }
+type AttributeLocations = { [k: string]: number }
 
-function getAttributeLocations<A extends AttributeDefs>(gl: WebGLRenderingContext, program: WebGLProgram, attributes: A) {
+function getAttributeLocations(ctx: Context, program: WebGLProgram, attributeDefs: AttributeDefs) {
+    const { gl } = ctx
+    const locations: AttributeLocations = {}
     gl.useProgram(program)
-    const locations: Partial<AttributeLocations<A>> = {}
-    Object.keys(attributes).forEach(k => {
+    Object.keys(attributeDefs).forEach(k => {
         const loc = gl.getAttribLocation(program, k)
         gl.enableVertexAttribArray(loc)
         locations[k] = loc
     })
-    return locations as AttributeLocations<A>
+    return locations
 }
 
-function getTextureUniforms<T extends TextureDefs>(textures: T) {
-    const textureUniforms: Partial<TextureUniforms<T>> = {}
+function getTextureUniforms(textures: TextureDefs) {
+    const textureUniforms: TextureUniforms = {}
     Object.keys(textureUniforms).forEach(k => textureUniforms[k] = 't2')
-    return textureUniforms as TextureUniforms<T>
+    return textureUniforms
 }
 
-export function createProgram<U extends UniformDefs, A extends AttributeDefs, T extends TextureDefs>(ctx: Context, shaders: Shaders, uniformDefs: U, attributeDefs: A, textureDefs: T): Program<U, A, T> {
-    const { gl } = ctx
+export interface ProgramProps {
+    shaderCode: ShaderCode,
+    uniformDefs: UniformDefs,
+    attributeDefs: AttributeDefs,
+    textureDefs: TextureDefs
+}
+
+export function createProgram(ctx: Context, props: ProgramProps): Program {
+    const { gl, shaderCache } = ctx
+    const { shaderCode, uniformDefs, attributeDefs, textureDefs } = props
 
     const program = gl.createProgram()
     if (program === null) {
         throw new Error('Could not create WebGL program')
     }
 
-    const glVertShader = getShader(ctx, 'vert', shaders.vert)
-    const glFragShader = getShader(ctx, 'frag', shaders.frag)
+    const vertShaderRef = shaderCache.get(ctx, { type: 'vert', source: shaderCode.vert })
+    const fragShaderRef = shaderCache.get(ctx, { type: 'frag', source: shaderCode.frag })
 
-    gl.attachShader(program, glVertShader.value)
-    gl.attachShader(program, glFragShader.value)
+    vertShaderRef.value.attach(program)
+    fragShaderRef.value.attach(program)
     gl.linkProgram(program)
 
-    const uniformSetters = getUniformSetters(gl, program, uniformDefs)
-    const attributeLocations = getAttributeLocations(gl, program, attributeDefs)
+    const uniformSetters = getUniformSetters(ctx, program, uniformDefs)
+    const attributeLocations = getAttributeLocations(ctx, program, attributeDefs)
     const textureUniforms = getTextureUniforms(textureDefs)
-    const textureUniformSetters = getUniformSetters(gl, program, textureUniforms)
+    const textureUniformSetters = getUniformSetters(ctx, program, textureUniforms)
 
     let destroyed = false
 
     return {
         id: 0,
 
-        setUniforms: (uniformValues: Partial<UniformValues<U>>) => {
+        setUniforms: (uniformValues: UniformValues) => {
             Object.keys(uniformValues).forEach(k => {
                 const value = uniformValues[k]
                 if (value !== undefined) uniformSetters[k](value)
             })
         },
-        bindAttributes: (attribueBuffers: AttributeBuffers<A>) => {
+        bindAttributes: (attribueBuffers: AttributeBuffers) => {
             Object.keys(attribueBuffers).forEach(k => {
-                attribueBuffers[k].bind(attributeLocations[k], 0, 0)
+                attribueBuffers[k].bind(attributeLocations[k])
             })
         },
-        bindTextures: (textures: Textures<T>) => {
+        bindTextures: (textures: Textures) => {
             Object.keys(textures).forEach((k, i) => {
                 textures[k].bind(i as TextureId)
                 textureUniformSetters[k](i)
@@ -85,10 +94,20 @@ export function createProgram<U extends UniformDefs, A extends AttributeDefs, T
 
         destroy: () => {
             if (destroyed) return
-            glVertShader.free()
-            glFragShader.free()
+            vertShaderRef.free()
+            fragShaderRef.free()
             gl.deleteProgram(program)
             destroyed = true
         }
     }
+}
+
+export type ProgramCache = ReferenceCache<Program, ProgramProps, Context>
+
+export function createProgramCache(): ProgramCache {
+    return createReferenceCache(
+        (props: ProgramProps) => JSON.stringify(props),
+        (ctx: Context, props: ProgramProps) => createProgram(ctx, props),
+        (program: Program) => { program.destroy() }
+    )
 }

+ 108 - 0
src/mol-gl/webgl/render-item.ts

@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+import { UniformDefs, UniformValues } from './uniform';
+import { AttributeDefs, AttributeValues, createAttributeBuffers, createElementsBuffer, ElementsKind, ElementsBuffer } from './buffer';
+import { TextureDefs, TextureValues, createTextures } from './texture';
+import { Context } from './context';
+import { ShaderCode } from '../shader-code';
+
+export type DrawMode = 'points' | 'lines' | 'line-strip' | 'line-loop' | 'triangles' | 'triangle-strip' | 'triangle-fan'
+
+export function getDrawMode(ctx: Context, drawMode: DrawMode) {
+    const { gl } = ctx
+    switch (drawMode) {
+        case 'points': return gl.POINTS
+        case 'lines': return gl.LINES
+        case 'line-strip': return gl.LINE_STRIP
+        case 'line-loop': return gl.LINE_LOOP
+        case 'triangles': return gl.TRIANGLES
+        case 'triangle-strip': return gl.TRIANGLE_STRIP
+        case 'triangle-fan': return gl.TRIANGLE_FAN
+    }
+}
+
+export type RenderItemProps = {
+    shaderCode: ShaderCode
+    
+    uniformDefs: UniformDefs
+    attributeDefs: AttributeDefs
+    textureDefs: TextureDefs
+
+    elementsKind?: ElementsKind
+    drawMode: DrawMode
+}
+
+export type RenderItemState = {
+    uniformValues: UniformValues
+    attributeValues: AttributeValues
+    textureValues: TextureValues
+
+    elements?: Uint32Array
+    drawCount: number
+    instanceCount: number
+}
+
+export interface RenderItem {
+    readonly hash: string
+    readonly programId: number
+
+    update: (state: RenderItemState) => void
+
+    draw: () => void
+    dispose: () => void
+}
+
+export function createRenderItem(ctx: Context, props: RenderItemProps, state: RenderItemState): RenderItem {
+    const { programCache } = ctx
+    const { angleInstancedArrays } = ctx.extensions
+    const { shaderCode, uniformDefs, attributeDefs, textureDefs, elementsKind } = props
+    const { attributeValues, textureValues, uniformValues, elements } = state
+    
+    const hash = JSON.stringify(props)
+    const drawMode = getDrawMode(ctx, props.drawMode)
+    const programRef = programCache.get(ctx, { shaderCode, uniformDefs, attributeDefs, textureDefs })
+    const program = programRef.value
+
+    const attributeBuffers = createAttributeBuffers(ctx, attributeDefs, attributeValues)
+    const textures = createTextures(ctx, textureDefs, textureValues)
+    
+    let elementsBuffer: ElementsBuffer
+    if (elements && elementsKind) {
+        elementsBuffer = createElementsBuffer(ctx, elements)
+    }
+
+    let { drawCount, instanceCount } = state
+
+    return {
+        hash,
+        programId: program.id,
+
+        draw: () => {
+            program.setUniforms(uniformValues)
+            program.bindAttributes(attributeBuffers)
+            program.bindTextures(textures)
+            if (elementsBuffer) {
+                angleInstancedArrays.drawElementsInstancedANGLE(drawMode, drawCount, elementsBuffer._dataType, 0, instanceCount);
+            } else {
+                angleInstancedArrays.drawArraysInstancedANGLE(drawMode, 0, drawCount, instanceCount)
+                // gl.drawArrays(drawMode, 0, drawCount)
+            }
+        },
+        update: (state: RenderItemState) => {
+            // TODO
+            const { attributeValues } = state
+            Object.keys(attributeValues).forEach(k => {
+                const value = attributeValues[k]
+                if (value !== undefined) attributeBuffers[k].updateData(value)
+            })
+        },
+        dispose: () => {
+            // TODO
+            programRef.free()
+        }
+    }
+}

+ 27 - 20
src/mol-gl/webgl/shader.ts

@@ -4,7 +4,8 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { Context } from './context'
+import { createReferenceCache, ReferenceCache } from 'mol-util/reference-cache';
+import { Context } from './context';
 
 function addLineNumbers(source: string) {
     const lines = source.split('\n')
@@ -14,11 +15,18 @@ function addLineNumbers(source: string) {
     return lines.join('\n')
 }
 
-type ShaderType = 'vert' | 'frag'
+export type ShaderType = 'vert' | 'frag'
+export interface ShaderProps { type: ShaderType, source: string }
+export interface Shader {
+    attach: (program: WebGLProgram) => void
+    destroy: () => void
+}
 
-function createShader(gl: WebGLRenderingContext, type: ShaderType, source: string) {
-    const shader = gl.createShader(type === 'vert' ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER)
+function createShader(ctx: Context, props: ShaderProps): Shader {
+    const { gl } = ctx
+    const { type, source } = props
 
+    const shader = gl.createShader(type === 'vert' ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER)
     if (shader === null) {
         throw new Error(`Error creating ${type} shader`)
     }
@@ -31,23 +39,22 @@ function createShader(gl: WebGLRenderingContext, type: ShaderType, source: strin
         throw new Error(`Error compiling ${type} shader`)
     }
 
-    return shader
-}
-
-export function getShader(ctx: Context, type: ShaderType, source: string) {
-    let shaderRef = ctx.shaderCache.get(source)
-    if (!shaderRef) {
-        shaderRef = { usageCount: 0, value: createShader(ctx.gl, type, source) }
-        ctx.shaderCache.set(source, shaderRef)
-    }
-    shaderRef.usageCount += 1
     return {
-        free: () => {
-            if (shaderRef) {
-                shaderRef.usageCount -= 1
-                shaderRef = undefined
-            }
+        attach: (program: WebGLProgram) => {
+            gl.attachShader(program, shader)
         },
-        value: shaderRef.value
+        destroy: () => {
+            gl.deleteShader(shader)
+        }
     }
+}
+
+export type ShaderCache = ReferenceCache<Shader, ShaderProps, Context>
+
+export function createShaderCache(): ShaderCache {
+    return createReferenceCache(
+        (props: ShaderProps) => JSON.stringify(props),
+        (ctx: Context, props: ShaderProps) => createShader(ctx, props),
+        (shader: Shader) => { shader.destroy() }
+    )
 }

+ 34 - 21
src/mol-gl/webgl/texture.ts

@@ -4,49 +4,62 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-// import { Context } from './context'
+import { Context } from './context'
+import { TextureImage } from '../renderable/util';
 
 export interface Texture {
-    load: (image: ImageData) => void
+    load: (image: TextureImage) => void
     bind: (id: TextureId) => void
+    unbind: (id: TextureId) => void
 }
 
 export type TextureId = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15
-export type TextureTarget = 'TEXTURE0' | 'TEXTURE1' | 'TEXTURE2' | 'TEXTURE3' | 'TEXTURE4' | 'TEXTURE5' | 'TEXTURE6' | 'TEXTURE7' | 'TEXTURE8' | 'TEXTURE9' | 'TEXTURE10' | 'TEXTURE11' | 'TEXTURE12' | 'TEXTURE13' | 'TEXTURE14' | 'TEXTURE15'
 
-export type TextureDefs = { [k: string]: '' }
-export type TextureUniforms<T extends TextureDefs> = { [k in keyof T]: 't2' }
-export type TextureValues<T extends TextureDefs> = { [k in keyof T]: ImageData }
-export type Textures<T extends TextureDefs> = { [k in keyof T]: Texture }
+export type TextureDefs = { [k: string]: true }
+export type TextureUniforms = { [k: string]: 't2' }
+export type TextureValues = { [k: string]: TextureImage }
+export type Textures = { [k: string]: Texture }
 
-export function createTexture(gl: WebGLRenderingContext): Texture {
+export function createTexture(ctx: Context): Texture {
+    const { gl } = ctx
     const texture = gl.createTexture()
     if (texture === null) {
         throw new Error('Could not create WebGL texture')
     }
 
+    const _textureType = gl.TEXTURE_2D
+    const _magFilter = gl.NEAREST
+    const _minFilter = gl.NEAREST
+    const _format = gl.RGBA
+    const _arrayType = gl.UNSIGNED_BYTE
+
     return {
-        load: (image: ImageData) => {
-            gl.bindTexture(gl.TEXTURE_2D, texture)
+        load: (image: TextureImage) => {
+            const { array, width, height } = image
+            gl.bindTexture(_textureType, texture)
             gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
-            gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)
-            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
-            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
-            gl.bindTexture(gl.TEXTURE_2D, null)
+            gl.texImage2D(_textureType, 0, _format, width, height, 0, _format, _arrayType, array)
+            gl.texParameteri(_textureType, gl.TEXTURE_MAG_FILTER, _magFilter)
+            gl.texParameteri(_textureType, gl.TEXTURE_MIN_FILTER, _minFilter)
+            gl.bindTexture(_textureType, null)
         },
         bind: (id: TextureId) => {
-            gl.activeTexture(gl[`TEXTURE${id}` as TextureTarget])
-            gl.bindTexture(gl.TEXTURE_2D, texture)
+            gl.activeTexture(gl.TEXTURE0 + id)
+            gl.bindTexture(_textureType, texture)
+        },
+        unbind: (id: TextureId) => {
+            gl.activeTexture(gl.TEXTURE0 + id)
+            gl.bindTexture(_textureType, null)
         }
     }
 }
 
-export function createTextures<T extends TextureDefs>(gl: WebGLRenderingContext, props: T, state: TextureValues<T>) {
-    const textures: Partial<Textures<T>> = {}
+export function createTextures(ctx: Context, props: TextureDefs, state: TextureValues) {
+    const textures: Textures = {}
     Object.keys(props).forEach(k => {
-        const texture = createTexture(gl)
+        const texture = createTexture(ctx)
         texture.load(state[k])
         textures[k] = texture
     })
-    return textures as Textures<T>
-}
+    return textures
+}

+ 20 - 14
src/mol-gl/webgl/uniform.ts

@@ -5,6 +5,7 @@
  */
 
 import { Mat3, Mat4, Vec2, Vec3, Vec4 } from 'mol-math/linear-algebra'
+import { Context } from './context';
 
 export type UniformKindValue = {
     'f': number
@@ -17,29 +18,34 @@ export type UniformKindValue = {
     't2': number
 }
 export type UniformKind = keyof UniformKindValue
+export type UniformType = number | Vec2 | Vec3 | Vec4 | Mat3 | Mat4 | ImageData
 
 export type UniformDefs = { [k: string]: UniformKind }
-export type UniformValues<T extends UniformDefs> = { [K in keyof T]: UniformKindValue[T[K]] }
-export type UniformSetters<T extends UniformDefs> = { [K in keyof T]: (value: UniformKindValue[T[K]]) => void }
+export type UniformValues = { [k: string]: UniformType }
+export type UniformSetters = { [k: string]: (value: UniformType) => void }
 
-export function createUniformSetter<K extends UniformKind, V = UniformKindValue[K]>(gl: WebGLRenderingContext, program: WebGLProgram, name: string, kind: K): (value: V) => void {
+export function createUniformSetter(ctx: Context, program: WebGLProgram, name: string, kind: UniformKind): (value: any) => void {
+    const { gl } = ctx
     const location = gl.getUniformLocation(program, name)
+    if (location === null) {
+        throw new Error(`Could not get WebGL uniform location for '${name}'`)
+    }
     switch (kind) {
-        case 'f' as K: return (value: V) => gl.uniform1f(location, value as any as number)
-        case 'i': case 't2': return (value: V) => gl.uniform1i(location, value as any as number)
-        case 'v2': return (value: V) => gl.uniform2fv(location, value as any as Vec2)
-        case 'v3': return (value: V) => gl.uniform3fv(location, value as any as Vec3)
-        case 'v4': return (value: V) => gl.uniform4fv(location, value as any as Vec4)
-        case 'm3': return (value: V) => gl.uniformMatrix3fv(location, false, value as any as Mat3)
-        case 'm4': return (value: V) => gl.uniformMatrix4fv(location, false, value as any as Mat4)
+        case 'f': return (value: number) => gl.uniform1f(location, value)
+        case 'i': case 't2': return (value: number) => gl.uniform1i(location, value)
+        case 'v2': return (value: Vec2) => gl.uniform2fv(location, value)
+        case 'v3': return (value: Vec3) => gl.uniform3fv(location, value)
+        case 'v4': return (value: Vec4) => gl.uniform4fv(location, value)
+        case 'm3': return (value: Mat3) => gl.uniformMatrix3fv(location, false, value)
+        case 'm4': return (value: Mat4) => gl.uniformMatrix4fv(location, false, value)
     }
     throw new Error('Should never happen')
 }
 
-export function getUniformSetters<T extends UniformDefs, K = keyof T>(gl: WebGLRenderingContext, program: WebGLProgram, uniforms: T) {
-    const setters: Partial<UniformSetters<T>> = {}
+export function getUniformSetters(ctx: Context, program: WebGLProgram, uniforms: UniformDefs) {
+    const setters: UniformSetters = {}
     Object.keys(uniforms).forEach(k => {
-        setters[k] = createUniformSetter(gl, program, k, uniforms[k])
+        setters[k] = createUniformSetter(ctx, program, k, uniforms[k])
     })
-    return setters as UniformSetters<T>
+    return setters
 }

+ 61 - 0
src/mol-util/reference-cache.ts

@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ *
+ * @author Alexander Rose <alexander.rose@weirdbyte.de>
+ */
+
+export interface Reference<T> { value: T, usageCount: number }
+
+export function createReference<T>(value: T, usageCount = 0) {
+    return { value, usageCount }
+}
+
+export interface ReferenceItem<T> {
+    free: () => void
+    value: T
+}
+
+export function createReferenceItem<T>(ref: Reference<T>) {
+    return {
+        free: () => {
+            ref.usageCount -= 1
+        },
+        value: ref.value
+    }
+}
+
+export interface ReferenceCache<T, P, C> {
+    get: (ctx: C, props: P) => ReferenceItem<T>
+    clear: () => void
+    count: number
+}
+
+export function createReferenceCache<T, P, C>(hashFn: (props: P) => string, ctor: (ctx: C, props: P) => T, deleteFn: (v: T) => void): ReferenceCache<T, P, C> {
+    const map: Map<string, Reference<T>> = new Map()
+    
+    return {
+        get: (ctx: C, props: P) => {
+            const id = hashFn(props)
+            let ref = map.get(id)
+            if (!ref) {
+                ref = createReference<T>(ctor(ctx, props))
+                map.set(id, ref)
+            }
+            ref.usageCount += 1
+            return createReferenceItem(ref)
+        },
+        clear: () => {
+            map.forEach((ref, id) => {
+                if (ref.usageCount <= 0) {
+                    if (ref.usageCount < 0) {
+                        console.warn('Reference usageCount below zero.')
+                    }
+                    deleteFn(ref.value)
+                }
+            })
+        },
+        get count () {
+            return map.size
+        }
+    }
+}

+ 6 - 2
src/mol-view/viewer.ts

@@ -15,6 +15,7 @@ import TrackballControls from './controls/trackball'
 import { Viewport } from './camera/util'
 import { PerspectiveCamera } from './camera/perspective'
 import { resizeCanvas } from './util';
+import { createContext } from 'mol-gl/webgl/context';
 
 interface Viewer {
     hide: (repr: StructureRepresentation) => void
@@ -64,9 +65,12 @@ namespace Viewer {
         })
 
         const gl = getWebGLContext(canvas)
-        if (gl === null) throw new Error('Could not create a WebGL rendering context')
+        if (gl === null) {
+            throw new Error('Could not create a WebGL rendering context')
+        }
+        const ctx = createContext(gl)
 
-        const renderer = Renderer.create(gl, camera)
+        const renderer = Renderer.create(ctx, camera)
 
         let drawPending = false
         const prevProjectionView = Mat4.zero()