diff --git a/src/mol-gl/renderable/schema.ts b/src/mol-gl/renderable/schema.ts index e243a029394a6c8a033db86aa3f912344d62a952..233176dd2e3ef1d691cc24e5b2e2d5348ade57ab 100644 --- a/src/mol-gl/renderable/schema.ts +++ b/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> ) } diff --git a/src/mol-gl/webgl/buffer.ts b/src/mol-gl/webgl/buffer.ts index e0d35551b61688c8adc6de93fc379ac39ec7951e..cac6c2946b01e75ba5f62626e9ac3a6c593613b0 100644 --- a/src/mol-gl/webgl/buffer.ts +++ b/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][] diff --git a/src/mol-gl/webgl/program.ts b/src/mol-gl/webgl/program.ts index 6d9658a6613deaa3e80a4009a8964e42a284e789..883bf8ca2224c1cb6c1144b0e1539436371980b0 100644 --- a/src/mol-gl/webgl/program.ts +++ b/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 { diff --git a/src/mol-gl/webgl/render-item.ts b/src/mol-gl/webgl/render-item.ts index 3939c89a10c1f6d48f108d7b5cd575d059ca27a0..842bf50c45ff974432a23ea8d04d184671b37e23 100644 --- a/src/mol-gl/webgl/render-item.ts +++ b/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 } diff --git a/src/mol-gl/webgl/uniform.ts b/src/mol-gl/webgl/uniform.ts index d7e9a123cabc377ed632b68f53777fcf23f5c0fe..bdc7bccc1c2ed3dd7e070ceca149d7e75e780134 100644 --- a/src/mol-gl/webgl/uniform.ts +++ b/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