diff --git a/src/core/index.js b/src/core/index.js index 1b35344..f7a817c 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -43,6 +43,7 @@ export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; export { default as Shader } from './shader/Shader'; +export { default as Program } from './shader/Program'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; export { default as Application } from './Application'; diff --git a/src/core/index.js b/src/core/index.js index 1b35344..f7a817c 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -43,6 +43,7 @@ export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; export { default as Shader } from './shader/Shader'; +export { default as Program } from './shader/Program'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; export { default as Application } from './Application'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 5ac65b3..d0896b7 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -40,7 +40,8 @@ */ bindShader(shader, dontSync) { - const glShader = shader.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. // if (this.shader !== shader) @@ -64,7 +65,7 @@ */ setUniforms(uniforms) { - const shader = this.shader; + const shader = this.shader.program; const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); @@ -90,16 +91,17 @@ */ generateShader(shader) { + const program = shader.program; const attribMap = {}; - for (const i in shader.attributeData) + for (const i in program.attributeData) { - attribMap[i] = shader.attributeData[i].location; + attribMap[i] = program.attributeData[i].location; } - const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - shader.glShaders[this.renderer.CONTEXT_UID] = glShader; + program.glShaders[this.renderer.CONTEXT_UID] = glShader; return glShader; } diff --git a/src/core/index.js b/src/core/index.js index 1b35344..f7a817c 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -43,6 +43,7 @@ export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; export { default as Shader } from './shader/Shader'; +export { default as Program } from './shader/Program'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; export { default as Application } from './Application'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 5ac65b3..d0896b7 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -40,7 +40,8 @@ */ bindShader(shader, dontSync) { - const glShader = shader.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. // if (this.shader !== shader) @@ -64,7 +65,7 @@ */ setUniforms(uniforms) { - const shader = this.shader; + const shader = this.shader.program; const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); @@ -90,16 +91,17 @@ */ generateShader(shader) { + const program = shader.program; const attribMap = {}; - for (const i in shader.attributeData) + for (const i in program.attributeData) { - attribMap[i] = shader.attributeData[i].location; + attribMap[i] = program.attributeData[i].location; } - const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - shader.glShaders[this.renderer.CONTEXT_UID] = glShader; + program.glShaders[this.renderer.CONTEXT_UID] = glShader; return glShader; } diff --git a/src/core/shader/Program.js b/src/core/shader/Program.js new file mode 100644 index 0000000..da7bcf1 --- /dev/null +++ b/src/core/shader/Program.js @@ -0,0 +1,123 @@ +import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; +import generateUniformsSync from './generateUniformsSync'; + +let UID = 0; + +// let math = require('../../../math'); +/** + * @class + * @memberof PIXI + * @extends PIXI.Shader + */ +class Program +{ + /** + * @param {string} [vertexSrc] - The source of the vertex shader. + * @param {string} [fragmentSrc] - The source of the fragment shader. + */ + constructor(vertexSrc, fragmentSrc) + { + /** + * The vertex shader. + * + * @member {string} + */ + this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; + + /** + * The fragment shader. + * + * @member {string} + */ + this.fragmentSrc = fragmentSrc || Shader.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 is where we store shader references.. + // TODO we could cache this! + this.glShaders = {}; + + this.syncUniforms = generateUniformsSync(this.uniformData); + this.id = UID++; + } + + /** + * The default vertex shader source + * + * @static + * @constant + */ + static get defaultVertexSrc() + { + return [ + 'attribute vec2 aVertexPosition;', + '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'); + } + + /** + * The default fragment shader source + * + * @static + * @constant + */ + static get defaultFragmentSrc() + { + 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;', + '}', + ].join('\n'); + } +} + +export default Program; diff --git a/src/core/index.js b/src/core/index.js index 1b35344..f7a817c 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -43,6 +43,7 @@ export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; export { default as Shader } from './shader/Shader'; +export { default as Program } from './shader/Program'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; export { default as Application } from './Application'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 5ac65b3..d0896b7 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -40,7 +40,8 @@ */ bindShader(shader, dontSync) { - const glShader = shader.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. // if (this.shader !== shader) @@ -64,7 +65,7 @@ */ setUniforms(uniforms) { - const shader = this.shader; + const shader = this.shader.program; const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); @@ -90,16 +91,17 @@ */ generateShader(shader) { + const program = shader.program; const attribMap = {}; - for (const i in shader.attributeData) + for (const i in program.attributeData) { - attribMap[i] = shader.attributeData[i].location; + attribMap[i] = program.attributeData[i].location; } - const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - shader.glShaders[this.renderer.CONTEXT_UID] = glShader; + program.glShaders[this.renderer.CONTEXT_UID] = glShader; return glShader; } diff --git a/src/core/shader/Program.js b/src/core/shader/Program.js new file mode 100644 index 0000000..da7bcf1 --- /dev/null +++ b/src/core/shader/Program.js @@ -0,0 +1,123 @@ +import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; +import generateUniformsSync from './generateUniformsSync'; + +let UID = 0; + +// let math = require('../../../math'); +/** + * @class + * @memberof PIXI + * @extends PIXI.Shader + */ +class Program +{ + /** + * @param {string} [vertexSrc] - The source of the vertex shader. + * @param {string} [fragmentSrc] - The source of the fragment shader. + */ + constructor(vertexSrc, fragmentSrc) + { + /** + * The vertex shader. + * + * @member {string} + */ + this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; + + /** + * The fragment shader. + * + * @member {string} + */ + this.fragmentSrc = fragmentSrc || Shader.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 is where we store shader references.. + // TODO we could cache this! + this.glShaders = {}; + + this.syncUniforms = generateUniformsSync(this.uniformData); + this.id = UID++; + } + + /** + * The default vertex shader source + * + * @static + * @constant + */ + static get defaultVertexSrc() + { + return [ + 'attribute vec2 aVertexPosition;', + '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'); + } + + /** + * The default fragment shader source + * + * @static + * @constant + */ + static get defaultFragmentSrc() + { + 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;', + '}', + ].join('\n'); + } +} + +export default Program; diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 30f5b4d..e488400 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,6 +1,8 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; import extractAttributesFromSrc from './extractAttributesFromSrc'; import generateUniformsSync from './generateUniformsSync'; +import Program from './Program'; +import { ProgramCache } from '../utils'; let UID = 0; @@ -17,108 +19,47 @@ * @param {string} [fragmentSrc] - The source of the fragment shader. * @param {object} [uniforms] - Custom uniforms to use to augment the built-in ones. */ - constructor(vertexSrc, fragmentSrc, uniforms) + constructor(program, uniforms) { - /** - * The vertex shader. - * - * @member {string} - */ - this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; - - /** - * The fragment shader. - * - * @member {string} - */ - this.fragmentSrc = fragmentSrc || Shader.defaultFragmentSrc; - - // pull out the vertex and shader uniforms if they are not specified.. - // currently this does not extract structs only default types - this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc); - // TODO - 'projectionMatrix|uSampler|translationMatrix'); - - this.attributeData = extractAttributesFromSrc(this.vertexSrc); - - this.uniforms = {}; + this.program = program; + this.uniforms = 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) + for (const i in program.uniformData) { - if (!this.uniforms[i]) + const uniform = this.uniforms[i]; + + if (!uniform) { - this.uniforms[i] = this.uniformData[i].value; + + this.uniforms[i] = program.uniformData[i].value; + } + else + { + if(uniform instanceof Array) + { + this.uniforms[i] = new Float32Array(uniform) + } } } - - // this is where we store shader references.. - // TODO we could cache this! - this.glShaders = {}; - - this.syncUniforms = generateUniformsSync(this.uniformData); - this.id = UID++; } - /** - * The default vertex shader source - * - * @static - * @constant - */ - static get defaultVertexSrc() + static from(vertexSrc, fragmentSrc, uniforms) { - return [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', + const key = vertexSrc + fragmentSrc; - 'uniform mat3 projectionMatrix;', - 'uniform mat3 filterMatrix;', + let program = ProgramCache[key]; - 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', + if(!program) + { + ProgramCache[key] = program = new Program(vertexSrc,fragmentSrc); + } - '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'); + return new Shader(program, uniforms); } - /** - * The default fragment shader source - * - * @static - * @constant - */ - static get defaultFragmentSrc() - { - 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;', - '}', - ].join('\n'); - } } export default Shader; diff --git a/src/core/index.js b/src/core/index.js index 1b35344..f7a817c 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -43,6 +43,7 @@ export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; export { default as Shader } from './shader/Shader'; +export { default as Program } from './shader/Program'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; export { default as Application } from './Application'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 5ac65b3..d0896b7 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -40,7 +40,8 @@ */ bindShader(shader, dontSync) { - const glShader = shader.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. // if (this.shader !== shader) @@ -64,7 +65,7 @@ */ setUniforms(uniforms) { - const shader = this.shader; + const shader = this.shader.program; const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); @@ -90,16 +91,17 @@ */ generateShader(shader) { + const program = shader.program; const attribMap = {}; - for (const i in shader.attributeData) + for (const i in program.attributeData) { - attribMap[i] = shader.attributeData[i].location; + attribMap[i] = program.attributeData[i].location; } - const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - shader.glShaders[this.renderer.CONTEXT_UID] = glShader; + program.glShaders[this.renderer.CONTEXT_UID] = glShader; return glShader; } diff --git a/src/core/shader/Program.js b/src/core/shader/Program.js new file mode 100644 index 0000000..da7bcf1 --- /dev/null +++ b/src/core/shader/Program.js @@ -0,0 +1,123 @@ +import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; +import generateUniformsSync from './generateUniformsSync'; + +let UID = 0; + +// let math = require('../../../math'); +/** + * @class + * @memberof PIXI + * @extends PIXI.Shader + */ +class Program +{ + /** + * @param {string} [vertexSrc] - The source of the vertex shader. + * @param {string} [fragmentSrc] - The source of the fragment shader. + */ + constructor(vertexSrc, fragmentSrc) + { + /** + * The vertex shader. + * + * @member {string} + */ + this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; + + /** + * The fragment shader. + * + * @member {string} + */ + this.fragmentSrc = fragmentSrc || Shader.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 is where we store shader references.. + // TODO we could cache this! + this.glShaders = {}; + + this.syncUniforms = generateUniformsSync(this.uniformData); + this.id = UID++; + } + + /** + * The default vertex shader source + * + * @static + * @constant + */ + static get defaultVertexSrc() + { + return [ + 'attribute vec2 aVertexPosition;', + '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'); + } + + /** + * The default fragment shader source + * + * @static + * @constant + */ + static get defaultFragmentSrc() + { + 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;', + '}', + ].join('\n'); + } +} + +export default Program; diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 30f5b4d..e488400 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,6 +1,8 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; import extractAttributesFromSrc from './extractAttributesFromSrc'; import generateUniformsSync from './generateUniformsSync'; +import Program from './Program'; +import { ProgramCache } from '../utils'; let UID = 0; @@ -17,108 +19,47 @@ * @param {string} [fragmentSrc] - The source of the fragment shader. * @param {object} [uniforms] - Custom uniforms to use to augment the built-in ones. */ - constructor(vertexSrc, fragmentSrc, uniforms) + constructor(program, uniforms) { - /** - * The vertex shader. - * - * @member {string} - */ - this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; - - /** - * The fragment shader. - * - * @member {string} - */ - this.fragmentSrc = fragmentSrc || Shader.defaultFragmentSrc; - - // pull out the vertex and shader uniforms if they are not specified.. - // currently this does not extract structs only default types - this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc); - // TODO - 'projectionMatrix|uSampler|translationMatrix'); - - this.attributeData = extractAttributesFromSrc(this.vertexSrc); - - this.uniforms = {}; + this.program = program; + this.uniforms = 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) + for (const i in program.uniformData) { - if (!this.uniforms[i]) + const uniform = this.uniforms[i]; + + if (!uniform) { - this.uniforms[i] = this.uniformData[i].value; + + this.uniforms[i] = program.uniformData[i].value; + } + else + { + if(uniform instanceof Array) + { + this.uniforms[i] = new Float32Array(uniform) + } } } - - // this is where we store shader references.. - // TODO we could cache this! - this.glShaders = {}; - - this.syncUniforms = generateUniformsSync(this.uniformData); - this.id = UID++; } - /** - * The default vertex shader source - * - * @static - * @constant - */ - static get defaultVertexSrc() + static from(vertexSrc, fragmentSrc, uniforms) { - return [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', + const key = vertexSrc + fragmentSrc; - 'uniform mat3 projectionMatrix;', - 'uniform mat3 filterMatrix;', + let program = ProgramCache[key]; - 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', + if(!program) + { + ProgramCache[key] = program = new Program(vertexSrc,fragmentSrc); + } - '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'); + return new Shader(program, uniforms); } - /** - * The default fragment shader source - * - * @static - * @constant - */ - static get defaultFragmentSrc() - { - 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;', - '}', - ].join('\n'); - } } export default Shader; diff --git a/src/core/shader/generateUniformsSync.js b/src/core/shader/generateUniformsSync.js index b0e6fa8..54a172d 100644 --- a/src/core/shader/generateUniformsSync.js +++ b/src/core/shader/generateUniformsSync.js @@ -17,6 +17,7 @@ cacheValue[0] = value[0]; cacheValue[1] = value[1]; cacheValue[2] = value[2]; + gl.uniform3f(location, value[0], value[1], value[2]) }`, vec4: 'gl.uniform4f(location, value[0], value[1], value[2], value[3])', diff --git a/src/core/index.js b/src/core/index.js index 1b35344..f7a817c 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -43,6 +43,7 @@ export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; export { default as Shader } from './shader/Shader'; +export { default as Program } from './shader/Program'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; export { default as Application } from './Application'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 5ac65b3..d0896b7 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -40,7 +40,8 @@ */ bindShader(shader, dontSync) { - const glShader = shader.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. // if (this.shader !== shader) @@ -64,7 +65,7 @@ */ setUniforms(uniforms) { - const shader = this.shader; + const shader = this.shader.program; const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); @@ -90,16 +91,17 @@ */ generateShader(shader) { + const program = shader.program; const attribMap = {}; - for (const i in shader.attributeData) + for (const i in program.attributeData) { - attribMap[i] = shader.attributeData[i].location; + attribMap[i] = program.attributeData[i].location; } - const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - shader.glShaders[this.renderer.CONTEXT_UID] = glShader; + program.glShaders[this.renderer.CONTEXT_UID] = glShader; return glShader; } diff --git a/src/core/shader/Program.js b/src/core/shader/Program.js new file mode 100644 index 0000000..da7bcf1 --- /dev/null +++ b/src/core/shader/Program.js @@ -0,0 +1,123 @@ +import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; +import generateUniformsSync from './generateUniformsSync'; + +let UID = 0; + +// let math = require('../../../math'); +/** + * @class + * @memberof PIXI + * @extends PIXI.Shader + */ +class Program +{ + /** + * @param {string} [vertexSrc] - The source of the vertex shader. + * @param {string} [fragmentSrc] - The source of the fragment shader. + */ + constructor(vertexSrc, fragmentSrc) + { + /** + * The vertex shader. + * + * @member {string} + */ + this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; + + /** + * The fragment shader. + * + * @member {string} + */ + this.fragmentSrc = fragmentSrc || Shader.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 is where we store shader references.. + // TODO we could cache this! + this.glShaders = {}; + + this.syncUniforms = generateUniformsSync(this.uniformData); + this.id = UID++; + } + + /** + * The default vertex shader source + * + * @static + * @constant + */ + static get defaultVertexSrc() + { + return [ + 'attribute vec2 aVertexPosition;', + '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'); + } + + /** + * The default fragment shader source + * + * @static + * @constant + */ + static get defaultFragmentSrc() + { + 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;', + '}', + ].join('\n'); + } +} + +export default Program; diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 30f5b4d..e488400 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,6 +1,8 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; import extractAttributesFromSrc from './extractAttributesFromSrc'; import generateUniformsSync from './generateUniformsSync'; +import Program from './Program'; +import { ProgramCache } from '../utils'; let UID = 0; @@ -17,108 +19,47 @@ * @param {string} [fragmentSrc] - The source of the fragment shader. * @param {object} [uniforms] - Custom uniforms to use to augment the built-in ones. */ - constructor(vertexSrc, fragmentSrc, uniforms) + constructor(program, uniforms) { - /** - * The vertex shader. - * - * @member {string} - */ - this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; - - /** - * The fragment shader. - * - * @member {string} - */ - this.fragmentSrc = fragmentSrc || Shader.defaultFragmentSrc; - - // pull out the vertex and shader uniforms if they are not specified.. - // currently this does not extract structs only default types - this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc); - // TODO - 'projectionMatrix|uSampler|translationMatrix'); - - this.attributeData = extractAttributesFromSrc(this.vertexSrc); - - this.uniforms = {}; + this.program = program; + this.uniforms = 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) + for (const i in program.uniformData) { - if (!this.uniforms[i]) + const uniform = this.uniforms[i]; + + if (!uniform) { - this.uniforms[i] = this.uniformData[i].value; + + this.uniforms[i] = program.uniformData[i].value; + } + else + { + if(uniform instanceof Array) + { + this.uniforms[i] = new Float32Array(uniform) + } } } - - // this is where we store shader references.. - // TODO we could cache this! - this.glShaders = {}; - - this.syncUniforms = generateUniformsSync(this.uniformData); - this.id = UID++; } - /** - * The default vertex shader source - * - * @static - * @constant - */ - static get defaultVertexSrc() + static from(vertexSrc, fragmentSrc, uniforms) { - return [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', + const key = vertexSrc + fragmentSrc; - 'uniform mat3 projectionMatrix;', - 'uniform mat3 filterMatrix;', + let program = ProgramCache[key]; - 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', + if(!program) + { + ProgramCache[key] = program = new Program(vertexSrc,fragmentSrc); + } - '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'); + return new Shader(program, uniforms); } - /** - * The default fragment shader source - * - * @static - * @constant - */ - static get defaultFragmentSrc() - { - 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;', - '}', - ].join('\n'); - } } export default Shader; diff --git a/src/core/shader/generateUniformsSync.js b/src/core/shader/generateUniformsSync.js index b0e6fa8..54a172d 100644 --- a/src/core/shader/generateUniformsSync.js +++ b/src/core/shader/generateUniformsSync.js @@ -17,6 +17,7 @@ cacheValue[0] = value[0]; cacheValue[1] = value[1]; cacheValue[2] = value[2]; + gl.uniform3f(location, value[0], value[1], value[2]) }`, vec4: 'gl.uniform4f(location, value[0], value[1], value[2], value[3])', diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 2a7497b..6ff609f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -360,3 +360,11 @@ * @private */ export const BaseTextureCache = {}; + +/** + * @todo Describe property usage + * + * @memberof PIXI.utils + * @private + */ +export const ProgramCache = {}; diff --git a/src/core/index.js b/src/core/index.js index 1b35344..f7a817c 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -43,6 +43,7 @@ export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; export { default as Shader } from './shader/Shader'; +export { default as Program } from './shader/Program'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; export { default as Application } from './Application'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 5ac65b3..d0896b7 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -40,7 +40,8 @@ */ bindShader(shader, dontSync) { - const glShader = shader.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. // if (this.shader !== shader) @@ -64,7 +65,7 @@ */ setUniforms(uniforms) { - const shader = this.shader; + const shader = this.shader.program; const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); @@ -90,16 +91,17 @@ */ generateShader(shader) { + const program = shader.program; const attribMap = {}; - for (const i in shader.attributeData) + for (const i in program.attributeData) { - attribMap[i] = shader.attributeData[i].location; + attribMap[i] = program.attributeData[i].location; } - const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - shader.glShaders[this.renderer.CONTEXT_UID] = glShader; + program.glShaders[this.renderer.CONTEXT_UID] = glShader; return glShader; } diff --git a/src/core/shader/Program.js b/src/core/shader/Program.js new file mode 100644 index 0000000..da7bcf1 --- /dev/null +++ b/src/core/shader/Program.js @@ -0,0 +1,123 @@ +import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; +import generateUniformsSync from './generateUniformsSync'; + +let UID = 0; + +// let math = require('../../../math'); +/** + * @class + * @memberof PIXI + * @extends PIXI.Shader + */ +class Program +{ + /** + * @param {string} [vertexSrc] - The source of the vertex shader. + * @param {string} [fragmentSrc] - The source of the fragment shader. + */ + constructor(vertexSrc, fragmentSrc) + { + /** + * The vertex shader. + * + * @member {string} + */ + this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; + + /** + * The fragment shader. + * + * @member {string} + */ + this.fragmentSrc = fragmentSrc || Shader.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 is where we store shader references.. + // TODO we could cache this! + this.glShaders = {}; + + this.syncUniforms = generateUniformsSync(this.uniformData); + this.id = UID++; + } + + /** + * The default vertex shader source + * + * @static + * @constant + */ + static get defaultVertexSrc() + { + return [ + 'attribute vec2 aVertexPosition;', + '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'); + } + + /** + * The default fragment shader source + * + * @static + * @constant + */ + static get defaultFragmentSrc() + { + 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;', + '}', + ].join('\n'); + } +} + +export default Program; diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 30f5b4d..e488400 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,6 +1,8 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; import extractAttributesFromSrc from './extractAttributesFromSrc'; import generateUniformsSync from './generateUniformsSync'; +import Program from './Program'; +import { ProgramCache } from '../utils'; let UID = 0; @@ -17,108 +19,47 @@ * @param {string} [fragmentSrc] - The source of the fragment shader. * @param {object} [uniforms] - Custom uniforms to use to augment the built-in ones. */ - constructor(vertexSrc, fragmentSrc, uniforms) + constructor(program, uniforms) { - /** - * The vertex shader. - * - * @member {string} - */ - this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; - - /** - * The fragment shader. - * - * @member {string} - */ - this.fragmentSrc = fragmentSrc || Shader.defaultFragmentSrc; - - // pull out the vertex and shader uniforms if they are not specified.. - // currently this does not extract structs only default types - this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc); - // TODO - 'projectionMatrix|uSampler|translationMatrix'); - - this.attributeData = extractAttributesFromSrc(this.vertexSrc); - - this.uniforms = {}; + this.program = program; + this.uniforms = 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) + for (const i in program.uniformData) { - if (!this.uniforms[i]) + const uniform = this.uniforms[i]; + + if (!uniform) { - this.uniforms[i] = this.uniformData[i].value; + + this.uniforms[i] = program.uniformData[i].value; + } + else + { + if(uniform instanceof Array) + { + this.uniforms[i] = new Float32Array(uniform) + } } } - - // this is where we store shader references.. - // TODO we could cache this! - this.glShaders = {}; - - this.syncUniforms = generateUniformsSync(this.uniformData); - this.id = UID++; } - /** - * The default vertex shader source - * - * @static - * @constant - */ - static get defaultVertexSrc() + static from(vertexSrc, fragmentSrc, uniforms) { - return [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', + const key = vertexSrc + fragmentSrc; - 'uniform mat3 projectionMatrix;', - 'uniform mat3 filterMatrix;', + let program = ProgramCache[key]; - 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', + if(!program) + { + ProgramCache[key] = program = new Program(vertexSrc,fragmentSrc); + } - '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'); + return new Shader(program, uniforms); } - /** - * The default fragment shader source - * - * @static - * @constant - */ - static get defaultFragmentSrc() - { - 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;', - '}', - ].join('\n'); - } } export default Shader; diff --git a/src/core/shader/generateUniformsSync.js b/src/core/shader/generateUniformsSync.js index b0e6fa8..54a172d 100644 --- a/src/core/shader/generateUniformsSync.js +++ b/src/core/shader/generateUniformsSync.js @@ -17,6 +17,7 @@ cacheValue[0] = value[0]; cacheValue[1] = value[1]; cacheValue[2] = value[2]; + gl.uniform3f(location, value[0], value[1], value[2]) }`, vec4: 'gl.uniform4f(location, value[0], value[1], value[2], value[3])', diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 2a7497b..6ff609f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -360,3 +360,11 @@ * @private */ export const BaseTextureCache = {}; + +/** + * @todo Describe property usage + * + * @memberof PIXI.utils + * @private + */ +export const ProgramCache = {}; diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js index 18043a7..575aad4 100644 --- a/src/extras/cacheAsBitmap.js +++ b/src/extras/cacheAsBitmap.js @@ -41,6 +41,9 @@ * provide a performance benefit for complex static displayObjects. * To remove simply set this property to 'false' * + * IMPORTANT GOTCHA - make sure that all your textures are preloaded BEFORE calling setting this property + * as it will take a snapshot of what is currently there. If the textures have not loaded then they will not appear. + * * @member {boolean} * @memberof PIXI.DisplayObject# */ diff --git a/src/core/index.js b/src/core/index.js index 1b35344..f7a817c 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -43,6 +43,7 @@ export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; export { default as Shader } from './shader/Shader'; +export { default as Program } from './shader/Program'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; export { default as Application } from './Application'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 5ac65b3..d0896b7 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -40,7 +40,8 @@ */ bindShader(shader, dontSync) { - const glShader = shader.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. // if (this.shader !== shader) @@ -64,7 +65,7 @@ */ setUniforms(uniforms) { - const shader = this.shader; + const shader = this.shader.program; const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); @@ -90,16 +91,17 @@ */ generateShader(shader) { + const program = shader.program; const attribMap = {}; - for (const i in shader.attributeData) + for (const i in program.attributeData) { - attribMap[i] = shader.attributeData[i].location; + attribMap[i] = program.attributeData[i].location; } - const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - shader.glShaders[this.renderer.CONTEXT_UID] = glShader; + program.glShaders[this.renderer.CONTEXT_UID] = glShader; return glShader; } diff --git a/src/core/shader/Program.js b/src/core/shader/Program.js new file mode 100644 index 0000000..da7bcf1 --- /dev/null +++ b/src/core/shader/Program.js @@ -0,0 +1,123 @@ +import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; +import generateUniformsSync from './generateUniformsSync'; + +let UID = 0; + +// let math = require('../../../math'); +/** + * @class + * @memberof PIXI + * @extends PIXI.Shader + */ +class Program +{ + /** + * @param {string} [vertexSrc] - The source of the vertex shader. + * @param {string} [fragmentSrc] - The source of the fragment shader. + */ + constructor(vertexSrc, fragmentSrc) + { + /** + * The vertex shader. + * + * @member {string} + */ + this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; + + /** + * The fragment shader. + * + * @member {string} + */ + this.fragmentSrc = fragmentSrc || Shader.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 is where we store shader references.. + // TODO we could cache this! + this.glShaders = {}; + + this.syncUniforms = generateUniformsSync(this.uniformData); + this.id = UID++; + } + + /** + * The default vertex shader source + * + * @static + * @constant + */ + static get defaultVertexSrc() + { + return [ + 'attribute vec2 aVertexPosition;', + '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'); + } + + /** + * The default fragment shader source + * + * @static + * @constant + */ + static get defaultFragmentSrc() + { + 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;', + '}', + ].join('\n'); + } +} + +export default Program; diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 30f5b4d..e488400 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,6 +1,8 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; import extractAttributesFromSrc from './extractAttributesFromSrc'; import generateUniformsSync from './generateUniformsSync'; +import Program from './Program'; +import { ProgramCache } from '../utils'; let UID = 0; @@ -17,108 +19,47 @@ * @param {string} [fragmentSrc] - The source of the fragment shader. * @param {object} [uniforms] - Custom uniforms to use to augment the built-in ones. */ - constructor(vertexSrc, fragmentSrc, uniforms) + constructor(program, uniforms) { - /** - * The vertex shader. - * - * @member {string} - */ - this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; - - /** - * The fragment shader. - * - * @member {string} - */ - this.fragmentSrc = fragmentSrc || Shader.defaultFragmentSrc; - - // pull out the vertex and shader uniforms if they are not specified.. - // currently this does not extract structs only default types - this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc); - // TODO - 'projectionMatrix|uSampler|translationMatrix'); - - this.attributeData = extractAttributesFromSrc(this.vertexSrc); - - this.uniforms = {}; + this.program = program; + this.uniforms = 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) + for (const i in program.uniformData) { - if (!this.uniforms[i]) + const uniform = this.uniforms[i]; + + if (!uniform) { - this.uniforms[i] = this.uniformData[i].value; + + this.uniforms[i] = program.uniformData[i].value; + } + else + { + if(uniform instanceof Array) + { + this.uniforms[i] = new Float32Array(uniform) + } } } - - // this is where we store shader references.. - // TODO we could cache this! - this.glShaders = {}; - - this.syncUniforms = generateUniformsSync(this.uniformData); - this.id = UID++; } - /** - * The default vertex shader source - * - * @static - * @constant - */ - static get defaultVertexSrc() + static from(vertexSrc, fragmentSrc, uniforms) { - return [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', + const key = vertexSrc + fragmentSrc; - 'uniform mat3 projectionMatrix;', - 'uniform mat3 filterMatrix;', + let program = ProgramCache[key]; - 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', + if(!program) + { + ProgramCache[key] = program = new Program(vertexSrc,fragmentSrc); + } - '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'); + return new Shader(program, uniforms); } - /** - * The default fragment shader source - * - * @static - * @constant - */ - static get defaultFragmentSrc() - { - 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;', - '}', - ].join('\n'); - } } export default Shader; diff --git a/src/core/shader/generateUniformsSync.js b/src/core/shader/generateUniformsSync.js index b0e6fa8..54a172d 100644 --- a/src/core/shader/generateUniformsSync.js +++ b/src/core/shader/generateUniformsSync.js @@ -17,6 +17,7 @@ cacheValue[0] = value[0]; cacheValue[1] = value[1]; cacheValue[2] = value[2]; + gl.uniform3f(location, value[0], value[1], value[2]) }`, vec4: 'gl.uniform4f(location, value[0], value[1], value[2], value[3])', diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 2a7497b..6ff609f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -360,3 +360,11 @@ * @private */ export const BaseTextureCache = {}; + +/** + * @todo Describe property usage + * + * @memberof PIXI.utils + * @private + */ +export const ProgramCache = {}; diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js index 18043a7..575aad4 100644 --- a/src/extras/cacheAsBitmap.js +++ b/src/extras/cacheAsBitmap.js @@ -41,6 +41,9 @@ * provide a performance benefit for complex static displayObjects. * To remove simply set this property to 'false' * + * IMPORTANT GOTCHA - make sure that all your textures are preloaded BEFORE calling setting this property + * as it will take a snapshot of what is currently there. If the textures have not loaded then they will not appear. + * * @member {boolean} * @memberof PIXI.DisplayObject# */ diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 872ab35..49f7f31 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -4,7 +4,7 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -let meshShader; +let meshProgram; /** * Base mesh class @@ -25,10 +25,10 @@ { const geometry = new Geometry(); - if (!meshShader) + if (!meshProgram) { - meshShader = new core.Shader(readFileSync(join(__dirname, './webgl/mesh.vert'), 'utf8'), - readFileSync(join(__dirname, './webgl/mesh.frag'), 'utf8')); + meshProgram = new core.Program(readFileSync(join(__dirname, './webgl/mesh.vert'), 'utf8'), + readFileSync(join(__dirname, './webgl/mesh.frag'), 'utf8')); } geometry.addAttribute('aVertexPosition', vertices) @@ -44,8 +44,9 @@ }; - super(geometry, meshShader, uniforms, null, drawMode); + super(geometry, new core.Shader(meshProgram,uniforms), null, drawMode); + this.uniforms = uniforms; this.texture = texture; this._tint = 0xFFFFFF; diff --git a/src/core/index.js b/src/core/index.js index 1b35344..f7a817c 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -43,6 +43,7 @@ export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; export { default as Shader } from './shader/Shader'; +export { default as Program } from './shader/Program'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; export { default as Application } from './Application'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 5ac65b3..d0896b7 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -40,7 +40,8 @@ */ bindShader(shader, dontSync) { - const glShader = shader.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. // if (this.shader !== shader) @@ -64,7 +65,7 @@ */ setUniforms(uniforms) { - const shader = this.shader; + const shader = this.shader.program; const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); @@ -90,16 +91,17 @@ */ generateShader(shader) { + const program = shader.program; const attribMap = {}; - for (const i in shader.attributeData) + for (const i in program.attributeData) { - attribMap[i] = shader.attributeData[i].location; + attribMap[i] = program.attributeData[i].location; } - const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - shader.glShaders[this.renderer.CONTEXT_UID] = glShader; + program.glShaders[this.renderer.CONTEXT_UID] = glShader; return glShader; } diff --git a/src/core/shader/Program.js b/src/core/shader/Program.js new file mode 100644 index 0000000..da7bcf1 --- /dev/null +++ b/src/core/shader/Program.js @@ -0,0 +1,123 @@ +import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; +import generateUniformsSync from './generateUniformsSync'; + +let UID = 0; + +// let math = require('../../../math'); +/** + * @class + * @memberof PIXI + * @extends PIXI.Shader + */ +class Program +{ + /** + * @param {string} [vertexSrc] - The source of the vertex shader. + * @param {string} [fragmentSrc] - The source of the fragment shader. + */ + constructor(vertexSrc, fragmentSrc) + { + /** + * The vertex shader. + * + * @member {string} + */ + this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; + + /** + * The fragment shader. + * + * @member {string} + */ + this.fragmentSrc = fragmentSrc || Shader.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 is where we store shader references.. + // TODO we could cache this! + this.glShaders = {}; + + this.syncUniforms = generateUniformsSync(this.uniformData); + this.id = UID++; + } + + /** + * The default vertex shader source + * + * @static + * @constant + */ + static get defaultVertexSrc() + { + return [ + 'attribute vec2 aVertexPosition;', + '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'); + } + + /** + * The default fragment shader source + * + * @static + * @constant + */ + static get defaultFragmentSrc() + { + 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;', + '}', + ].join('\n'); + } +} + +export default Program; diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 30f5b4d..e488400 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,6 +1,8 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; import extractAttributesFromSrc from './extractAttributesFromSrc'; import generateUniformsSync from './generateUniformsSync'; +import Program from './Program'; +import { ProgramCache } from '../utils'; let UID = 0; @@ -17,108 +19,47 @@ * @param {string} [fragmentSrc] - The source of the fragment shader. * @param {object} [uniforms] - Custom uniforms to use to augment the built-in ones. */ - constructor(vertexSrc, fragmentSrc, uniforms) + constructor(program, uniforms) { - /** - * The vertex shader. - * - * @member {string} - */ - this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; - - /** - * The fragment shader. - * - * @member {string} - */ - this.fragmentSrc = fragmentSrc || Shader.defaultFragmentSrc; - - // pull out the vertex and shader uniforms if they are not specified.. - // currently this does not extract structs only default types - this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc); - // TODO - 'projectionMatrix|uSampler|translationMatrix'); - - this.attributeData = extractAttributesFromSrc(this.vertexSrc); - - this.uniforms = {}; + this.program = program; + this.uniforms = 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) + for (const i in program.uniformData) { - if (!this.uniforms[i]) + const uniform = this.uniforms[i]; + + if (!uniform) { - this.uniforms[i] = this.uniformData[i].value; + + this.uniforms[i] = program.uniformData[i].value; + } + else + { + if(uniform instanceof Array) + { + this.uniforms[i] = new Float32Array(uniform) + } } } - - // this is where we store shader references.. - // TODO we could cache this! - this.glShaders = {}; - - this.syncUniforms = generateUniformsSync(this.uniformData); - this.id = UID++; } - /** - * The default vertex shader source - * - * @static - * @constant - */ - static get defaultVertexSrc() + static from(vertexSrc, fragmentSrc, uniforms) { - return [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', + const key = vertexSrc + fragmentSrc; - 'uniform mat3 projectionMatrix;', - 'uniform mat3 filterMatrix;', + let program = ProgramCache[key]; - 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', + if(!program) + { + ProgramCache[key] = program = new Program(vertexSrc,fragmentSrc); + } - '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'); + return new Shader(program, uniforms); } - /** - * The default fragment shader source - * - * @static - * @constant - */ - static get defaultFragmentSrc() - { - 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;', - '}', - ].join('\n'); - } } export default Shader; diff --git a/src/core/shader/generateUniformsSync.js b/src/core/shader/generateUniformsSync.js index b0e6fa8..54a172d 100644 --- a/src/core/shader/generateUniformsSync.js +++ b/src/core/shader/generateUniformsSync.js @@ -17,6 +17,7 @@ cacheValue[0] = value[0]; cacheValue[1] = value[1]; cacheValue[2] = value[2]; + gl.uniform3f(location, value[0], value[1], value[2]) }`, vec4: 'gl.uniform4f(location, value[0], value[1], value[2], value[3])', diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 2a7497b..6ff609f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -360,3 +360,11 @@ * @private */ export const BaseTextureCache = {}; + +/** + * @todo Describe property usage + * + * @memberof PIXI.utils + * @private + */ +export const ProgramCache = {}; diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js index 18043a7..575aad4 100644 --- a/src/extras/cacheAsBitmap.js +++ b/src/extras/cacheAsBitmap.js @@ -41,6 +41,9 @@ * provide a performance benefit for complex static displayObjects. * To remove simply set this property to 'false' * + * IMPORTANT GOTCHA - make sure that all your textures are preloaded BEFORE calling setting this property + * as it will take a snapshot of what is currently there. If the textures have not loaded then they will not appear. + * * @member {boolean} * @memberof PIXI.DisplayObject# */ diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 872ab35..49f7f31 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -4,7 +4,7 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -let meshShader; +let meshProgram; /** * Base mesh class @@ -25,10 +25,10 @@ { const geometry = new Geometry(); - if (!meshShader) + if (!meshProgram) { - meshShader = new core.Shader(readFileSync(join(__dirname, './webgl/mesh.vert'), 'utf8'), - readFileSync(join(__dirname, './webgl/mesh.frag'), 'utf8')); + meshProgram = new core.Program(readFileSync(join(__dirname, './webgl/mesh.vert'), 'utf8'), + readFileSync(join(__dirname, './webgl/mesh.frag'), 'utf8')); } geometry.addAttribute('aVertexPosition', vertices) @@ -44,8 +44,9 @@ }; - super(geometry, meshShader, uniforms, null, drawMode); + super(geometry, new core.Shader(meshProgram,uniforms), null, drawMode); + this.uniforms = uniforms; this.texture = texture; this._tint = 0xFFFFFF; diff --git a/src/mesh/RawMesh.js b/src/mesh/RawMesh.js index cda86c5..17a969d 100644 --- a/src/mesh/RawMesh.js +++ b/src/mesh/RawMesh.js @@ -30,7 +30,7 @@ * @param {PIXI.State} state the state that the webGL context is required to be in to render the mesh * @param {number} drawMode the drawMode, can be any of the PIXI.DRAW_MODES consts */ - constructor(geometry, shader, uniforms, state, drawMode = core.DRAW_MODES.TRIANGLES) + constructor(geometry, shader, state, drawMode = core.DRAW_MODES.TRIANGLES) { super(); @@ -60,23 +60,10 @@ */ this.drawMode = drawMode; - uniforms = uniforms || {}; - - // make sure to add required feilds - // if the user misses any uniforms we can add the default valujes from the shader - for (const i in shader.uniforms) - { - if (uniforms[i] === undefined) - { - uniforms[i] = shader.uniforms[i]; - } - } - /** * The way uniforms that will be used by the mesh's shader. * @member {Object} */ - this.uniforms = uniforms; /** * A map of renderer IDs to webgl render data diff --git a/src/core/index.js b/src/core/index.js index 1b35344..f7a817c 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -43,6 +43,7 @@ export { default as RenderTarget } from './renderers/webgl/utils/RenderTarget'; export { default as Quad } from './renderers/webgl/utils/Quad'; export { default as Shader } from './shader/Shader'; +export { default as Program } from './shader/Program'; export { default as SpriteMaskFilter } from './renderers/webgl/filters/spriteMask/SpriteMaskFilter'; export { default as Filter } from './renderers/webgl/filters/Filter'; export { default as Application } from './Application'; diff --git a/src/core/renderers/webgl/ShaderManager.js b/src/core/renderers/webgl/ShaderManager.js index 5ac65b3..d0896b7 100644 --- a/src/core/renderers/webgl/ShaderManager.js +++ b/src/core/renderers/webgl/ShaderManager.js @@ -40,7 +40,8 @@ */ bindShader(shader, dontSync) { - const glShader = shader.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); + const program = shader.program; + const glShader = program.glShaders[this.renderer.CONTEXT_UID] || this.generateShader(shader); // TODO - some current pixi plugins bypass this.. so it not safe to use yet.. // if (this.shader !== shader) @@ -64,7 +65,7 @@ */ setUniforms(uniforms) { - const shader = this.shader; + const shader = this.shader.program; const glShader = shader.glShaders[this.renderer.CONTEXT_UID]; shader.syncUniforms(glShader.uniformData, uniforms, this.renderer); @@ -90,16 +91,17 @@ */ generateShader(shader) { + const program = shader.program; const attribMap = {}; - for (const i in shader.attributeData) + for (const i in program.attributeData) { - attribMap[i] = shader.attributeData[i].location; + attribMap[i] = program.attributeData[i].location; } - const glShader = new GLShader(this.gl, shader.vertexSrc, shader.fragmentSrc, PRECISION.DEFAULT, attribMap); + const glShader = new GLShader(this.gl, program.vertexSrc, program.fragmentSrc, PRECISION.DEFAULT, attribMap); - shader.glShaders[this.renderer.CONTEXT_UID] = glShader; + program.glShaders[this.renderer.CONTEXT_UID] = glShader; return glShader; } diff --git a/src/core/shader/Program.js b/src/core/shader/Program.js new file mode 100644 index 0000000..da7bcf1 --- /dev/null +++ b/src/core/shader/Program.js @@ -0,0 +1,123 @@ +import extractUniformsFromSrc from './extractUniformsFromSrc'; +import extractAttributesFromSrc from './extractAttributesFromSrc'; +import generateUniformsSync from './generateUniformsSync'; + +let UID = 0; + +// let math = require('../../../math'); +/** + * @class + * @memberof PIXI + * @extends PIXI.Shader + */ +class Program +{ + /** + * @param {string} [vertexSrc] - The source of the vertex shader. + * @param {string} [fragmentSrc] - The source of the fragment shader. + */ + constructor(vertexSrc, fragmentSrc) + { + /** + * The vertex shader. + * + * @member {string} + */ + this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; + + /** + * The fragment shader. + * + * @member {string} + */ + this.fragmentSrc = fragmentSrc || Shader.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 is where we store shader references.. + // TODO we could cache this! + this.glShaders = {}; + + this.syncUniforms = generateUniformsSync(this.uniformData); + this.id = UID++; + } + + /** + * The default vertex shader source + * + * @static + * @constant + */ + static get defaultVertexSrc() + { + return [ + 'attribute vec2 aVertexPosition;', + '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'); + } + + /** + * The default fragment shader source + * + * @static + * @constant + */ + static get defaultFragmentSrc() + { + 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;', + '}', + ].join('\n'); + } +} + +export default Program; diff --git a/src/core/shader/Shader.js b/src/core/shader/Shader.js index 30f5b4d..e488400 100644 --- a/src/core/shader/Shader.js +++ b/src/core/shader/Shader.js @@ -1,6 +1,8 @@ import extractUniformsFromSrc from './extractUniformsFromSrc'; import extractAttributesFromSrc from './extractAttributesFromSrc'; import generateUniformsSync from './generateUniformsSync'; +import Program from './Program'; +import { ProgramCache } from '../utils'; let UID = 0; @@ -17,108 +19,47 @@ * @param {string} [fragmentSrc] - The source of the fragment shader. * @param {object} [uniforms] - Custom uniforms to use to augment the built-in ones. */ - constructor(vertexSrc, fragmentSrc, uniforms) + constructor(program, uniforms) { - /** - * The vertex shader. - * - * @member {string} - */ - this.vertexSrc = vertexSrc || Shader.defaultVertexSrc; - - /** - * The fragment shader. - * - * @member {string} - */ - this.fragmentSrc = fragmentSrc || Shader.defaultFragmentSrc; - - // pull out the vertex and shader uniforms if they are not specified.. - // currently this does not extract structs only default types - this.uniformData = uniforms || extractUniformsFromSrc(this.vertexSrc, this.fragmentSrc); - // TODO - 'projectionMatrix|uSampler|translationMatrix'); - - this.attributeData = extractAttributesFromSrc(this.vertexSrc); - - this.uniforms = {}; + this.program = program; + this.uniforms = 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) + for (const i in program.uniformData) { - if (!this.uniforms[i]) + const uniform = this.uniforms[i]; + + if (!uniform) { - this.uniforms[i] = this.uniformData[i].value; + + this.uniforms[i] = program.uniformData[i].value; + } + else + { + if(uniform instanceof Array) + { + this.uniforms[i] = new Float32Array(uniform) + } } } - - // this is where we store shader references.. - // TODO we could cache this! - this.glShaders = {}; - - this.syncUniforms = generateUniformsSync(this.uniformData); - this.id = UID++; } - /** - * The default vertex shader source - * - * @static - * @constant - */ - static get defaultVertexSrc() + static from(vertexSrc, fragmentSrc, uniforms) { - return [ - 'attribute vec2 aVertexPosition;', - 'attribute vec2 aTextureCoord;', + const key = vertexSrc + fragmentSrc; - 'uniform mat3 projectionMatrix;', - 'uniform mat3 filterMatrix;', + let program = ProgramCache[key]; - 'varying vec2 vTextureCoord;', - 'varying vec2 vFilterCoord;', + if(!program) + { + ProgramCache[key] = program = new Program(vertexSrc,fragmentSrc); + } - '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'); + return new Shader(program, uniforms); } - /** - * The default fragment shader source - * - * @static - * @constant - */ - static get defaultFragmentSrc() - { - 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;', - '}', - ].join('\n'); - } } export default Shader; diff --git a/src/core/shader/generateUniformsSync.js b/src/core/shader/generateUniformsSync.js index b0e6fa8..54a172d 100644 --- a/src/core/shader/generateUniformsSync.js +++ b/src/core/shader/generateUniformsSync.js @@ -17,6 +17,7 @@ cacheValue[0] = value[0]; cacheValue[1] = value[1]; cacheValue[2] = value[2]; + gl.uniform3f(location, value[0], value[1], value[2]) }`, vec4: 'gl.uniform4f(location, value[0], value[1], value[2], value[3])', diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 2a7497b..6ff609f 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -360,3 +360,11 @@ * @private */ export const BaseTextureCache = {}; + +/** + * @todo Describe property usage + * + * @memberof PIXI.utils + * @private + */ +export const ProgramCache = {}; diff --git a/src/extras/cacheAsBitmap.js b/src/extras/cacheAsBitmap.js index 18043a7..575aad4 100644 --- a/src/extras/cacheAsBitmap.js +++ b/src/extras/cacheAsBitmap.js @@ -41,6 +41,9 @@ * provide a performance benefit for complex static displayObjects. * To remove simply set this property to 'false' * + * IMPORTANT GOTCHA - make sure that all your textures are preloaded BEFORE calling setting this property + * as it will take a snapshot of what is currently there. If the textures have not loaded then they will not appear. + * * @member {boolean} * @memberof PIXI.DisplayObject# */ diff --git a/src/mesh/Mesh.js b/src/mesh/Mesh.js index 872ab35..49f7f31 100644 --- a/src/mesh/Mesh.js +++ b/src/mesh/Mesh.js @@ -4,7 +4,7 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -let meshShader; +let meshProgram; /** * Base mesh class @@ -25,10 +25,10 @@ { const geometry = new Geometry(); - if (!meshShader) + if (!meshProgram) { - meshShader = new core.Shader(readFileSync(join(__dirname, './webgl/mesh.vert'), 'utf8'), - readFileSync(join(__dirname, './webgl/mesh.frag'), 'utf8')); + meshProgram = new core.Program(readFileSync(join(__dirname, './webgl/mesh.vert'), 'utf8'), + readFileSync(join(__dirname, './webgl/mesh.frag'), 'utf8')); } geometry.addAttribute('aVertexPosition', vertices) @@ -44,8 +44,9 @@ }; - super(geometry, meshShader, uniforms, null, drawMode); + super(geometry, new core.Shader(meshProgram,uniforms), null, drawMode); + this.uniforms = uniforms; this.texture = texture; this._tint = 0xFFFFFF; diff --git a/src/mesh/RawMesh.js b/src/mesh/RawMesh.js index cda86c5..17a969d 100644 --- a/src/mesh/RawMesh.js +++ b/src/mesh/RawMesh.js @@ -30,7 +30,7 @@ * @param {PIXI.State} state the state that the webGL context is required to be in to render the mesh * @param {number} drawMode the drawMode, can be any of the PIXI.DRAW_MODES consts */ - constructor(geometry, shader, uniforms, state, drawMode = core.DRAW_MODES.TRIANGLES) + constructor(geometry, shader, state, drawMode = core.DRAW_MODES.TRIANGLES) { super(); @@ -60,23 +60,10 @@ */ this.drawMode = drawMode; - uniforms = uniforms || {}; - - // make sure to add required feilds - // if the user misses any uniforms we can add the default valujes from the shader - for (const i in shader.uniforms) - { - if (uniforms[i] === undefined) - { - uniforms[i] = shader.uniforms[i]; - } - } - /** * The way uniforms that will be used by the mesh's shader. * @member {Object} */ - this.uniforms = uniforms; /** * A map of renderer IDs to webgl render data diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index 34a1423..a3a5d35 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -54,7 +54,7 @@ } // set unifomrs.. - this.renderer.shaderManager.setUniforms(mesh.uniforms); + this.renderer.shaderManager.setUniforms(mesh.shader.uniforms); // sync uniforms.. this.renderer.state.setState(mesh.state); @@ -66,6 +66,11 @@ mesh.geometry.glVertexArrayObjects[this.CONTEXT_UID].draw(mesh.drawMode, mesh.size, mesh.start); } + draw(mesh) + { + mesh.geometry.glVertexArrayObjects[this.CONTEXT_UID].draw(mesh.drawMode, mesh.size, mesh.start); + } + /** * Binds geometry so that is can be drawn. Creating a Vao if required * @private