diff --git a/README.md b/README.md index f5ea1a9..5aecd9d 100644 --- a/README.md +++ b/README.md @@ -36,15 +36,6 @@ Deletes the shader program and associated resources. ### `gl` The WebGL context associated to the shader -### `handle` -A handle to the underlying WebGL program object - -### `vertexShader` -A handle to the underlying WebGL fragment shader object - -### `fragmentShader` -A handle to the underlying WebGL vertex shader object - ## Uniforms The uniforms for the shader program are packaged up as properties in the `shader.uniforms` object. For example, to update a scalar uniform you can just assign to it: @@ -107,9 +98,7 @@ Or you can read the currently bound location back by just accessing it: console.log(attrib.location) ``` -Internally, these methods just call [`gl.bindAttribLocation`](http://www.khronos.org/opengles/sdk/docs/man/xhtml/glBindAttribLocation.xml) and access the stored location. - -**WARNING** Changing the attribute location requires recompiling the program. Do not dynamically modify this variable in your render loop. +**WARNING** Changing the attribute location requires recompiling the program. This recompilation is deferred until the next call to `.bind()` ### `attrib.pointer([type, normalized, stride, offset])` A shortcut for `gl.vertexAttribPointer`/`gl.enableVertexAttribArray`. See the [OpenGL man page for details on how this works](http://www.khronos.org/opengles/sdk/docs/man/xhtml/glVertexAttribPointer.xml). The main difference here is that the WebGL context, size and index are known and so these parameters are bound. @@ -129,6 +118,5 @@ console.log(shader.types) This reflects the uniform and attribute parameters that were passed to the shader constructor. - ## Credits (c) 2013 Mikola Lysenko. MIT License \ No newline at end of file diff --git a/lib/create-attributes.js b/lib/create-attributes.js index 1806bbd..1efb93a 100644 --- a/lib/create-attributes.js +++ b/lib/create-attributes.js @@ -2,42 +2,66 @@ module.exports = createAttributeWrapper -//Shader attribute class -function ShaderAttribute(gl, program, location, dimension, name, constFunc, relink) { - this._gl = gl - this._program = program - this._location = location +function ShaderAttribute( + gl + , wrapper + , index + , locations + , dimension) { + this._gl = gl + this._wrapper = wrapper + this._index = index + this._locations = locations this._dimension = dimension - this._name = name - this._constFunc = constFunc - this._relink = relink } var proto = ShaderAttribute.prototype -proto.pointer = function setAttribPointer(type, normalized, stride, offset) { - var gl = this._gl - gl.vertexAttribPointer(this._location, this._dimension, type||gl.FLOAT, !!normalized, stride||0, offset||0) - this._gl.enableVertexAttribArray(this._location) +proto.pointer = function setAttribPointer( + type + , normalized + , stride + , offset) { + + var self = this + var gl = self._gl + var location = self._locations[self._index] + + gl.vertexAttribPointer( + location + , self._dimension + , type || gl.FLOAT + , !!normalized + , stride || 0 + , offset || 0) + + this._gl.enableVertexAttribArray(location) } Object.defineProperty(proto, 'location', { get: function() { - return this._location + return this._locations[this._index] } , set: function(v) { - if(v !== this._location) { - this._location = v - this._gl.bindAttribLocation(this._program, v, this._name) - this._gl.linkProgram(this._program) - this._relink() + if(v !== this._locations[this._index]) { + this._locations[this._index] = v + this._wrapper.program = null } } }) //Adds a vector attribute to obj -function addVectorAttribute(gl, program, location, dimension, obj, name, doLink) { +function addVectorAttribute( + gl + , wrapper + , index + , locations + , dimension + , obj + , name) { + + //Construct constant function var constFuncArgs = [ 'gl', 'v' ] var varNames = [] for(var i=0; i 4) { throw new Error('gl-shader: Invalid data type for attribute ' + name + ': ' + type) } - addVectorAttribute(gl, program, location, d, obj, name, doLink) + addVectorAttribute( + gl + , wrapper + , i + , locations + , d + , obj + , name) } else { throw new Error('gl-shader: Unknown data type for attribute ' + name + ': ' + type) } diff --git a/lib/create-uniforms.js b/lib/create-uniforms.js index 8185354..fc82ee4 100644 --- a/lib/create-uniforms.js +++ b/lib/create-uniforms.js @@ -12,12 +12,15 @@ function identity(x) { } //Create shims for uniforms -function createUniformWrapper(gl, program, uniforms, locations) { +function createUniformWrapper(gl, wrapper, uniforms, locations) { function makeGetter(index) { - var proc = new Function('gl', 'prog', 'locations', - 'return function(){return gl.getUniform(prog,locations[' + index + '])}') - return proc(gl, program, locations) + var proc = new Function( + 'gl' + , 'wrapper' + , 'locations' + , 'return function(){return gl.getUniform(wrapper.program,locations[' + index + '])}') + return proc(gl, wrapper, locations) } function makePropSetter(path, index, type) { @@ -92,8 +95,8 @@ function createUniformWrapper(gl, program, uniforms, locations) { } } code.push('return obj}') - var proc = new Function('gl', 'prog', 'locations', code.join('\n')) - return proc(gl, program, locations) + var proc = new Function('gl', 'locations', code.join('\n')) + return proc(gl, locations) } function defaultValue(type) { diff --git a/lib/shader-cache.js b/lib/shader-cache.js new file mode 100644 index 0000000..45eb415 --- /dev/null +++ b/lib/shader-cache.js @@ -0,0 +1,129 @@ +'use strict' + +exports.shader = getShaderReference +exports.program = createProgram + +var weakMap = typeof WeakMap === 'undefined' ? require('weakmap-shim') : WeakMap +var CACHE = new weakMap() + +var SHADER_COUNTER = 0 + +function ShaderReference(id, src, type, shader, programs, count, cache) { + this.id = id + this.src = src + this.type = type + this.shader = shader + this.count = count + this.programs = [] + this.cache = cache +} + +ShaderReference.prototype.dispose = function() { + if(--this.count === 0) { + var cache = this.cache + var gl = cache.gl + + //Remove program references + var programs = this.programs + for(var i=0, n=programs.length; i b.name) { + return 1 + } + return 0 + }) + + //Extract names + var attributeNames = attributes.map(function(attr) { + return attr.name + }) + + //Get default location + var attributeLocations = attributes.map(function(attr) { + if('location' in attr) { + return attr.location|0 + } else { + return -1 + } + }) + + //For all unspecified attributes, assign them lexicographically min attribute + var curLocation = 0 + for(var i=0; i= 0) { + curLocation += 1 + } + attributeLocations[i] = curLocation + } } - this.attributes = createAttributeWrapper( - gl, - program, - attributes, - doLink - ) - - Object.defineProperty(this, 'uniforms', createUniformWrapper( - gl, - program, - uniforms, - locations - )) -} + //Relinks all uniforms + function relink() { + + //Build program + wrapper.program = shaderCache.program( + gl + , wrapper._vref + , wrapper._fref + , attributeNames + , attributeLocations) -//Relinks all uniforms -function relinkUniforms(gl, program, locations, uniforms) { - for(var i=0; i