diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 1b2c52f..bbba342 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -232,6 +232,9 @@ this.emit('context', gl); + // set the latest testing context.. + glCore._testingContext = gl; + // setup the width/height properties and gl viewport this.resize(this.width, this.height); } diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 1b2c52f..bbba342 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -232,6 +232,9 @@ this.emit('context', gl); + // set the latest testing context.. + glCore._testingContext = gl; + // setup the width/height properties and gl viewport this.resize(this.width, this.height); } diff --git a/src/core/shader/Program.js b/src/core/shader/Program.js index 1828d86..44d9b78 100644 --- a/src/core/shader/Program.js +++ b/src/core/shader/Program.js @@ -1,10 +1,9 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; -import extractAttributesFromSrc from './extractAttributesFromSrc'; import generateUniformsSync from './generateUniformsSync'; +import glCore from 'pixi-gl-core'; let UID = 0; -// let math = require('../../../math'); /** * @class * @memberof PIXI @@ -32,35 +31,133 @@ */ this.fragmentSrc = fragmentSrc || Program.defaultFragmentSrc; - // pull out the vertex and shader uniforms if they are not specified.. // currently this does not extract structs only default types - this.uniformData = extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc); - // TODO - 'projectionMatrix|uSampler|translationMatrix'); - - this.attributeData = extractAttributesFromSrc(this.vertexSrc); - - this.uniforms = {}; - - // time to build some getters and setters! - // I guess down the line this could sort of generate an instruction list rather than use dirty ids? - // does the trick for now though! - for (const i in this.uniformData) - { - if (!this.uniforms[i]) - { - this.uniforms[i] = this.uniformData[i].value; - } - } + this.extractData(this.vertexSrc, this.fragmentSrc); // this is where we store shader references.. - // TODO we could cache this! this.glShaders = {}; this.syncUniforms = generateUniformsSync(this.uniformData); + this.id = UID++; } /** + * Extracts the data for a buy creating a small test program + * or reading the src directly. + * @private + * + * @param {string} [vertexSrc] - The source of the vertex shader. + * @param {string} [fragmentSrc] - The source of the fragment shader. + */ + extractData(vertexSrc, fragmentSrc) + { + const gl = glCore._testingContext || Program.getTestingContext; + + if (!gl) + { + // uh oh! no webGL.. lets read uniforms from the strings.. + this.attributeData = {}; + this.uniformData = extractUniformsFromSrc(vertexSrc, fragmentSrc); + } + else + { + vertexSrc = glCore.shader.setPrecision(vertexSrc, 'mediump'); + fragmentSrc = glCore.shader.setPrecision(fragmentSrc, 'mediump'); + + const program = glCore.shader.compileProgram(gl, vertexSrc, fragmentSrc); + + this.attributeData = this.getAttributeData(program, gl); + this.uniformData = this.getUniformData(program, gl); + + gl.deleteProgram(program); + } + } + + /** + * returns the attribute data from the program + * @private + * + * @param {webGL-program} [program] - the webgl program + * @param {contex} [gl] - the webGL context + * + * @returns {object} the attribute data for this program + */ + getAttributeData(program, gl) + { + const attributes = {}; + const attributesArray = []; + + const totalAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); + + for (let i = 0; i < totalAttributes; i++) + { + const attribData = gl.getActiveAttrib(program, i); + const type = glCore.shader.mapType(gl, attribData.type); + + /*eslint-disable */ + const data = { + type: type, + name: attribData.name, + size: glCore.shader.mapSize(type), + location: 0, + }; + /*eslint-enable */ + + attributes[attribData.name] = data; + attributesArray.push(data); + } + + attributesArray.sort((a, b) => (a.name > b.name) ? 1 : -1); // eslint-disable-line no-confusing-arrow + + for (let i = 0; i < attributesArray.length; i++) + { + attributesArray[i].location = i; + } + + return attributes; + } + + /** + * returns the uniform data from the program + * @private + * + * @param {webGL-program} [program] - the webgl program + * @param {contex} [gl] - the webGL context + * + * @returns {object} the uniform data for this program + */ + getUniformData(program, gl) + { + const uniforms = {}; + + const totalUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + + // TODO expose this as a prop? + const maskRegex = new RegExp('^(projectionMatrix|uSampler|translationMatrix)$'); + + for (let i = 0; i < totalUniforms; i++) + { + const uniformData = gl.getActiveUniform(program, i); + const name = uniformData.name.replace(/\[.*?\]/, ''); + const type = glCore.shader.mapType(gl, uniformData.type); + + if (!name.match(maskRegex)) + { + /*eslint-disable */ + uniforms[name] = { + type: type, + size: uniformData.size, + value: glCore.shader.defaultValue(type, uniformData.size), + }; + /*eslint-enable */ + } + } + + return uniforms; + } + + /** * The default vertex shader source * * @static @@ -73,14 +170,11 @@ 'attribute vec2 aTextureCoord;', 'uniform mat3 projectionMatrix;', - 'uniform mat3 filterMatrix;', 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', 'void main(void){', ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vFilterCoord = ( filterMatrix * vec3( aTextureCoord, 1.0) ).xy;', ' vTextureCoord = aTextureCoord ;', '}', ].join('\n'); @@ -96,28 +190,44 @@ { return [ 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', 'uniform sampler2D uSampler;', - 'uniform sampler2D filterSampler;', 'void main(void){', - ' vec4 masky = texture2D(filterSampler, vFilterCoord);', - ' vec4 sample = texture2D(uSampler, vTextureCoord);', - ' vec4 color;', - ' if(mod(vFilterCoord.x, 1.0) > 0.5)', - ' {', - ' color = vec4(1.0, 0.0, 0.0, 1.0);', - ' }', - ' else', - ' {', - ' color = vec4(0.0, 1.0, 0.0, 1.0);', - ' }', - ' gl_FragColor = mix(sample, masky, 0.5);', - ' gl_FragColor *= sample.a;', + ' gl_FragColor *= texture2D(uSampler, vTextureCoord);', '}', ].join('\n'); } + + /** + * returns a little webGL context to use for program inspection. + * + * @static + * @private + * @returns {webGL-context} a gl context to test with + */ + static getTestingContext() + { + try + { + if (!Program.testingContext) + { + const canvas = document.createElement('canvas'); + + canvas.width = 1; + canvas.height = 1; + + Program.testingContext = glCore.createContext(canvas); + } + } + + catch (e) + { + // eslint-disable-line no-empty + } + + return Program.testingContext; + } } export default Program; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 1b2c52f..bbba342 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -232,6 +232,9 @@ this.emit('context', gl); + // set the latest testing context.. + glCore._testingContext = gl; + // setup the width/height properties and gl viewport this.resize(this.width, this.height); } diff --git a/src/core/shader/Program.js b/src/core/shader/Program.js index 1828d86..44d9b78 100644 --- a/src/core/shader/Program.js +++ b/src/core/shader/Program.js @@ -1,10 +1,9 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; -import extractAttributesFromSrc from './extractAttributesFromSrc'; import generateUniformsSync from './generateUniformsSync'; +import glCore from 'pixi-gl-core'; let UID = 0; -// let math = require('../../../math'); /** * @class * @memberof PIXI @@ -32,35 +31,133 @@ */ this.fragmentSrc = fragmentSrc || Program.defaultFragmentSrc; - // pull out the vertex and shader uniforms if they are not specified.. // currently this does not extract structs only default types - this.uniformData = extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc); - // TODO - 'projectionMatrix|uSampler|translationMatrix'); - - this.attributeData = extractAttributesFromSrc(this.vertexSrc); - - this.uniforms = {}; - - // time to build some getters and setters! - // I guess down the line this could sort of generate an instruction list rather than use dirty ids? - // does the trick for now though! - for (const i in this.uniformData) - { - if (!this.uniforms[i]) - { - this.uniforms[i] = this.uniformData[i].value; - } - } + this.extractData(this.vertexSrc, this.fragmentSrc); // this is where we store shader references.. - // TODO we could cache this! this.glShaders = {}; this.syncUniforms = generateUniformsSync(this.uniformData); + this.id = UID++; } /** + * Extracts the data for a buy creating a small test program + * or reading the src directly. + * @private + * + * @param {string} [vertexSrc] - The source of the vertex shader. + * @param {string} [fragmentSrc] - The source of the fragment shader. + */ + extractData(vertexSrc, fragmentSrc) + { + const gl = glCore._testingContext || Program.getTestingContext; + + if (!gl) + { + // uh oh! no webGL.. lets read uniforms from the strings.. + this.attributeData = {}; + this.uniformData = extractUniformsFromSrc(vertexSrc, fragmentSrc); + } + else + { + vertexSrc = glCore.shader.setPrecision(vertexSrc, 'mediump'); + fragmentSrc = glCore.shader.setPrecision(fragmentSrc, 'mediump'); + + const program = glCore.shader.compileProgram(gl, vertexSrc, fragmentSrc); + + this.attributeData = this.getAttributeData(program, gl); + this.uniformData = this.getUniformData(program, gl); + + gl.deleteProgram(program); + } + } + + /** + * returns the attribute data from the program + * @private + * + * @param {webGL-program} [program] - the webgl program + * @param {contex} [gl] - the webGL context + * + * @returns {object} the attribute data for this program + */ + getAttributeData(program, gl) + { + const attributes = {}; + const attributesArray = []; + + const totalAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); + + for (let i = 0; i < totalAttributes; i++) + { + const attribData = gl.getActiveAttrib(program, i); + const type = glCore.shader.mapType(gl, attribData.type); + + /*eslint-disable */ + const data = { + type: type, + name: attribData.name, + size: glCore.shader.mapSize(type), + location: 0, + }; + /*eslint-enable */ + + attributes[attribData.name] = data; + attributesArray.push(data); + } + + attributesArray.sort((a, b) => (a.name > b.name) ? 1 : -1); // eslint-disable-line no-confusing-arrow + + for (let i = 0; i < attributesArray.length; i++) + { + attributesArray[i].location = i; + } + + return attributes; + } + + /** + * returns the uniform data from the program + * @private + * + * @param {webGL-program} [program] - the webgl program + * @param {contex} [gl] - the webGL context + * + * @returns {object} the uniform data for this program + */ + getUniformData(program, gl) + { + const uniforms = {}; + + const totalUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS); + + // TODO expose this as a prop? + const maskRegex = new RegExp('^(projectionMatrix|uSampler|translationMatrix)$'); + + for (let i = 0; i < totalUniforms; i++) + { + const uniformData = gl.getActiveUniform(program, i); + const name = uniformData.name.replace(/\[.*?\]/, ''); + const type = glCore.shader.mapType(gl, uniformData.type); + + if (!name.match(maskRegex)) + { + /*eslint-disable */ + uniforms[name] = { + type: type, + size: uniformData.size, + value: glCore.shader.defaultValue(type, uniformData.size), + }; + /*eslint-enable */ + } + } + + return uniforms; + } + + /** * The default vertex shader source * * @static @@ -73,14 +170,11 @@ 'attribute vec2 aTextureCoord;', 'uniform mat3 projectionMatrix;', - 'uniform mat3 filterMatrix;', 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', 'void main(void){', ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vFilterCoord = ( filterMatrix * vec3( aTextureCoord, 1.0) ).xy;', ' vTextureCoord = aTextureCoord ;', '}', ].join('\n'); @@ -96,28 +190,44 @@ { return [ 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', 'uniform sampler2D uSampler;', - 'uniform sampler2D filterSampler;', 'void main(void){', - ' vec4 masky = texture2D(filterSampler, vFilterCoord);', - ' vec4 sample = texture2D(uSampler, vTextureCoord);', - ' vec4 color;', - ' if(mod(vFilterCoord.x, 1.0) > 0.5)', - ' {', - ' color = vec4(1.0, 0.0, 0.0, 1.0);', - ' }', - ' else', - ' {', - ' color = vec4(0.0, 1.0, 0.0, 1.0);', - ' }', - ' gl_FragColor = mix(sample, masky, 0.5);', - ' gl_FragColor *= sample.a;', + ' gl_FragColor *= texture2D(uSampler, vTextureCoord);', '}', ].join('\n'); } + + /** + * returns a little webGL context to use for program inspection. + * + * @static + * @private + * @returns {webGL-context} a gl context to test with + */ + static getTestingContext() + { + try + { + if (!Program.testingContext) + { + const canvas = document.createElement('canvas'); + + canvas.width = 1; + canvas.height = 1; + + Program.testingContext = glCore.createContext(canvas); + } + } + + catch (e) + { + // eslint-disable-line no-empty + } + + return Program.testingContext; + } } export default Program; diff --git a/src/core/shader/extractAttributesFromSrc.js b/src/core/shader/extractAttributesFromSrc.js deleted file mode 100644 index f60da6f..0000000 --- a/src/core/shader/extractAttributesFromSrc.js +++ /dev/null @@ -1,70 +0,0 @@ -import glCore from 'pixi-gl-core'; - -const defaultValue = glCore.shader.defaultValue; - -function extractAttributesFromSrc(vertexSrc, mask) -{ - const vertAttributes = extractAttributesFromString(vertexSrc, mask); - - return vertAttributes; -} - -function extractAttributesFromString(string) -{ - const attributesArray = []; - let nameSplit; - - // clean the lines a little - remove extra spaces / teabs etc - // then split along ';' - const lines = string.replace(/\s+/g, ' ') - .split(/\s*;\s*/); - - // loop through.. - for (let i = 0; i < lines.length; i++) - { - const line = lines[i].trim(); - - if (line.indexOf('attribute') > -1) - { - const splitLine = line.split(' '); - const startIndex = splitLine.indexOf('attribute'); - - const type = splitLine[startIndex + 1]; - - let name = splitLine[startIndex + 2]; - let size = 1; - - if (name.indexOf('[') > -1) - { - // array! - nameSplit = name.split(/\[|\]/); - name = nameSplit[0]; - size *= Number(nameSplit[1]); - } - - attributesArray.push({ - value: defaultValue(type, size), - name, - location: 0, - type, - }); - } - } - - attributesArray.sort((a, b) => (a.name > b.name) ? 1 : -1); // eslint-disable-line no-confusing-arrow - - const attributes = {}; - - // now lets sort them alphabetically.. - for (let i = 0; i < attributesArray.length; i++) - { - const attrib = attributesArray[i]; - - attrib.location = i; - attributes[attrib.name] = attrib; - } - - return attributes; -} - -export default extractAttributesFromSrc;