Browse Source

added basic checks if gl program is compatible with schema

Alexander Rose 6 years ago
parent
commit
75425e7b2c

+ 4 - 4
src/mol-gl/renderable/schema.ts

@@ -5,7 +5,7 @@
  */
 
 import { ValueCell } from 'mol-util';
-import { ArrayKind, AttributeItemSize, ElementsKind, AttributeValues } from '../webgl/buffer';
+import { AttributeItemSize, ElementsKind, AttributeValues, AttributeKind } from '../webgl/buffer';
 import { UniformKind, UniformValues } from '../webgl/uniform';
 import { DefineKind, DefineValues } from '../shader-code';
 import { Vec2, Vec3, Vec4, Mat3, Mat4 } from 'mol-math/linear-algebra';
@@ -94,8 +94,8 @@ export function getValueVersions<T extends RenderableValues>(values: T) {
 
 //
 
-export type AttributeSpec<K extends ArrayKind> = { type: 'attribute', kind: K, itemSize: AttributeItemSize, divisor: number }
-export function AttributeSpec<K extends ArrayKind>(kind: K, itemSize: AttributeItemSize, divisor: number): AttributeSpec<K> {
+export type AttributeSpec<K extends AttributeKind> = { type: 'attribute', kind: K, itemSize: AttributeItemSize, divisor: number }
+export function AttributeSpec<K extends AttributeKind>(kind: K, itemSize: AttributeItemSize, divisor: number): AttributeSpec<K> {
     return { type: 'attribute', kind, itemSize, divisor }
 }
 
@@ -128,7 +128,7 @@ export function ValueSpec<K extends ValueKind>(kind: K): ValueSpec<K> {
 
 export type RenderableSchema = {
     [k: string]: (
-        AttributeSpec<ArrayKind> | UniformSpec<UniformKind> | TextureSpec<TextureKind> |
+        AttributeSpec<AttributeKind> | UniformSpec<UniformKind> | TextureSpec<TextureKind> |
         ValueSpec<ValueKind> | DefineSpec<DefineKind> | ElementsSpec<ElementsKind>
     )
 }

+ 26 - 1
src/mol-gl/webgl/buffer.ts

@@ -147,9 +147,34 @@ export function createBuffer(ctx: WebGLContext, array: ArrayType, usageHint: Usa
 //
 
 export type AttributeItemSize = 1 | 2 | 3 | 4 | 16
+export type AttributeKind = 'float32' | 'int32'
+
+export function getAttribType(ctx: WebGLContext, kind: AttributeKind, itemSize: AttributeItemSize) {
+    const { gl } = ctx
+    switch (kind) {
+        case 'int32':
+            switch (itemSize) {
+                case 1: return gl.INT
+                case 2: return gl.INT_VEC2
+                case 3: return gl.INT_VEC3
+                case 4: return gl.INT_VEC4
+            }
+            break
+        case 'float32':
+            switch (itemSize) {
+                case 1: return gl.FLOAT
+                case 2: return gl.FLOAT_VEC2
+                case 3: return gl.FLOAT_VEC3
+                case 4: return gl.FLOAT_VEC4
+                case 16: return gl.FLOAT_MAT4
+            }
+            break
+    }
+    throw new Error(`unknown attribute type for kind '${kind}' and itemSize '${itemSize}'`)
+}
 
 export type AttributeDefs = {
-    [k: string]: { kind: ArrayKind, itemSize: AttributeItemSize, divisor: number }
+    [k: string]: { kind: AttributeKind, itemSize: AttributeItemSize, divisor: number }
 }
 export type AttributeValues = { [k: string]: ValueCell<ArrayType> }
 export type AttributeBuffers = [string, AttributeBuffer][]

+ 65 - 2
src/mol-gl/webgl/program.ts

@@ -6,13 +6,14 @@
 
 import { ShaderCode, DefineValues, addShaderDefines } from '../shader-code'
 import { WebGLContext } from './context';
-import { getUniformSetters, UniformsList } from './uniform';
-import { AttributeBuffers } from './buffer';
+import { getUniformSetters, UniformsList, getUniformType } from './uniform';
+import { AttributeBuffers, getAttribType } from './buffer';
 import { TextureId, Textures } from './texture';
 import { createReferenceCache, ReferenceCache } from 'mol-util/reference-cache';
 import { idFactory } from 'mol-util/id-factory';
 import { RenderableSchema } from '../renderable/schema';
 import { hashFnv32a, hashString } from 'mol-data/util';
+import { isProductionMode } from 'mol-util/debug';
 
 const getNextProgramId = idFactory()
 
@@ -47,6 +48,63 @@ function getLocations(ctx: WebGLContext, program: WebGLProgram, schema: Renderab
     return locations
 }
 
+function checkActiveAttributes(ctx: WebGLContext, program: WebGLProgram, schema: RenderableSchema) {
+    const { gl } = ctx
+    const attribCount = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
+    for (let i = 0; i < attribCount; ++i) {
+        const info = gl.getActiveAttrib(program, i);
+        if (info) {
+            const { name, type } = info
+            const spec = schema[name]
+            if (spec === undefined) {
+                throw new Error(`missing 'uniform' or 'texture' with name '${name}' in schema`)
+            }
+            if (spec.type !== 'attribute') {
+                throw new Error(`'${name}' must be of type 'attribute' but is '${spec.type}'`)
+            }
+            const attribType = getAttribType(ctx, spec.kind, spec.itemSize)
+            if (attribType !== type) {
+                throw new Error(`unexpected attribute type for ${name}`)
+            }
+        }
+    }
+}
+
+function checkActiveUniforms(ctx: WebGLContext, program: WebGLProgram, schema: RenderableSchema) {
+    const { gl } = ctx
+    const attribCount = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
+    for (let i = 0; i < attribCount; ++i) {
+        const info = gl.getActiveUniform(program, i);
+        if (info) {
+            const { name, type } = info
+            const spec = schema[name]
+            if (spec === undefined) {
+                throw new Error(`missing 'uniform' or 'texture' with name '${name}' in schema`)
+            }
+            if (spec.type === 'uniform') {
+                const uniformType = getUniformType(gl, spec.kind)
+                if (uniformType !== type) {
+                    throw new Error(`unexpected uniform type for ${name}`)
+                }
+            } else if (spec.type === 'texture') {
+                if (spec.kind === 'image-float32' || spec.kind === 'image-uint8') {
+                    if (type !== gl.SAMPLER_2D) {
+                        throw new Error(`unexpected sampler type for '${name}'`)
+                    }
+                } else if (spec.kind === 'volume-float32' || spec.kind === 'volume-uint8') {
+                    if (type !== (gl as WebGL2RenderingContext).SAMPLER_3D) {
+                        throw new Error(`unexpected sampler type for '${name}'`)
+                    }
+                } else {
+                    // TODO
+                }
+            } else {
+                throw new Error(`'${name}' must be of type 'uniform' or 'texture' but is '${spec.type}'`)
+            }
+        }
+    }
+}
+
 export interface ProgramProps {
     defineValues: DefineValues,
     shaderCode: ShaderCode,
@@ -77,6 +135,11 @@ export function createProgram(ctx: WebGLContext, props: ProgramProps): Program {
     const locations = getLocations(ctx, program, schema)
     const uniformSetters = getUniformSetters(schema)
 
+    if (!isProductionMode) {
+        checkActiveAttributes(ctx, program, schema)
+        checkActiveUniforms(ctx, program, schema)
+    }
+
     let destroyed = false
 
     return {

+ 2 - 2
src/mol-gl/webgl/render-item.ts

@@ -4,7 +4,7 @@
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
  */
 
-import { createAttributeBuffers, createElementsBuffer, ElementsBuffer, createAttributeBuffer, ArrayKind } from './buffer';
+import { createAttributeBuffers, createElementsBuffer, ElementsBuffer, createAttributeBuffer, AttributeKind } from './buffer';
 import { createTextures } from './texture';
 import { WebGLContext } from './context';
 import { ShaderCode } from '../shader-code';
@@ -212,7 +212,7 @@ export function createRenderItem(ctx: WebGLContext, drawMode: DrawMode, shaderCo
                     } else {
                         // console.log('attribute array to small, need to create new attribute', k, value.ref.id, value.ref.version)
                         buffer.destroy()
-                        const { itemSize, divisor } = schema[k] as AttributeSpec<ArrayKind>
+                        const { itemSize, divisor } = schema[k] as AttributeSpec<AttributeKind>
                         attributeBuffers[i] = [k, createAttributeBuffer(ctx, value.ref.value, itemSize, divisor)]
                         valueChanges.attributes = true
                     }

+ 13 - 0
src/mol-gl/webgl/uniform.ts

@@ -25,6 +25,19 @@ export type UniformType = number | Vec2 | Vec3 | Vec4 | Mat3 | Mat4
 export type UniformValues = { [k: string]: ValueCell<UniformType> }
 export type UniformsList = [string, ValueCell<UniformType>][]
 
+export function getUniformType(gl: GLRenderingContext, kind: UniformKind) {
+    switch (kind) {
+        case 'f': return gl.FLOAT
+        case 'i': return gl.INT
+        case 'v2': return gl.FLOAT_VEC2
+        case 'v3': return gl.FLOAT_VEC3
+        case 'v4': return gl.FLOAT_VEC4
+        case 'm3': return gl.FLOAT_MAT3
+        case 'm4': return gl.FLOAT_MAT4
+        default: console.error(`unknown uniform kind '${kind}'`)
+    }
+}
+
 export function setUniform(gl: GLRenderingContext, location: WebGLUniformLocation | null, kind: UniformKind, value: any) {
     switch (kind) {
         case 'f': gl.uniform1f(location, value); break