diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 4eaef9d..6f01156 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -18,6 +18,8 @@ * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ function CanvasRenderer(width, height, options) { + utils.sayHello('Canvas'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -29,11 +31,6 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('Canvas'); - defaultRenderer = this; - } - /** * The renderer type. * @@ -183,7 +180,7 @@ this.context.globalAlpha = 1; this.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; + this.context.globalCompositeOperation = this.blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 4eaef9d..6f01156 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -18,6 +18,8 @@ * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ function CanvasRenderer(width, height, options) { + utils.sayHello('Canvas'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -29,11 +31,6 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('Canvas'); - defaultRenderer = this; - } - /** * The renderer type. * @@ -183,7 +180,7 @@ this.context.globalAlpha = 1; this.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; + this.context.globalCompositeOperation = this.blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9913f0a..bfa6f4a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -8,9 +8,6 @@ utils = require('../../utils'), CONST = require('../../const'); -glContexts = []; // this is where we store the webGL contexts for easy access. -instances = []; - /** * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. @@ -30,6 +27,8 @@ * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ function WebGLRenderer(width, height, options) { + utils.sayHello('webGL'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -41,10 +40,7 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('webGL'); - defaultRenderer = this; - } + this.uuid = utils.uuid(); /** * @member {number} @@ -218,6 +214,8 @@ this.blendModes = null; + utils.webglRenderers.push(this); + // time init the context.. this._initContext(); @@ -229,7 +227,7 @@ WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; -utils.EventTarget.mixin(WebGLRenderer.prototype); +utils.eventTarget.mixin(WebGLRenderer.prototype); Object.defineProperties(WebGLRenderer.prototype, { backgroundColor: { @@ -256,11 +254,9 @@ throw new Error('This browser does not support webGL. Try using the canvas renderer'); } - this.glContextId = gl.id = WebGLRenderer.glContextId ++; - - glContexts[this.glContextId] = gl; - - instances[this.glContextId] = this; + this.glContextId = WebGLRenderer.glContextId++; + gl.id = this.glContextId; + gl.renderer = this; // set up the default pixi settings.. gl.disable(gl.DEPTH_TEST); @@ -371,9 +367,11 @@ /** * Updates and Creates a WebGL texture for the renderers context. * - * @param texture {Texture} the texture to update + * @param texture {BaseTexture|Texture} the texture to update */ WebGLRenderer.prototype.updateTexture = function (texture) { + texture = texture.baseTexture || texture; + if (!texture.hasLoaded) { return; } @@ -382,6 +380,7 @@ if (!texture._glTextures[gl.id]) { texture._glTextures[gl.id] = gl.createTexture(); + texture.on('update', this._boundUpdateTexture); } gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); @@ -410,11 +409,21 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - texture._dirty[gl.id] = false; - return texture._glTextures[gl.id]; }; +WebGLRenderer.prototype.destroyTexture = function (texture) { + texture = texture.baseTexture || texture; + + if (!texture.hasLoaded) { + return; + } + + if (texture._glTextures[this.gl.id]) { + this.gl.deleteTexture(texture._glTextures[this.gl.id]); + } +}; + /** * Handles a lost webgl context * @@ -453,8 +462,6 @@ this.view.removeEventListener('webglcontextlost', this.contextLostBound); this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); - glContexts[this.glContextId] = null; - this.projection = null; this.offset = null; diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 4eaef9d..6f01156 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -18,6 +18,8 @@ * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ function CanvasRenderer(width, height, options) { + utils.sayHello('Canvas'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -29,11 +31,6 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('Canvas'); - defaultRenderer = this; - } - /** * The renderer type. * @@ -183,7 +180,7 @@ this.context.globalAlpha = 1; this.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; + this.context.globalCompositeOperation = this.blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9913f0a..bfa6f4a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -8,9 +8,6 @@ utils = require('../../utils'), CONST = require('../../const'); -glContexts = []; // this is where we store the webGL contexts for easy access. -instances = []; - /** * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. @@ -30,6 +27,8 @@ * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ function WebGLRenderer(width, height, options) { + utils.sayHello('webGL'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -41,10 +40,7 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('webGL'); - defaultRenderer = this; - } + this.uuid = utils.uuid(); /** * @member {number} @@ -218,6 +214,8 @@ this.blendModes = null; + utils.webglRenderers.push(this); + // time init the context.. this._initContext(); @@ -229,7 +227,7 @@ WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; -utils.EventTarget.mixin(WebGLRenderer.prototype); +utils.eventTarget.mixin(WebGLRenderer.prototype); Object.defineProperties(WebGLRenderer.prototype, { backgroundColor: { @@ -256,11 +254,9 @@ throw new Error('This browser does not support webGL. Try using the canvas renderer'); } - this.glContextId = gl.id = WebGLRenderer.glContextId ++; - - glContexts[this.glContextId] = gl; - - instances[this.glContextId] = this; + this.glContextId = WebGLRenderer.glContextId++; + gl.id = this.glContextId; + gl.renderer = this; // set up the default pixi settings.. gl.disable(gl.DEPTH_TEST); @@ -371,9 +367,11 @@ /** * Updates and Creates a WebGL texture for the renderers context. * - * @param texture {Texture} the texture to update + * @param texture {BaseTexture|Texture} the texture to update */ WebGLRenderer.prototype.updateTexture = function (texture) { + texture = texture.baseTexture || texture; + if (!texture.hasLoaded) { return; } @@ -382,6 +380,7 @@ if (!texture._glTextures[gl.id]) { texture._glTextures[gl.id] = gl.createTexture(); + texture.on('update', this._boundUpdateTexture); } gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); @@ -410,11 +409,21 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - texture._dirty[gl.id] = false; - return texture._glTextures[gl.id]; }; +WebGLRenderer.prototype.destroyTexture = function (texture) { + texture = texture.baseTexture || texture; + + if (!texture.hasLoaded) { + return; + } + + if (texture._glTextures[this.gl.id]) { + this.gl.deleteTexture(texture._glTextures[this.gl.id]); + } +}; + /** * Handles a lost webgl context * @@ -453,8 +462,6 @@ this.view.removeEventListener('webglcontextlost', this.contextLostBound); this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); - glContexts[this.glContextId] = null; - this.projection = null; this.offset = null; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 60e97be..13ac4cc 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -355,11 +355,6 @@ uniform._init = true; } - // if it has been initialized, check if dirty and needs update - else if (uniform.value.baseTexture._dirty[gl.id] !== false) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - break; default: diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 4eaef9d..6f01156 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -18,6 +18,8 @@ * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ function CanvasRenderer(width, height, options) { + utils.sayHello('Canvas'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -29,11 +31,6 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('Canvas'); - defaultRenderer = this; - } - /** * The renderer type. * @@ -183,7 +180,7 @@ this.context.globalAlpha = 1; this.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; + this.context.globalCompositeOperation = this.blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9913f0a..bfa6f4a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -8,9 +8,6 @@ utils = require('../../utils'), CONST = require('../../const'); -glContexts = []; // this is where we store the webGL contexts for easy access. -instances = []; - /** * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. @@ -30,6 +27,8 @@ * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ function WebGLRenderer(width, height, options) { + utils.sayHello('webGL'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -41,10 +40,7 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('webGL'); - defaultRenderer = this; - } + this.uuid = utils.uuid(); /** * @member {number} @@ -218,6 +214,8 @@ this.blendModes = null; + utils.webglRenderers.push(this); + // time init the context.. this._initContext(); @@ -229,7 +227,7 @@ WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; -utils.EventTarget.mixin(WebGLRenderer.prototype); +utils.eventTarget.mixin(WebGLRenderer.prototype); Object.defineProperties(WebGLRenderer.prototype, { backgroundColor: { @@ -256,11 +254,9 @@ throw new Error('This browser does not support webGL. Try using the canvas renderer'); } - this.glContextId = gl.id = WebGLRenderer.glContextId ++; - - glContexts[this.glContextId] = gl; - - instances[this.glContextId] = this; + this.glContextId = WebGLRenderer.glContextId++; + gl.id = this.glContextId; + gl.renderer = this; // set up the default pixi settings.. gl.disable(gl.DEPTH_TEST); @@ -371,9 +367,11 @@ /** * Updates and Creates a WebGL texture for the renderers context. * - * @param texture {Texture} the texture to update + * @param texture {BaseTexture|Texture} the texture to update */ WebGLRenderer.prototype.updateTexture = function (texture) { + texture = texture.baseTexture || texture; + if (!texture.hasLoaded) { return; } @@ -382,6 +380,7 @@ if (!texture._glTextures[gl.id]) { texture._glTextures[gl.id] = gl.createTexture(); + texture.on('update', this._boundUpdateTexture); } gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); @@ -410,11 +409,21 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - texture._dirty[gl.id] = false; - return texture._glTextures[gl.id]; }; +WebGLRenderer.prototype.destroyTexture = function (texture) { + texture = texture.baseTexture || texture; + + if (!texture.hasLoaded) { + return; + } + + if (texture._glTextures[this.gl.id]) { + this.gl.deleteTexture(texture._glTextures[this.gl.id]); + } +}; + /** * Handles a lost webgl context * @@ -453,8 +462,6 @@ this.view.removeEventListener('webglcontextlost', this.contextLostBound); this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); - glContexts[this.glContextId] = null; - this.projection = null; this.offset = null; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 60e97be..13ac4cc 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -355,11 +355,6 @@ uniform._init = true; } - // if it has been initialized, check if dirty and needs update - else if (uniform.value.baseTexture._dirty[gl.id] !== false) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - break; default: diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index e0984c4..2a54317 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -551,14 +551,8 @@ var gl = this.gl; - // check if a texture is dirty.. - if (texture._dirty[gl.id]) { - this.renderSession.renderer.updateTexture(texture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - } + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); // now draw those suckas! gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 4eaef9d..6f01156 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -18,6 +18,8 @@ * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ function CanvasRenderer(width, height, options) { + utils.sayHello('Canvas'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -29,11 +31,6 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('Canvas'); - defaultRenderer = this; - } - /** * The renderer type. * @@ -183,7 +180,7 @@ this.context.globalAlpha = 1; this.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; + this.context.globalCompositeOperation = this.blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9913f0a..bfa6f4a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -8,9 +8,6 @@ utils = require('../../utils'), CONST = require('../../const'); -glContexts = []; // this is where we store the webGL contexts for easy access. -instances = []; - /** * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. @@ -30,6 +27,8 @@ * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ function WebGLRenderer(width, height, options) { + utils.sayHello('webGL'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -41,10 +40,7 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('webGL'); - defaultRenderer = this; - } + this.uuid = utils.uuid(); /** * @member {number} @@ -218,6 +214,8 @@ this.blendModes = null; + utils.webglRenderers.push(this); + // time init the context.. this._initContext(); @@ -229,7 +227,7 @@ WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; -utils.EventTarget.mixin(WebGLRenderer.prototype); +utils.eventTarget.mixin(WebGLRenderer.prototype); Object.defineProperties(WebGLRenderer.prototype, { backgroundColor: { @@ -256,11 +254,9 @@ throw new Error('This browser does not support webGL. Try using the canvas renderer'); } - this.glContextId = gl.id = WebGLRenderer.glContextId ++; - - glContexts[this.glContextId] = gl; - - instances[this.glContextId] = this; + this.glContextId = WebGLRenderer.glContextId++; + gl.id = this.glContextId; + gl.renderer = this; // set up the default pixi settings.. gl.disable(gl.DEPTH_TEST); @@ -371,9 +367,11 @@ /** * Updates and Creates a WebGL texture for the renderers context. * - * @param texture {Texture} the texture to update + * @param texture {BaseTexture|Texture} the texture to update */ WebGLRenderer.prototype.updateTexture = function (texture) { + texture = texture.baseTexture || texture; + if (!texture.hasLoaded) { return; } @@ -382,6 +380,7 @@ if (!texture._glTextures[gl.id]) { texture._glTextures[gl.id] = gl.createTexture(); + texture.on('update', this._boundUpdateTexture); } gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); @@ -410,11 +409,21 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - texture._dirty[gl.id] = false; - return texture._glTextures[gl.id]; }; +WebGLRenderer.prototype.destroyTexture = function (texture) { + texture = texture.baseTexture || texture; + + if (!texture.hasLoaded) { + return; + } + + if (texture._glTextures[this.gl.id]) { + this.gl.deleteTexture(texture._glTextures[this.gl.id]); + } +}; + /** * Handles a lost webgl context * @@ -453,8 +462,6 @@ this.view.removeEventListener('webglcontextlost', this.contextLostBound); this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); - glContexts[this.glContextId] = null; - this.projection = null; this.offset = null; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 60e97be..13ac4cc 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -355,11 +355,6 @@ uniform._init = true; } - // if it has been initialized, check if dirty and needs update - else if (uniform.value.baseTexture._dirty[gl.id] !== false) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - break; default: diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index e0984c4..2a54317 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -551,14 +551,8 @@ var gl = this.gl; - // check if a texture is dirty.. - if (texture._dirty[gl.id]) { - this.renderSession.renderer.updateTexture(texture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - } + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); // now draw those suckas! gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 121a583..34605a2 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -5,7 +5,7 @@ * A texture stores the information that represents an image. All textures have a base texture. * * @class - * @mixes EventTarget + * @mixes eventTarget * @namespace PIXI * @param source {string} the source object (image or canvas) * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values @@ -71,28 +71,23 @@ // used for webGL /** - * @member {Array} - * @private - */ - this._glTextures = []; - - /** * * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used * Also the texture must be a power of two size to work * - * @member {{boolean}} + * @member {boolean} */ this.mipmap = false; - // used for webGL texture updating... - // TODO - this needs to be addressed - /** - * @member {object} + * A map of renderer IDs to webgl textures + * + * @member {object} * @private */ - this._dirty = {}; + this._glTextures = {}; + + this._needsUpdate = false; if (!source) { return; @@ -102,7 +97,7 @@ this.hasLoaded = true; this.width = this.source.naturalWidth || this.source.width; this.height = this.source.naturalHeight || this.source.height; - this.dirty(); + this.needsUpdate = true; } else { var scope = this; @@ -112,15 +107,13 @@ scope.hasLoaded = true; scope.width = scope.source.naturalWidth || scope.source.width; scope.height = scope.source.naturalHeight || scope.source.height; + this.needsUpdate = true; - scope.dirty(); - - // add it to somewhere... - scope.dispatchEvent( { type: 'loaded', content: scope } ); + scope.emit('loaded', scope); }; this.source.onerror = function () { - scope.dispatchEvent( { type: 'error', content: scope } ); + scope.emit('error', scope); }; } @@ -139,7 +132,22 @@ BaseTexture.prototype.constructor = BaseTexture; module.exports = BaseTexture; -utils.EventTarget.mixin(BaseTexture.prototype); +utils.eventTarget.mixin(BaseTexture.prototype); + +Object.defineProperties(BaseTexture.prototype, { + needsUpdate: { + get: function () { + return this._needsUpdate; + }, + set: function (val) { + this._needsUpdate = val; + + if (val) { + this.emit('update', this); + } + } + } +}); /** * Destroys this base texture @@ -159,7 +167,10 @@ } this.source = null; - this.unloadFromGPU(); + // delete the webGL textures if any. + for (var i = 0; i < utils.webglRenderers.length; ++i) { + utils.webglRenderers[i].destroyTexture(this); + } }; /** @@ -169,45 +180,11 @@ */ BaseTexture.prototype.updateSourceImage = function (newSrc) { this.hasLoaded = false; - this.source.src = null; + this.source.src = newSrc; }; /** - * Sets all glTextures to be dirty. - * - */ -BaseTexture.prototype.dirty = function () { - for (var i = 0; i < this._glTextures.length; i++) { - this._dirty[i] = true; - } -}; - -/** - * Removes the base texture from the GPU, useful for managing resources on the GPU. - * Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it. - * - */ -BaseTexture.prototype.unloadFromGPU = function () { - this.dirty(); - - // delete the webGL textures if any. - for (var i = this._glTextures.length - 1; i >= 0; i--) { - var glTexture = this._glTextures[i]; - var gl = glContexts[i]; - - if (gl && glTexture) { - gl.deleteTexture(glTexture); - } - - } - - this._glTextures.length = 0; - - this.dirty(); -}; - -/** * Helper function that creates a base texture from the given image url. * If the image is not in the base texture cache it will be created and loaded. * @@ -256,7 +233,7 @@ */ BaseTexture.fromCanvas = function (canvas, scaleMode) { if (!canvas._pixiId) { - canvas._pixiId = 'canvas_' + utils.TextureCacheIdGenerator++; + canvas._pixiId = 'canvas_' + utils.uuid(); } var baseTexture = utils.BaseTextureCache[canvas._pixiId]; diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 4eaef9d..6f01156 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -18,6 +18,8 @@ * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ function CanvasRenderer(width, height, options) { + utils.sayHello('Canvas'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -29,11 +31,6 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('Canvas'); - defaultRenderer = this; - } - /** * The renderer type. * @@ -183,7 +180,7 @@ this.context.globalAlpha = 1; this.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; + this.context.globalCompositeOperation = this.blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9913f0a..bfa6f4a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -8,9 +8,6 @@ utils = require('../../utils'), CONST = require('../../const'); -glContexts = []; // this is where we store the webGL contexts for easy access. -instances = []; - /** * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. @@ -30,6 +27,8 @@ * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ function WebGLRenderer(width, height, options) { + utils.sayHello('webGL'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -41,10 +40,7 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('webGL'); - defaultRenderer = this; - } + this.uuid = utils.uuid(); /** * @member {number} @@ -218,6 +214,8 @@ this.blendModes = null; + utils.webglRenderers.push(this); + // time init the context.. this._initContext(); @@ -229,7 +227,7 @@ WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; -utils.EventTarget.mixin(WebGLRenderer.prototype); +utils.eventTarget.mixin(WebGLRenderer.prototype); Object.defineProperties(WebGLRenderer.prototype, { backgroundColor: { @@ -256,11 +254,9 @@ throw new Error('This browser does not support webGL. Try using the canvas renderer'); } - this.glContextId = gl.id = WebGLRenderer.glContextId ++; - - glContexts[this.glContextId] = gl; - - instances[this.glContextId] = this; + this.glContextId = WebGLRenderer.glContextId++; + gl.id = this.glContextId; + gl.renderer = this; // set up the default pixi settings.. gl.disable(gl.DEPTH_TEST); @@ -371,9 +367,11 @@ /** * Updates and Creates a WebGL texture for the renderers context. * - * @param texture {Texture} the texture to update + * @param texture {BaseTexture|Texture} the texture to update */ WebGLRenderer.prototype.updateTexture = function (texture) { + texture = texture.baseTexture || texture; + if (!texture.hasLoaded) { return; } @@ -382,6 +380,7 @@ if (!texture._glTextures[gl.id]) { texture._glTextures[gl.id] = gl.createTexture(); + texture.on('update', this._boundUpdateTexture); } gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); @@ -410,11 +409,21 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - texture._dirty[gl.id] = false; - return texture._glTextures[gl.id]; }; +WebGLRenderer.prototype.destroyTexture = function (texture) { + texture = texture.baseTexture || texture; + + if (!texture.hasLoaded) { + return; + } + + if (texture._glTextures[this.gl.id]) { + this.gl.deleteTexture(texture._glTextures[this.gl.id]); + } +}; + /** * Handles a lost webgl context * @@ -453,8 +462,6 @@ this.view.removeEventListener('webglcontextlost', this.contextLostBound); this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); - glContexts[this.glContextId] = null; - this.projection = null; this.offset = null; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 60e97be..13ac4cc 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -355,11 +355,6 @@ uniform._init = true; } - // if it has been initialized, check if dirty and needs update - else if (uniform.value.baseTexture._dirty[gl.id] !== false) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - break; default: diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index e0984c4..2a54317 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -551,14 +551,8 @@ var gl = this.gl; - // check if a texture is dirty.. - if (texture._dirty[gl.id]) { - this.renderSession.renderer.updateTexture(texture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - } + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); // now draw those suckas! gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 121a583..34605a2 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -5,7 +5,7 @@ * A texture stores the information that represents an image. All textures have a base texture. * * @class - * @mixes EventTarget + * @mixes eventTarget * @namespace PIXI * @param source {string} the source object (image or canvas) * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values @@ -71,28 +71,23 @@ // used for webGL /** - * @member {Array} - * @private - */ - this._glTextures = []; - - /** * * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used * Also the texture must be a power of two size to work * - * @member {{boolean}} + * @member {boolean} */ this.mipmap = false; - // used for webGL texture updating... - // TODO - this needs to be addressed - /** - * @member {object} + * A map of renderer IDs to webgl textures + * + * @member {object} * @private */ - this._dirty = {}; + this._glTextures = {}; + + this._needsUpdate = false; if (!source) { return; @@ -102,7 +97,7 @@ this.hasLoaded = true; this.width = this.source.naturalWidth || this.source.width; this.height = this.source.naturalHeight || this.source.height; - this.dirty(); + this.needsUpdate = true; } else { var scope = this; @@ -112,15 +107,13 @@ scope.hasLoaded = true; scope.width = scope.source.naturalWidth || scope.source.width; scope.height = scope.source.naturalHeight || scope.source.height; + this.needsUpdate = true; - scope.dirty(); - - // add it to somewhere... - scope.dispatchEvent( { type: 'loaded', content: scope } ); + scope.emit('loaded', scope); }; this.source.onerror = function () { - scope.dispatchEvent( { type: 'error', content: scope } ); + scope.emit('error', scope); }; } @@ -139,7 +132,22 @@ BaseTexture.prototype.constructor = BaseTexture; module.exports = BaseTexture; -utils.EventTarget.mixin(BaseTexture.prototype); +utils.eventTarget.mixin(BaseTexture.prototype); + +Object.defineProperties(BaseTexture.prototype, { + needsUpdate: { + get: function () { + return this._needsUpdate; + }, + set: function (val) { + this._needsUpdate = val; + + if (val) { + this.emit('update', this); + } + } + } +}); /** * Destroys this base texture @@ -159,7 +167,10 @@ } this.source = null; - this.unloadFromGPU(); + // delete the webGL textures if any. + for (var i = 0; i < utils.webglRenderers.length; ++i) { + utils.webglRenderers[i].destroyTexture(this); + } }; /** @@ -169,45 +180,11 @@ */ BaseTexture.prototype.updateSourceImage = function (newSrc) { this.hasLoaded = false; - this.source.src = null; + this.source.src = newSrc; }; /** - * Sets all glTextures to be dirty. - * - */ -BaseTexture.prototype.dirty = function () { - for (var i = 0; i < this._glTextures.length; i++) { - this._dirty[i] = true; - } -}; - -/** - * Removes the base texture from the GPU, useful for managing resources on the GPU. - * Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it. - * - */ -BaseTexture.prototype.unloadFromGPU = function () { - this.dirty(); - - // delete the webGL textures if any. - for (var i = this._glTextures.length - 1; i >= 0; i--) { - var glTexture = this._glTextures[i]; - var gl = glContexts[i]; - - if (gl && glTexture) { - gl.deleteTexture(glTexture); - } - - } - - this._glTextures.length = 0; - - this.dirty(); -}; - -/** * Helper function that creates a base texture from the given image url. * If the image is not in the base texture cache it will be created and loaded. * @@ -256,7 +233,7 @@ */ BaseTexture.fromCanvas = function (canvas, scaleMode) { if (!canvas._pixiId) { - canvas._pixiId = 'canvas_' + utils.TextureCacheIdGenerator++; + canvas._pixiId = 'canvas_' + utils.uuid(); } var baseTexture = utils.BaseTextureCache[canvas._pixiId]; diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index c9b9b31..fbc4d49 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -32,13 +32,17 @@ * @class * @extends Texture * @namespace PIXI - * @param width {number} The width of the render texture - * @param height {number} The height of the render texture * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used for this RenderTexture - * @param scaleMode {number} See {@link scaleModes} for possible values - * @param resolution {number} The resolution of the texture being generated + * @param [width=100] {number} The width of the render texture + * @param [height=100] {number} The height of the render texture + * @param [scaleMode] {number} See {@link scaleModes} for possible values + * @param [resolution=1] {number} The resolution of the texture being generated */ -function RenderTexture(width, height, renderer, scaleMode, resolution) { +function RenderTexture(renderer, width, height, scaleMode, resolution) { + if (!renderer) { + throw new Error('Unable to create RenderTexture, you must pass a renderer into the constructor.'); + } + /** * The with of the render texture * @@ -100,11 +104,10 @@ * * @member {CanvasRenderer|WebGLRenderer} */ - this.renderer = renderer || defaultRenderer; + this.renderer = renderer; if (this.renderer.type === CONST.WEBGL_RENDERER) { var gl = this.renderer.gl; - this.baseTexture._dirty[gl.id] = false; this.textureBuffer = new FilterTexture(gl, this.width * this.resolution, this.height * this.resolution, this.baseTexture.scaleMode); this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 4eaef9d..6f01156 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -18,6 +18,8 @@ * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ function CanvasRenderer(width, height, options) { + utils.sayHello('Canvas'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -29,11 +31,6 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('Canvas'); - defaultRenderer = this; - } - /** * The renderer type. * @@ -183,7 +180,7 @@ this.context.globalAlpha = 1; this.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; + this.context.globalCompositeOperation = this.blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9913f0a..bfa6f4a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -8,9 +8,6 @@ utils = require('../../utils'), CONST = require('../../const'); -glContexts = []; // this is where we store the webGL contexts for easy access. -instances = []; - /** * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. @@ -30,6 +27,8 @@ * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ function WebGLRenderer(width, height, options) { + utils.sayHello('webGL'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -41,10 +40,7 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('webGL'); - defaultRenderer = this; - } + this.uuid = utils.uuid(); /** * @member {number} @@ -218,6 +214,8 @@ this.blendModes = null; + utils.webglRenderers.push(this); + // time init the context.. this._initContext(); @@ -229,7 +227,7 @@ WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; -utils.EventTarget.mixin(WebGLRenderer.prototype); +utils.eventTarget.mixin(WebGLRenderer.prototype); Object.defineProperties(WebGLRenderer.prototype, { backgroundColor: { @@ -256,11 +254,9 @@ throw new Error('This browser does not support webGL. Try using the canvas renderer'); } - this.glContextId = gl.id = WebGLRenderer.glContextId ++; - - glContexts[this.glContextId] = gl; - - instances[this.glContextId] = this; + this.glContextId = WebGLRenderer.glContextId++; + gl.id = this.glContextId; + gl.renderer = this; // set up the default pixi settings.. gl.disable(gl.DEPTH_TEST); @@ -371,9 +367,11 @@ /** * Updates and Creates a WebGL texture for the renderers context. * - * @param texture {Texture} the texture to update + * @param texture {BaseTexture|Texture} the texture to update */ WebGLRenderer.prototype.updateTexture = function (texture) { + texture = texture.baseTexture || texture; + if (!texture.hasLoaded) { return; } @@ -382,6 +380,7 @@ if (!texture._glTextures[gl.id]) { texture._glTextures[gl.id] = gl.createTexture(); + texture.on('update', this._boundUpdateTexture); } gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); @@ -410,11 +409,21 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - texture._dirty[gl.id] = false; - return texture._glTextures[gl.id]; }; +WebGLRenderer.prototype.destroyTexture = function (texture) { + texture = texture.baseTexture || texture; + + if (!texture.hasLoaded) { + return; + } + + if (texture._glTextures[this.gl.id]) { + this.gl.deleteTexture(texture._glTextures[this.gl.id]); + } +}; + /** * Handles a lost webgl context * @@ -453,8 +462,6 @@ this.view.removeEventListener('webglcontextlost', this.contextLostBound); this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); - glContexts[this.glContextId] = null; - this.projection = null; this.offset = null; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 60e97be..13ac4cc 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -355,11 +355,6 @@ uniform._init = true; } - // if it has been initialized, check if dirty and needs update - else if (uniform.value.baseTexture._dirty[gl.id] !== false) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - break; default: diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index e0984c4..2a54317 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -551,14 +551,8 @@ var gl = this.gl; - // check if a texture is dirty.. - if (texture._dirty[gl.id]) { - this.renderSession.renderer.updateTexture(texture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - } + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); // now draw those suckas! gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 121a583..34605a2 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -5,7 +5,7 @@ * A texture stores the information that represents an image. All textures have a base texture. * * @class - * @mixes EventTarget + * @mixes eventTarget * @namespace PIXI * @param source {string} the source object (image or canvas) * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values @@ -71,28 +71,23 @@ // used for webGL /** - * @member {Array} - * @private - */ - this._glTextures = []; - - /** * * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used * Also the texture must be a power of two size to work * - * @member {{boolean}} + * @member {boolean} */ this.mipmap = false; - // used for webGL texture updating... - // TODO - this needs to be addressed - /** - * @member {object} + * A map of renderer IDs to webgl textures + * + * @member {object} * @private */ - this._dirty = {}; + this._glTextures = {}; + + this._needsUpdate = false; if (!source) { return; @@ -102,7 +97,7 @@ this.hasLoaded = true; this.width = this.source.naturalWidth || this.source.width; this.height = this.source.naturalHeight || this.source.height; - this.dirty(); + this.needsUpdate = true; } else { var scope = this; @@ -112,15 +107,13 @@ scope.hasLoaded = true; scope.width = scope.source.naturalWidth || scope.source.width; scope.height = scope.source.naturalHeight || scope.source.height; + this.needsUpdate = true; - scope.dirty(); - - // add it to somewhere... - scope.dispatchEvent( { type: 'loaded', content: scope } ); + scope.emit('loaded', scope); }; this.source.onerror = function () { - scope.dispatchEvent( { type: 'error', content: scope } ); + scope.emit('error', scope); }; } @@ -139,7 +132,22 @@ BaseTexture.prototype.constructor = BaseTexture; module.exports = BaseTexture; -utils.EventTarget.mixin(BaseTexture.prototype); +utils.eventTarget.mixin(BaseTexture.prototype); + +Object.defineProperties(BaseTexture.prototype, { + needsUpdate: { + get: function () { + return this._needsUpdate; + }, + set: function (val) { + this._needsUpdate = val; + + if (val) { + this.emit('update', this); + } + } + } +}); /** * Destroys this base texture @@ -159,7 +167,10 @@ } this.source = null; - this.unloadFromGPU(); + // delete the webGL textures if any. + for (var i = 0; i < utils.webglRenderers.length; ++i) { + utils.webglRenderers[i].destroyTexture(this); + } }; /** @@ -169,45 +180,11 @@ */ BaseTexture.prototype.updateSourceImage = function (newSrc) { this.hasLoaded = false; - this.source.src = null; + this.source.src = newSrc; }; /** - * Sets all glTextures to be dirty. - * - */ -BaseTexture.prototype.dirty = function () { - for (var i = 0; i < this._glTextures.length; i++) { - this._dirty[i] = true; - } -}; - -/** - * Removes the base texture from the GPU, useful for managing resources on the GPU. - * Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it. - * - */ -BaseTexture.prototype.unloadFromGPU = function () { - this.dirty(); - - // delete the webGL textures if any. - for (var i = this._glTextures.length - 1; i >= 0; i--) { - var glTexture = this._glTextures[i]; - var gl = glContexts[i]; - - if (gl && glTexture) { - gl.deleteTexture(glTexture); - } - - } - - this._glTextures.length = 0; - - this.dirty(); -}; - -/** * Helper function that creates a base texture from the given image url. * If the image is not in the base texture cache it will be created and loaded. * @@ -256,7 +233,7 @@ */ BaseTexture.fromCanvas = function (canvas, scaleMode) { if (!canvas._pixiId) { - canvas._pixiId = 'canvas_' + utils.TextureCacheIdGenerator++; + canvas._pixiId = 'canvas_' + utils.uuid(); } var baseTexture = utils.BaseTextureCache[canvas._pixiId]; diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index c9b9b31..fbc4d49 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -32,13 +32,17 @@ * @class * @extends Texture * @namespace PIXI - * @param width {number} The width of the render texture - * @param height {number} The height of the render texture * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used for this RenderTexture - * @param scaleMode {number} See {@link scaleModes} for possible values - * @param resolution {number} The resolution of the texture being generated + * @param [width=100] {number} The width of the render texture + * @param [height=100] {number} The height of the render texture + * @param [scaleMode] {number} See {@link scaleModes} for possible values + * @param [resolution=1] {number} The resolution of the texture being generated */ -function RenderTexture(width, height, renderer, scaleMode, resolution) { +function RenderTexture(renderer, width, height, scaleMode, resolution) { + if (!renderer) { + throw new Error('Unable to create RenderTexture, you must pass a renderer into the constructor.'); + } + /** * The with of the render texture * @@ -100,11 +104,10 @@ * * @member {CanvasRenderer|WebGLRenderer} */ - this.renderer = renderer || defaultRenderer; + this.renderer = renderer; if (this.renderer.type === CONST.WEBGL_RENDERER) { var gl = this.renderer.gl; - this.baseTexture._dirty[gl.id] = false; this.textureBuffer = new FilterTexture(gl, this.width * this.resolution, this.height * this.resolution, this.baseTexture.scaleMode); this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e54a0e8..16ed51c 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -1,5 +1,6 @@ var BaseTexture = require('./BaseTexture'), - EventTarget = require('../utils/EventTarget'), + VideoBaseTexture = require('./VideoBaseTexture'), + eventTarget = require('../utils/eventTarget'), TextureUvs = require('../renderers/webgl/utils/TextureUvs'), math = require('../math'), utils = require('../utils'); @@ -9,7 +10,7 @@ * to the display list directly. Instead use it as the texture for a Sprite. If no frame is provided then the whole image is used. * * @class - * @mixes EventTarget + * @mixes eventTarget * @namespace PIXI * @param baseTexture {BaseTexture} The base texture source to create the texture from * @param [frame] {Rectangle} The rectangle frame of the texture to show @@ -112,7 +113,18 @@ Texture.prototype.constructor = Texture; module.exports = Texture; -EventTarget.mixin(Texture.prototype); +eventTarget.mixin(Texture.prototype); + +Object.defineProperties(BaseTexture.prototype, { + needsUpdate: { + get: function () { + return this.baseTexture.needsUpdate; + }, + set: function (val) { + this.baseTexture.needsUpdate = val; + } + } +}); /** * Called when the base texture is loaded @@ -246,18 +258,27 @@ }; /** - * Helper function that creates a new a Texture based on the given canvas element. + * Helper function that creates a new Texture based on the given canvas element. * * @static * @param canvas {Canvas} The canvas element source of the texture * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return Texture + * @return {Texture} */ Texture.fromCanvas = function (canvas, scaleMode) { - var baseTexture = BaseTexture.fromCanvas(canvas, scaleMode); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); +}; - return new Texture( baseTexture ); - +/** + * Helper function that creates a new Texture based on the given video element. + * + * @static + * @param video {HTMLVideoElement} + * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values + * @return {Texture} A Texture + */ +Texture.fromVideo = function (video, scaleMode) { + return new Texture(VideoBaseTexture.baseTextureFromVideo(video, scaleMode)); }; /** diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 4eaef9d..6f01156 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -18,6 +18,8 @@ * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ function CanvasRenderer(width, height, options) { + utils.sayHello('Canvas'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -29,11 +31,6 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('Canvas'); - defaultRenderer = this; - } - /** * The renderer type. * @@ -183,7 +180,7 @@ this.context.globalAlpha = 1; this.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; + this.context.globalCompositeOperation = this.blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9913f0a..bfa6f4a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -8,9 +8,6 @@ utils = require('../../utils'), CONST = require('../../const'); -glContexts = []; // this is where we store the webGL contexts for easy access. -instances = []; - /** * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. @@ -30,6 +27,8 @@ * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ function WebGLRenderer(width, height, options) { + utils.sayHello('webGL'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -41,10 +40,7 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('webGL'); - defaultRenderer = this; - } + this.uuid = utils.uuid(); /** * @member {number} @@ -218,6 +214,8 @@ this.blendModes = null; + utils.webglRenderers.push(this); + // time init the context.. this._initContext(); @@ -229,7 +227,7 @@ WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; -utils.EventTarget.mixin(WebGLRenderer.prototype); +utils.eventTarget.mixin(WebGLRenderer.prototype); Object.defineProperties(WebGLRenderer.prototype, { backgroundColor: { @@ -256,11 +254,9 @@ throw new Error('This browser does not support webGL. Try using the canvas renderer'); } - this.glContextId = gl.id = WebGLRenderer.glContextId ++; - - glContexts[this.glContextId] = gl; - - instances[this.glContextId] = this; + this.glContextId = WebGLRenderer.glContextId++; + gl.id = this.glContextId; + gl.renderer = this; // set up the default pixi settings.. gl.disable(gl.DEPTH_TEST); @@ -371,9 +367,11 @@ /** * Updates and Creates a WebGL texture for the renderers context. * - * @param texture {Texture} the texture to update + * @param texture {BaseTexture|Texture} the texture to update */ WebGLRenderer.prototype.updateTexture = function (texture) { + texture = texture.baseTexture || texture; + if (!texture.hasLoaded) { return; } @@ -382,6 +380,7 @@ if (!texture._glTextures[gl.id]) { texture._glTextures[gl.id] = gl.createTexture(); + texture.on('update', this._boundUpdateTexture); } gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); @@ -410,11 +409,21 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - texture._dirty[gl.id] = false; - return texture._glTextures[gl.id]; }; +WebGLRenderer.prototype.destroyTexture = function (texture) { + texture = texture.baseTexture || texture; + + if (!texture.hasLoaded) { + return; + } + + if (texture._glTextures[this.gl.id]) { + this.gl.deleteTexture(texture._glTextures[this.gl.id]); + } +}; + /** * Handles a lost webgl context * @@ -453,8 +462,6 @@ this.view.removeEventListener('webglcontextlost', this.contextLostBound); this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); - glContexts[this.glContextId] = null; - this.projection = null; this.offset = null; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 60e97be..13ac4cc 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -355,11 +355,6 @@ uniform._init = true; } - // if it has been initialized, check if dirty and needs update - else if (uniform.value.baseTexture._dirty[gl.id] !== false) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - break; default: diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index e0984c4..2a54317 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -551,14 +551,8 @@ var gl = this.gl; - // check if a texture is dirty.. - if (texture._dirty[gl.id]) { - this.renderSession.renderer.updateTexture(texture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - } + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); // now draw those suckas! gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 121a583..34605a2 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -5,7 +5,7 @@ * A texture stores the information that represents an image. All textures have a base texture. * * @class - * @mixes EventTarget + * @mixes eventTarget * @namespace PIXI * @param source {string} the source object (image or canvas) * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values @@ -71,28 +71,23 @@ // used for webGL /** - * @member {Array} - * @private - */ - this._glTextures = []; - - /** * * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used * Also the texture must be a power of two size to work * - * @member {{boolean}} + * @member {boolean} */ this.mipmap = false; - // used for webGL texture updating... - // TODO - this needs to be addressed - /** - * @member {object} + * A map of renderer IDs to webgl textures + * + * @member {object} * @private */ - this._dirty = {}; + this._glTextures = {}; + + this._needsUpdate = false; if (!source) { return; @@ -102,7 +97,7 @@ this.hasLoaded = true; this.width = this.source.naturalWidth || this.source.width; this.height = this.source.naturalHeight || this.source.height; - this.dirty(); + this.needsUpdate = true; } else { var scope = this; @@ -112,15 +107,13 @@ scope.hasLoaded = true; scope.width = scope.source.naturalWidth || scope.source.width; scope.height = scope.source.naturalHeight || scope.source.height; + this.needsUpdate = true; - scope.dirty(); - - // add it to somewhere... - scope.dispatchEvent( { type: 'loaded', content: scope } ); + scope.emit('loaded', scope); }; this.source.onerror = function () { - scope.dispatchEvent( { type: 'error', content: scope } ); + scope.emit('error', scope); }; } @@ -139,7 +132,22 @@ BaseTexture.prototype.constructor = BaseTexture; module.exports = BaseTexture; -utils.EventTarget.mixin(BaseTexture.prototype); +utils.eventTarget.mixin(BaseTexture.prototype); + +Object.defineProperties(BaseTexture.prototype, { + needsUpdate: { + get: function () { + return this._needsUpdate; + }, + set: function (val) { + this._needsUpdate = val; + + if (val) { + this.emit('update', this); + } + } + } +}); /** * Destroys this base texture @@ -159,7 +167,10 @@ } this.source = null; - this.unloadFromGPU(); + // delete the webGL textures if any. + for (var i = 0; i < utils.webglRenderers.length; ++i) { + utils.webglRenderers[i].destroyTexture(this); + } }; /** @@ -169,45 +180,11 @@ */ BaseTexture.prototype.updateSourceImage = function (newSrc) { this.hasLoaded = false; - this.source.src = null; + this.source.src = newSrc; }; /** - * Sets all glTextures to be dirty. - * - */ -BaseTexture.prototype.dirty = function () { - for (var i = 0; i < this._glTextures.length; i++) { - this._dirty[i] = true; - } -}; - -/** - * Removes the base texture from the GPU, useful for managing resources on the GPU. - * Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it. - * - */ -BaseTexture.prototype.unloadFromGPU = function () { - this.dirty(); - - // delete the webGL textures if any. - for (var i = this._glTextures.length - 1; i >= 0; i--) { - var glTexture = this._glTextures[i]; - var gl = glContexts[i]; - - if (gl && glTexture) { - gl.deleteTexture(glTexture); - } - - } - - this._glTextures.length = 0; - - this.dirty(); -}; - -/** * Helper function that creates a base texture from the given image url. * If the image is not in the base texture cache it will be created and loaded. * @@ -256,7 +233,7 @@ */ BaseTexture.fromCanvas = function (canvas, scaleMode) { if (!canvas._pixiId) { - canvas._pixiId = 'canvas_' + utils.TextureCacheIdGenerator++; + canvas._pixiId = 'canvas_' + utils.uuid(); } var baseTexture = utils.BaseTextureCache[canvas._pixiId]; diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index c9b9b31..fbc4d49 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -32,13 +32,17 @@ * @class * @extends Texture * @namespace PIXI - * @param width {number} The width of the render texture - * @param height {number} The height of the render texture * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used for this RenderTexture - * @param scaleMode {number} See {@link scaleModes} for possible values - * @param resolution {number} The resolution of the texture being generated + * @param [width=100] {number} The width of the render texture + * @param [height=100] {number} The height of the render texture + * @param [scaleMode] {number} See {@link scaleModes} for possible values + * @param [resolution=1] {number} The resolution of the texture being generated */ -function RenderTexture(width, height, renderer, scaleMode, resolution) { +function RenderTexture(renderer, width, height, scaleMode, resolution) { + if (!renderer) { + throw new Error('Unable to create RenderTexture, you must pass a renderer into the constructor.'); + } + /** * The with of the render texture * @@ -100,11 +104,10 @@ * * @member {CanvasRenderer|WebGLRenderer} */ - this.renderer = renderer || defaultRenderer; + this.renderer = renderer; if (this.renderer.type === CONST.WEBGL_RENDERER) { var gl = this.renderer.gl; - this.baseTexture._dirty[gl.id] = false; this.textureBuffer = new FilterTexture(gl, this.width * this.resolution, this.height * this.resolution, this.baseTexture.scaleMode); this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e54a0e8..16ed51c 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -1,5 +1,6 @@ var BaseTexture = require('./BaseTexture'), - EventTarget = require('../utils/EventTarget'), + VideoBaseTexture = require('./VideoBaseTexture'), + eventTarget = require('../utils/eventTarget'), TextureUvs = require('../renderers/webgl/utils/TextureUvs'), math = require('../math'), utils = require('../utils'); @@ -9,7 +10,7 @@ * to the display list directly. Instead use it as the texture for a Sprite. If no frame is provided then the whole image is used. * * @class - * @mixes EventTarget + * @mixes eventTarget * @namespace PIXI * @param baseTexture {BaseTexture} The base texture source to create the texture from * @param [frame] {Rectangle} The rectangle frame of the texture to show @@ -112,7 +113,18 @@ Texture.prototype.constructor = Texture; module.exports = Texture; -EventTarget.mixin(Texture.prototype); +eventTarget.mixin(Texture.prototype); + +Object.defineProperties(BaseTexture.prototype, { + needsUpdate: { + get: function () { + return this.baseTexture.needsUpdate; + }, + set: function (val) { + this.baseTexture.needsUpdate = val; + } + } +}); /** * Called when the base texture is loaded @@ -246,18 +258,27 @@ }; /** - * Helper function that creates a new a Texture based on the given canvas element. + * Helper function that creates a new Texture based on the given canvas element. * * @static * @param canvas {Canvas} The canvas element source of the texture * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return Texture + * @return {Texture} */ Texture.fromCanvas = function (canvas, scaleMode) { - var baseTexture = BaseTexture.fromCanvas(canvas, scaleMode); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); +}; - return new Texture( baseTexture ); - +/** + * Helper function that creates a new Texture based on the given video element. + * + * @static + * @param video {HTMLVideoElement} + * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values + * @return {Texture} A Texture + */ +Texture.fromVideo = function (video, scaleMode) { + return new Texture(VideoBaseTexture.baseTextureFromVideo(video, scaleMode)); }; /** diff --git a/src/core/textures/VideoTexture.js b/src/core/textures/VideoTexture.js deleted file mode 100644 index f61f377..0000000 --- a/src/core/textures/VideoTexture.js +++ /dev/null @@ -1,150 +0,0 @@ -var BaseTexture = require('./BaseTexture'), - Texture = require('./Texture'), - utils = require('../utils'); - -/** - * A texture of a [playing] Video. - * - * See the ["deus" demo](http://www.goodboydigital.com/pixijs/examples/deus/). - * - * @class - * @extends BaseTexture - * @namespace PIXI - * @param source {HTMLVideoElement} - * @param [scaleMode] {number} See {@link scaleModes} for possible values - */ -function VideoTexture(source, scaleMode) { - if (!source){ - throw new Error('No video source element specified.'); - } - - // hook in here to check if video is already available. - // BaseTexture looks for a source.complete boolean, plus width & height. - - if ((source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA) && source.width && source.height) { - source.complete = true; - } - - BaseTexture.call(this, source, scaleMode); - - this.autoUpdate = false; - this.updateBound = this._onUpdate.bind(this); - - if (!source.complete) { - this._onCanPlay = this.onCanPlay.bind(this); - - source.addEventListener('canplay', this._onCanPlay); - source.addEventListener('canplaythrough', this._onCanPlay); - - // started playing.. - source.addEventListener('play', this.onPlayStart.bind(this)); - source.addEventListener('pause', this.onPlayStop.bind(this)); - } -} - -VideoTexture.prototype = Object.create(BaseTexture.prototype); -VideoTexture.prototype.constructor = VideoTexture; -module.exports = VideoTexture; - -VideoTexture.prototype._onUpdate = function () { - if (this.autoUpdate) { - window.requestAnimationFrame(this.updateBound); - this.dirty(); - } -}; - -VideoTexture.prototype.onPlayStart = function () { - if (!this.autoUpdate) { - window.requestAnimationFrame(this.updateBound); - this.autoUpdate = true; - } -}; - -VideoTexture.prototype.onPlayStop = function () { - this.autoUpdate = false; -}; - -VideoTexture.prototype.onCanPlay = function () { - if (event.type === 'canplaythrough') { - this.hasLoaded = true; - - - if (this.source) { - this.source.removeEventListener('canplay', this._onCanPlay); - this.source.removeEventListener('canplaythrough', this._onCanPlay); - - this.width = this.source.videoWidth; - this.height = this.source.videoHeight; - - // prevent multiple loaded dispatches.. - if (!this.__loaded){ - this.__loaded = true; - this.dispatchEvent({ type: 'loaded', content: this }); - } - } - } -}; - -VideoTexture.prototype.destroy = function () { - if (this.source && this.source._pixiId) { - utils.BaseTextureCache[ this.source._pixiId ] = null; - delete utils.BaseTextureCache[ this.source._pixiId ]; - - this.source._pixiId = null; - delete this.source._pixiId; - } - - BaseTexture.prototype.destroy.call(this); -}; - -/** - * Mimic Pixi BaseTexture.from.... method. - * - * @static - * @param video {HTMLVideoElement} - * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return {VideoTexture} - */ -VideoTexture.baseTextureFromVideo = function (video, scaleMode) { - if (!video._pixiId) { - video._pixiId = 'video_' + utils.TextureCacheIdGenerator++; - } - - var baseTexture = utils.BaseTextureCache[ video._pixiId ]; - - if (!baseTexture) { - baseTexture = new VideoTexture(video, scaleMode); - utils.BaseTextureCache[ video._pixiId ] = baseTexture; - } - - return baseTexture; -}; - -/** - * Mimic Pixi BaseTexture.from.... method. - * - * @static - * @param video {HTMLVideoElement} - * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return {Texture} A Texture, but not a VideoTexture. - */ -VideoTexture.textureFromVideo = function (video, scaleMode) { - var baseTexture = VideoTexture.baseTextureFromVideo(video, scaleMode); - return new Texture(baseTexture); -}; - -/** - * Mimic Pixi BaseTexture.from.... method. - * - * @static - * @param videoSrc {string} The URL for the video. - * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return {VideoTexture} - */ -VideoTexture.fromUrl = function (videoSrc, scaleMode) { - var video = document.createElement('video'); - video.src = videoSrc; - video.autoPlay = true; - video.play(); - return VideoTexture.textureFromVideo(video, scaleMode); -}; diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 4eaef9d..6f01156 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -18,6 +18,8 @@ * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ function CanvasRenderer(width, height, options) { + utils.sayHello('Canvas'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -29,11 +31,6 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('Canvas'); - defaultRenderer = this; - } - /** * The renderer type. * @@ -183,7 +180,7 @@ this.context.globalAlpha = 1; this.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; + this.context.globalCompositeOperation = this.blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9913f0a..bfa6f4a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -8,9 +8,6 @@ utils = require('../../utils'), CONST = require('../../const'); -glContexts = []; // this is where we store the webGL contexts for easy access. -instances = []; - /** * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. @@ -30,6 +27,8 @@ * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ function WebGLRenderer(width, height, options) { + utils.sayHello('webGL'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -41,10 +40,7 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('webGL'); - defaultRenderer = this; - } + this.uuid = utils.uuid(); /** * @member {number} @@ -218,6 +214,8 @@ this.blendModes = null; + utils.webglRenderers.push(this); + // time init the context.. this._initContext(); @@ -229,7 +227,7 @@ WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; -utils.EventTarget.mixin(WebGLRenderer.prototype); +utils.eventTarget.mixin(WebGLRenderer.prototype); Object.defineProperties(WebGLRenderer.prototype, { backgroundColor: { @@ -256,11 +254,9 @@ throw new Error('This browser does not support webGL. Try using the canvas renderer'); } - this.glContextId = gl.id = WebGLRenderer.glContextId ++; - - glContexts[this.glContextId] = gl; - - instances[this.glContextId] = this; + this.glContextId = WebGLRenderer.glContextId++; + gl.id = this.glContextId; + gl.renderer = this; // set up the default pixi settings.. gl.disable(gl.DEPTH_TEST); @@ -371,9 +367,11 @@ /** * Updates and Creates a WebGL texture for the renderers context. * - * @param texture {Texture} the texture to update + * @param texture {BaseTexture|Texture} the texture to update */ WebGLRenderer.prototype.updateTexture = function (texture) { + texture = texture.baseTexture || texture; + if (!texture.hasLoaded) { return; } @@ -382,6 +380,7 @@ if (!texture._glTextures[gl.id]) { texture._glTextures[gl.id] = gl.createTexture(); + texture.on('update', this._boundUpdateTexture); } gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); @@ -410,11 +409,21 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - texture._dirty[gl.id] = false; - return texture._glTextures[gl.id]; }; +WebGLRenderer.prototype.destroyTexture = function (texture) { + texture = texture.baseTexture || texture; + + if (!texture.hasLoaded) { + return; + } + + if (texture._glTextures[this.gl.id]) { + this.gl.deleteTexture(texture._glTextures[this.gl.id]); + } +}; + /** * Handles a lost webgl context * @@ -453,8 +462,6 @@ this.view.removeEventListener('webglcontextlost', this.contextLostBound); this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); - glContexts[this.glContextId] = null; - this.projection = null; this.offset = null; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 60e97be..13ac4cc 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -355,11 +355,6 @@ uniform._init = true; } - // if it has been initialized, check if dirty and needs update - else if (uniform.value.baseTexture._dirty[gl.id] !== false) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - break; default: diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index e0984c4..2a54317 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -551,14 +551,8 @@ var gl = this.gl; - // check if a texture is dirty.. - if (texture._dirty[gl.id]) { - this.renderSession.renderer.updateTexture(texture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - } + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); // now draw those suckas! gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 121a583..34605a2 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -5,7 +5,7 @@ * A texture stores the information that represents an image. All textures have a base texture. * * @class - * @mixes EventTarget + * @mixes eventTarget * @namespace PIXI * @param source {string} the source object (image or canvas) * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values @@ -71,28 +71,23 @@ // used for webGL /** - * @member {Array} - * @private - */ - this._glTextures = []; - - /** * * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used * Also the texture must be a power of two size to work * - * @member {{boolean}} + * @member {boolean} */ this.mipmap = false; - // used for webGL texture updating... - // TODO - this needs to be addressed - /** - * @member {object} + * A map of renderer IDs to webgl textures + * + * @member {object} * @private */ - this._dirty = {}; + this._glTextures = {}; + + this._needsUpdate = false; if (!source) { return; @@ -102,7 +97,7 @@ this.hasLoaded = true; this.width = this.source.naturalWidth || this.source.width; this.height = this.source.naturalHeight || this.source.height; - this.dirty(); + this.needsUpdate = true; } else { var scope = this; @@ -112,15 +107,13 @@ scope.hasLoaded = true; scope.width = scope.source.naturalWidth || scope.source.width; scope.height = scope.source.naturalHeight || scope.source.height; + this.needsUpdate = true; - scope.dirty(); - - // add it to somewhere... - scope.dispatchEvent( { type: 'loaded', content: scope } ); + scope.emit('loaded', scope); }; this.source.onerror = function () { - scope.dispatchEvent( { type: 'error', content: scope } ); + scope.emit('error', scope); }; } @@ -139,7 +132,22 @@ BaseTexture.prototype.constructor = BaseTexture; module.exports = BaseTexture; -utils.EventTarget.mixin(BaseTexture.prototype); +utils.eventTarget.mixin(BaseTexture.prototype); + +Object.defineProperties(BaseTexture.prototype, { + needsUpdate: { + get: function () { + return this._needsUpdate; + }, + set: function (val) { + this._needsUpdate = val; + + if (val) { + this.emit('update', this); + } + } + } +}); /** * Destroys this base texture @@ -159,7 +167,10 @@ } this.source = null; - this.unloadFromGPU(); + // delete the webGL textures if any. + for (var i = 0; i < utils.webglRenderers.length; ++i) { + utils.webglRenderers[i].destroyTexture(this); + } }; /** @@ -169,45 +180,11 @@ */ BaseTexture.prototype.updateSourceImage = function (newSrc) { this.hasLoaded = false; - this.source.src = null; + this.source.src = newSrc; }; /** - * Sets all glTextures to be dirty. - * - */ -BaseTexture.prototype.dirty = function () { - for (var i = 0; i < this._glTextures.length; i++) { - this._dirty[i] = true; - } -}; - -/** - * Removes the base texture from the GPU, useful for managing resources on the GPU. - * Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it. - * - */ -BaseTexture.prototype.unloadFromGPU = function () { - this.dirty(); - - // delete the webGL textures if any. - for (var i = this._glTextures.length - 1; i >= 0; i--) { - var glTexture = this._glTextures[i]; - var gl = glContexts[i]; - - if (gl && glTexture) { - gl.deleteTexture(glTexture); - } - - } - - this._glTextures.length = 0; - - this.dirty(); -}; - -/** * Helper function that creates a base texture from the given image url. * If the image is not in the base texture cache it will be created and loaded. * @@ -256,7 +233,7 @@ */ BaseTexture.fromCanvas = function (canvas, scaleMode) { if (!canvas._pixiId) { - canvas._pixiId = 'canvas_' + utils.TextureCacheIdGenerator++; + canvas._pixiId = 'canvas_' + utils.uuid(); } var baseTexture = utils.BaseTextureCache[canvas._pixiId]; diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index c9b9b31..fbc4d49 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -32,13 +32,17 @@ * @class * @extends Texture * @namespace PIXI - * @param width {number} The width of the render texture - * @param height {number} The height of the render texture * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used for this RenderTexture - * @param scaleMode {number} See {@link scaleModes} for possible values - * @param resolution {number} The resolution of the texture being generated + * @param [width=100] {number} The width of the render texture + * @param [height=100] {number} The height of the render texture + * @param [scaleMode] {number} See {@link scaleModes} for possible values + * @param [resolution=1] {number} The resolution of the texture being generated */ -function RenderTexture(width, height, renderer, scaleMode, resolution) { +function RenderTexture(renderer, width, height, scaleMode, resolution) { + if (!renderer) { + throw new Error('Unable to create RenderTexture, you must pass a renderer into the constructor.'); + } + /** * The with of the render texture * @@ -100,11 +104,10 @@ * * @member {CanvasRenderer|WebGLRenderer} */ - this.renderer = renderer || defaultRenderer; + this.renderer = renderer; if (this.renderer.type === CONST.WEBGL_RENDERER) { var gl = this.renderer.gl; - this.baseTexture._dirty[gl.id] = false; this.textureBuffer = new FilterTexture(gl, this.width * this.resolution, this.height * this.resolution, this.baseTexture.scaleMode); this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e54a0e8..16ed51c 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -1,5 +1,6 @@ var BaseTexture = require('./BaseTexture'), - EventTarget = require('../utils/EventTarget'), + VideoBaseTexture = require('./VideoBaseTexture'), + eventTarget = require('../utils/eventTarget'), TextureUvs = require('../renderers/webgl/utils/TextureUvs'), math = require('../math'), utils = require('../utils'); @@ -9,7 +10,7 @@ * to the display list directly. Instead use it as the texture for a Sprite. If no frame is provided then the whole image is used. * * @class - * @mixes EventTarget + * @mixes eventTarget * @namespace PIXI * @param baseTexture {BaseTexture} The base texture source to create the texture from * @param [frame] {Rectangle} The rectangle frame of the texture to show @@ -112,7 +113,18 @@ Texture.prototype.constructor = Texture; module.exports = Texture; -EventTarget.mixin(Texture.prototype); +eventTarget.mixin(Texture.prototype); + +Object.defineProperties(BaseTexture.prototype, { + needsUpdate: { + get: function () { + return this.baseTexture.needsUpdate; + }, + set: function (val) { + this.baseTexture.needsUpdate = val; + } + } +}); /** * Called when the base texture is loaded @@ -246,18 +258,27 @@ }; /** - * Helper function that creates a new a Texture based on the given canvas element. + * Helper function that creates a new Texture based on the given canvas element. * * @static * @param canvas {Canvas} The canvas element source of the texture * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return Texture + * @return {Texture} */ Texture.fromCanvas = function (canvas, scaleMode) { - var baseTexture = BaseTexture.fromCanvas(canvas, scaleMode); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); +}; - return new Texture( baseTexture ); - +/** + * Helper function that creates a new Texture based on the given video element. + * + * @static + * @param video {HTMLVideoElement} + * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values + * @return {Texture} A Texture + */ +Texture.fromVideo = function (video, scaleMode) { + return new Texture(VideoBaseTexture.baseTextureFromVideo(video, scaleMode)); }; /** diff --git a/src/core/textures/VideoTexture.js b/src/core/textures/VideoTexture.js deleted file mode 100644 index f61f377..0000000 --- a/src/core/textures/VideoTexture.js +++ /dev/null @@ -1,150 +0,0 @@ -var BaseTexture = require('./BaseTexture'), - Texture = require('./Texture'), - utils = require('../utils'); - -/** - * A texture of a [playing] Video. - * - * See the ["deus" demo](http://www.goodboydigital.com/pixijs/examples/deus/). - * - * @class - * @extends BaseTexture - * @namespace PIXI - * @param source {HTMLVideoElement} - * @param [scaleMode] {number} See {@link scaleModes} for possible values - */ -function VideoTexture(source, scaleMode) { - if (!source){ - throw new Error('No video source element specified.'); - } - - // hook in here to check if video is already available. - // BaseTexture looks for a source.complete boolean, plus width & height. - - if ((source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA) && source.width && source.height) { - source.complete = true; - } - - BaseTexture.call(this, source, scaleMode); - - this.autoUpdate = false; - this.updateBound = this._onUpdate.bind(this); - - if (!source.complete) { - this._onCanPlay = this.onCanPlay.bind(this); - - source.addEventListener('canplay', this._onCanPlay); - source.addEventListener('canplaythrough', this._onCanPlay); - - // started playing.. - source.addEventListener('play', this.onPlayStart.bind(this)); - source.addEventListener('pause', this.onPlayStop.bind(this)); - } -} - -VideoTexture.prototype = Object.create(BaseTexture.prototype); -VideoTexture.prototype.constructor = VideoTexture; -module.exports = VideoTexture; - -VideoTexture.prototype._onUpdate = function () { - if (this.autoUpdate) { - window.requestAnimationFrame(this.updateBound); - this.dirty(); - } -}; - -VideoTexture.prototype.onPlayStart = function () { - if (!this.autoUpdate) { - window.requestAnimationFrame(this.updateBound); - this.autoUpdate = true; - } -}; - -VideoTexture.prototype.onPlayStop = function () { - this.autoUpdate = false; -}; - -VideoTexture.prototype.onCanPlay = function () { - if (event.type === 'canplaythrough') { - this.hasLoaded = true; - - - if (this.source) { - this.source.removeEventListener('canplay', this._onCanPlay); - this.source.removeEventListener('canplaythrough', this._onCanPlay); - - this.width = this.source.videoWidth; - this.height = this.source.videoHeight; - - // prevent multiple loaded dispatches.. - if (!this.__loaded){ - this.__loaded = true; - this.dispatchEvent({ type: 'loaded', content: this }); - } - } - } -}; - -VideoTexture.prototype.destroy = function () { - if (this.source && this.source._pixiId) { - utils.BaseTextureCache[ this.source._pixiId ] = null; - delete utils.BaseTextureCache[ this.source._pixiId ]; - - this.source._pixiId = null; - delete this.source._pixiId; - } - - BaseTexture.prototype.destroy.call(this); -}; - -/** - * Mimic Pixi BaseTexture.from.... method. - * - * @static - * @param video {HTMLVideoElement} - * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return {VideoTexture} - */ -VideoTexture.baseTextureFromVideo = function (video, scaleMode) { - if (!video._pixiId) { - video._pixiId = 'video_' + utils.TextureCacheIdGenerator++; - } - - var baseTexture = utils.BaseTextureCache[ video._pixiId ]; - - if (!baseTexture) { - baseTexture = new VideoTexture(video, scaleMode); - utils.BaseTextureCache[ video._pixiId ] = baseTexture; - } - - return baseTexture; -}; - -/** - * Mimic Pixi BaseTexture.from.... method. - * - * @static - * @param video {HTMLVideoElement} - * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return {Texture} A Texture, but not a VideoTexture. - */ -VideoTexture.textureFromVideo = function (video, scaleMode) { - var baseTexture = VideoTexture.baseTextureFromVideo(video, scaleMode); - return new Texture(baseTexture); -}; - -/** - * Mimic Pixi BaseTexture.from.... method. - * - * @static - * @param videoSrc {string} The URL for the video. - * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return {VideoTexture} - */ -VideoTexture.fromUrl = function (videoSrc, scaleMode) { - var video = document.createElement('video'); - video.src = videoSrc; - video.autoPlay = true; - video.play(); - return VideoTexture.textureFromVideo(video, scaleMode); -}; diff --git a/src/core/utils/EventTarget.js b/src/core/utils/EventTarget.js index 9c374b5..c8e59fe 100644 --- a/src/core/utils/EventTarget.js +++ b/src/core/utils/EventTarget.js @@ -8,12 +8,12 @@ * @example * function MyEmitter() {} * - * EventTarget.mixin(MyEmitter.prototype); + * eventTarget.mixin(MyEmitter.prototype); * * var em = new MyEmitter(); * em.emit('eventName', 'some data', 'some more data', {}, null, ...); */ -function EventTarget(obj) { +function eventTarget(obj) { /** * Return a list of assigned event listeners. * @@ -162,11 +162,11 @@ module.exports = { /** - * Mixes in the properties of the EventTarget prototype onto another object + * Mixes in the properties of the eventTarget prototype onto another object * * @param object {object} The obj to mix into */ mixin: function mixin(obj) { - EventTarget(obj); + eventTarget(obj); } }; diff --git a/src/core/renderers/canvas/CanvasRenderer.js b/src/core/renderers/canvas/CanvasRenderer.js index 4eaef9d..6f01156 100644 --- a/src/core/renderers/canvas/CanvasRenderer.js +++ b/src/core/renderers/canvas/CanvasRenderer.js @@ -18,6 +18,8 @@ * @param [options.clearBeforeRender=true] {boolean} This sets if the CanvasRenderer will clear the canvas or not before the new render pass. */ function CanvasRenderer(width, height, options) { + utils.sayHello('Canvas'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -29,11 +31,6 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('Canvas'); - defaultRenderer = this; - } - /** * The renderer type. * @@ -183,7 +180,7 @@ this.context.globalAlpha = 1; this.currentBlendMode = CONST.blendModes.NORMAL; - this.context.globalCompositeOperation = blendModes[CONST.blendModes.NORMAL]; + this.context.globalCompositeOperation = this.blendModes[CONST.blendModes.NORMAL]; if (navigator.isCocoonJS && this.view.screencanvas) { this.context.fillStyle = 'black'; diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9913f0a..bfa6f4a 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -8,9 +8,6 @@ utils = require('../../utils'), CONST = require('../../const'); -glContexts = []; // this is where we store the webGL contexts for easy access. -instances = []; - /** * The WebGLRenderer draws the scene and all its content onto a webGL enabled canvas. This renderer * should be used for browsers that support webGL. This Render works by automatically managing webGLBatchs. @@ -30,6 +27,8 @@ * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 */ function WebGLRenderer(width, height, options) { + utils.sayHello('webGL'); + if (options) { for (var i in CONST.defaultRenderOptions) { if (typeof options[i] === 'undefined') { @@ -41,10 +40,7 @@ options = CONST.defaultRenderOptions; } - if (!defaultRenderer) { - utils.sayHello('webGL'); - defaultRenderer = this; - } + this.uuid = utils.uuid(); /** * @member {number} @@ -218,6 +214,8 @@ this.blendModes = null; + utils.webglRenderers.push(this); + // time init the context.. this._initContext(); @@ -229,7 +227,7 @@ WebGLRenderer.prototype.constructor = WebGLRenderer; module.exports = WebGLRenderer; -utils.EventTarget.mixin(WebGLRenderer.prototype); +utils.eventTarget.mixin(WebGLRenderer.prototype); Object.defineProperties(WebGLRenderer.prototype, { backgroundColor: { @@ -256,11 +254,9 @@ throw new Error('This browser does not support webGL. Try using the canvas renderer'); } - this.glContextId = gl.id = WebGLRenderer.glContextId ++; - - glContexts[this.glContextId] = gl; - - instances[this.glContextId] = this; + this.glContextId = WebGLRenderer.glContextId++; + gl.id = this.glContextId; + gl.renderer = this; // set up the default pixi settings.. gl.disable(gl.DEPTH_TEST); @@ -371,9 +367,11 @@ /** * Updates and Creates a WebGL texture for the renderers context. * - * @param texture {Texture} the texture to update + * @param texture {BaseTexture|Texture} the texture to update */ WebGLRenderer.prototype.updateTexture = function (texture) { + texture = texture.baseTexture || texture; + if (!texture.hasLoaded) { return; } @@ -382,6 +380,7 @@ if (!texture._glTextures[gl.id]) { texture._glTextures[gl.id] = gl.createTexture(); + texture.on('update', this._boundUpdateTexture); } gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); @@ -410,11 +409,21 @@ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); } - texture._dirty[gl.id] = false; - return texture._glTextures[gl.id]; }; +WebGLRenderer.prototype.destroyTexture = function (texture) { + texture = texture.baseTexture || texture; + + if (!texture.hasLoaded) { + return; + } + + if (texture._glTextures[this.gl.id]) { + this.gl.deleteTexture(texture._glTextures[this.gl.id]); + } +}; + /** * Handles a lost webgl context * @@ -453,8 +462,6 @@ this.view.removeEventListener('webglcontextlost', this.contextLostBound); this.view.removeEventListener('webglcontextrestored', this.contextRestoredBound); - glContexts[this.glContextId] = null; - this.projection = null; this.offset = null; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 60e97be..13ac4cc 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -355,11 +355,6 @@ uniform._init = true; } - // if it has been initialized, check if dirty and needs update - else if (uniform.value.baseTexture._dirty[gl.id] !== false) { - instances[gl.id].updateTexture(uniform.value.baseTexture); - } - break; default: diff --git a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js index e0984c4..2a54317 100644 --- a/src/core/renderers/webgl/utils/WebGLSpriteBatch.js +++ b/src/core/renderers/webgl/utils/WebGLSpriteBatch.js @@ -551,14 +551,8 @@ var gl = this.gl; - // check if a texture is dirty.. - if (texture._dirty[gl.id]) { - this.renderSession.renderer.updateTexture(texture); - } - else { - // bind the current texture - gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); - } + // bind the current texture + gl.bindTexture(gl.TEXTURE_2D, texture._glTextures[gl.id]); // now draw those suckas! gl.drawElements(gl.TRIANGLES, size * 6, gl.UNSIGNED_SHORT, startIndex * 6 * 2); diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 121a583..34605a2 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -5,7 +5,7 @@ * A texture stores the information that represents an image. All textures have a base texture. * * @class - * @mixes EventTarget + * @mixes eventTarget * @namespace PIXI * @param source {string} the source object (image or canvas) * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values @@ -71,28 +71,23 @@ // used for webGL /** - * @member {Array} - * @private - */ - this._glTextures = []; - - /** * * Set this to true if a mipmap of this texture needs to be generated. This value needs to be set before the texture is used * Also the texture must be a power of two size to work * - * @member {{boolean}} + * @member {boolean} */ this.mipmap = false; - // used for webGL texture updating... - // TODO - this needs to be addressed - /** - * @member {object} + * A map of renderer IDs to webgl textures + * + * @member {object} * @private */ - this._dirty = {}; + this._glTextures = {}; + + this._needsUpdate = false; if (!source) { return; @@ -102,7 +97,7 @@ this.hasLoaded = true; this.width = this.source.naturalWidth || this.source.width; this.height = this.source.naturalHeight || this.source.height; - this.dirty(); + this.needsUpdate = true; } else { var scope = this; @@ -112,15 +107,13 @@ scope.hasLoaded = true; scope.width = scope.source.naturalWidth || scope.source.width; scope.height = scope.source.naturalHeight || scope.source.height; + this.needsUpdate = true; - scope.dirty(); - - // add it to somewhere... - scope.dispatchEvent( { type: 'loaded', content: scope } ); + scope.emit('loaded', scope); }; this.source.onerror = function () { - scope.dispatchEvent( { type: 'error', content: scope } ); + scope.emit('error', scope); }; } @@ -139,7 +132,22 @@ BaseTexture.prototype.constructor = BaseTexture; module.exports = BaseTexture; -utils.EventTarget.mixin(BaseTexture.prototype); +utils.eventTarget.mixin(BaseTexture.prototype); + +Object.defineProperties(BaseTexture.prototype, { + needsUpdate: { + get: function () { + return this._needsUpdate; + }, + set: function (val) { + this._needsUpdate = val; + + if (val) { + this.emit('update', this); + } + } + } +}); /** * Destroys this base texture @@ -159,7 +167,10 @@ } this.source = null; - this.unloadFromGPU(); + // delete the webGL textures if any. + for (var i = 0; i < utils.webglRenderers.length; ++i) { + utils.webglRenderers[i].destroyTexture(this); + } }; /** @@ -169,45 +180,11 @@ */ BaseTexture.prototype.updateSourceImage = function (newSrc) { this.hasLoaded = false; - this.source.src = null; + this.source.src = newSrc; }; /** - * Sets all glTextures to be dirty. - * - */ -BaseTexture.prototype.dirty = function () { - for (var i = 0; i < this._glTextures.length; i++) { - this._dirty[i] = true; - } -}; - -/** - * Removes the base texture from the GPU, useful for managing resources on the GPU. - * Atexture is still 100% usable and will simply be reuploaded if there is a sprite on screen that is using it. - * - */ -BaseTexture.prototype.unloadFromGPU = function () { - this.dirty(); - - // delete the webGL textures if any. - for (var i = this._glTextures.length - 1; i >= 0; i--) { - var glTexture = this._glTextures[i]; - var gl = glContexts[i]; - - if (gl && glTexture) { - gl.deleteTexture(glTexture); - } - - } - - this._glTextures.length = 0; - - this.dirty(); -}; - -/** * Helper function that creates a base texture from the given image url. * If the image is not in the base texture cache it will be created and loaded. * @@ -256,7 +233,7 @@ */ BaseTexture.fromCanvas = function (canvas, scaleMode) { if (!canvas._pixiId) { - canvas._pixiId = 'canvas_' + utils.TextureCacheIdGenerator++; + canvas._pixiId = 'canvas_' + utils.uuid(); } var baseTexture = utils.BaseTextureCache[canvas._pixiId]; diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index c9b9b31..fbc4d49 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -32,13 +32,17 @@ * @class * @extends Texture * @namespace PIXI - * @param width {number} The width of the render texture - * @param height {number} The height of the render texture * @param renderer {CanvasRenderer|WebGLRenderer} The renderer used for this RenderTexture - * @param scaleMode {number} See {@link scaleModes} for possible values - * @param resolution {number} The resolution of the texture being generated + * @param [width=100] {number} The width of the render texture + * @param [height=100] {number} The height of the render texture + * @param [scaleMode] {number} See {@link scaleModes} for possible values + * @param [resolution=1] {number} The resolution of the texture being generated */ -function RenderTexture(width, height, renderer, scaleMode, resolution) { +function RenderTexture(renderer, width, height, scaleMode, resolution) { + if (!renderer) { + throw new Error('Unable to create RenderTexture, you must pass a renderer into the constructor.'); + } + /** * The with of the render texture * @@ -100,11 +104,10 @@ * * @member {CanvasRenderer|WebGLRenderer} */ - this.renderer = renderer || defaultRenderer; + this.renderer = renderer; if (this.renderer.type === CONST.WEBGL_RENDERER) { var gl = this.renderer.gl; - this.baseTexture._dirty[gl.id] = false; this.textureBuffer = new FilterTexture(gl, this.width * this.resolution, this.height * this.resolution, this.baseTexture.scaleMode); this.baseTexture._glTextures[gl.id] = this.textureBuffer.texture; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e54a0e8..16ed51c 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -1,5 +1,6 @@ var BaseTexture = require('./BaseTexture'), - EventTarget = require('../utils/EventTarget'), + VideoBaseTexture = require('./VideoBaseTexture'), + eventTarget = require('../utils/eventTarget'), TextureUvs = require('../renderers/webgl/utils/TextureUvs'), math = require('../math'), utils = require('../utils'); @@ -9,7 +10,7 @@ * to the display list directly. Instead use it as the texture for a Sprite. If no frame is provided then the whole image is used. * * @class - * @mixes EventTarget + * @mixes eventTarget * @namespace PIXI * @param baseTexture {BaseTexture} The base texture source to create the texture from * @param [frame] {Rectangle} The rectangle frame of the texture to show @@ -112,7 +113,18 @@ Texture.prototype.constructor = Texture; module.exports = Texture; -EventTarget.mixin(Texture.prototype); +eventTarget.mixin(Texture.prototype); + +Object.defineProperties(BaseTexture.prototype, { + needsUpdate: { + get: function () { + return this.baseTexture.needsUpdate; + }, + set: function (val) { + this.baseTexture.needsUpdate = val; + } + } +}); /** * Called when the base texture is loaded @@ -246,18 +258,27 @@ }; /** - * Helper function that creates a new a Texture based on the given canvas element. + * Helper function that creates a new Texture based on the given canvas element. * * @static * @param canvas {Canvas} The canvas element source of the texture * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return Texture + * @return {Texture} */ Texture.fromCanvas = function (canvas, scaleMode) { - var baseTexture = BaseTexture.fromCanvas(canvas, scaleMode); + return new Texture(BaseTexture.fromCanvas(canvas, scaleMode)); +}; - return new Texture( baseTexture ); - +/** + * Helper function that creates a new Texture based on the given video element. + * + * @static + * @param video {HTMLVideoElement} + * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values + * @return {Texture} A Texture + */ +Texture.fromVideo = function (video, scaleMode) { + return new Texture(VideoBaseTexture.baseTextureFromVideo(video, scaleMode)); }; /** diff --git a/src/core/textures/VideoTexture.js b/src/core/textures/VideoTexture.js deleted file mode 100644 index f61f377..0000000 --- a/src/core/textures/VideoTexture.js +++ /dev/null @@ -1,150 +0,0 @@ -var BaseTexture = require('./BaseTexture'), - Texture = require('./Texture'), - utils = require('../utils'); - -/** - * A texture of a [playing] Video. - * - * See the ["deus" demo](http://www.goodboydigital.com/pixijs/examples/deus/). - * - * @class - * @extends BaseTexture - * @namespace PIXI - * @param source {HTMLVideoElement} - * @param [scaleMode] {number} See {@link scaleModes} for possible values - */ -function VideoTexture(source, scaleMode) { - if (!source){ - throw new Error('No video source element specified.'); - } - - // hook in here to check if video is already available. - // BaseTexture looks for a source.complete boolean, plus width & height. - - if ((source.readyState === source.HAVE_ENOUGH_DATA || source.readyState === source.HAVE_FUTURE_DATA) && source.width && source.height) { - source.complete = true; - } - - BaseTexture.call(this, source, scaleMode); - - this.autoUpdate = false; - this.updateBound = this._onUpdate.bind(this); - - if (!source.complete) { - this._onCanPlay = this.onCanPlay.bind(this); - - source.addEventListener('canplay', this._onCanPlay); - source.addEventListener('canplaythrough', this._onCanPlay); - - // started playing.. - source.addEventListener('play', this.onPlayStart.bind(this)); - source.addEventListener('pause', this.onPlayStop.bind(this)); - } -} - -VideoTexture.prototype = Object.create(BaseTexture.prototype); -VideoTexture.prototype.constructor = VideoTexture; -module.exports = VideoTexture; - -VideoTexture.prototype._onUpdate = function () { - if (this.autoUpdate) { - window.requestAnimationFrame(this.updateBound); - this.dirty(); - } -}; - -VideoTexture.prototype.onPlayStart = function () { - if (!this.autoUpdate) { - window.requestAnimationFrame(this.updateBound); - this.autoUpdate = true; - } -}; - -VideoTexture.prototype.onPlayStop = function () { - this.autoUpdate = false; -}; - -VideoTexture.prototype.onCanPlay = function () { - if (event.type === 'canplaythrough') { - this.hasLoaded = true; - - - if (this.source) { - this.source.removeEventListener('canplay', this._onCanPlay); - this.source.removeEventListener('canplaythrough', this._onCanPlay); - - this.width = this.source.videoWidth; - this.height = this.source.videoHeight; - - // prevent multiple loaded dispatches.. - if (!this.__loaded){ - this.__loaded = true; - this.dispatchEvent({ type: 'loaded', content: this }); - } - } - } -}; - -VideoTexture.prototype.destroy = function () { - if (this.source && this.source._pixiId) { - utils.BaseTextureCache[ this.source._pixiId ] = null; - delete utils.BaseTextureCache[ this.source._pixiId ]; - - this.source._pixiId = null; - delete this.source._pixiId; - } - - BaseTexture.prototype.destroy.call(this); -}; - -/** - * Mimic Pixi BaseTexture.from.... method. - * - * @static - * @param video {HTMLVideoElement} - * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return {VideoTexture} - */ -VideoTexture.baseTextureFromVideo = function (video, scaleMode) { - if (!video._pixiId) { - video._pixiId = 'video_' + utils.TextureCacheIdGenerator++; - } - - var baseTexture = utils.BaseTextureCache[ video._pixiId ]; - - if (!baseTexture) { - baseTexture = new VideoTexture(video, scaleMode); - utils.BaseTextureCache[ video._pixiId ] = baseTexture; - } - - return baseTexture; -}; - -/** - * Mimic Pixi BaseTexture.from.... method. - * - * @static - * @param video {HTMLVideoElement} - * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return {Texture} A Texture, but not a VideoTexture. - */ -VideoTexture.textureFromVideo = function (video, scaleMode) { - var baseTexture = VideoTexture.baseTextureFromVideo(video, scaleMode); - return new Texture(baseTexture); -}; - -/** - * Mimic Pixi BaseTexture.from.... method. - * - * @static - * @param videoSrc {string} The URL for the video. - * @param scaleMode {number} See {{#crossLink "PIXI/scaleModes:property"}}scaleModes{{/crossLink}} for possible values - * @return {VideoTexture} - */ -VideoTexture.fromUrl = function (videoSrc, scaleMode) { - var video = document.createElement('video'); - video.src = videoSrc; - video.autoPlay = true; - video.play(); - return VideoTexture.textureFromVideo(video, scaleMode); -}; diff --git a/src/core/utils/EventTarget.js b/src/core/utils/EventTarget.js index 9c374b5..c8e59fe 100644 --- a/src/core/utils/EventTarget.js +++ b/src/core/utils/EventTarget.js @@ -8,12 +8,12 @@ * @example * function MyEmitter() {} * - * EventTarget.mixin(MyEmitter.prototype); + * eventTarget.mixin(MyEmitter.prototype); * * var em = new MyEmitter(); * em.emit('eventName', 'some data', 'some more data', {}, null, ...); */ -function EventTarget(obj) { +function eventTarget(obj) { /** * Return a list of assigned event listeners. * @@ -162,11 +162,11 @@ module.exports = { /** - * Mixes in the properties of the EventTarget prototype onto another object + * Mixes in the properties of the eventTarget prototype onto another object * * @param object {object} The obj to mix into */ mixin: function mixin(obj) { - EventTarget(obj); + eventTarget(obj); } }; diff --git a/src/core/utils/index.js b/src/core/utils/index.js index 0ff62e1..64f80cb 100644 --- a/src/core/utils/index.js +++ b/src/core/utils/index.js @@ -5,6 +5,7 @@ */ var utils = module.exports = { _uid: 0, + _saidHello: false, /** * Gets the next uuid @@ -104,13 +105,19 @@ /** * Logs out the version and renderer information for this running instance of PIXI. - * If you don't want to see this message you can set PIXI.utils.sayHello = false; + * If you don't want to see this message you can set `PIXI.utils._saidHello = true;` + * so the library thinks it already said it. Keep in mind that doing that will forever + * makes you a jerk face. * * @param {string} type - The string renderer type to log. * @constant * @static */ sayHello: function (type) { + if (utils._saidHello) { + return; + } + if (navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { var args = [ '%c %c %c Pixi.js ' + CONST.VERSION + ' - ' + type + ' %c ' + ' %c ' + ' http://www.pixijs.com/ %c %c ♥%c♥%c♥ ', @@ -131,7 +138,7 @@ console.log('Pixi.js ' + CONST.VERSION + ' - http://www.pixijs.com/'); //jshint ignore:line } - utils.sayHello = false; + utils._saidHello = true; }, /** @@ -166,13 +173,11 @@ PolyK: require('./PolyK'), EventData: require('./EventData'), - EventTarget: require('./EventTarget'), + eventTarget: require('./eventTarget'), // TODO: refactor out this AnimCache: {}, FrameCache: {}, TextureCache: {}, - TextureCacheIdGenerator: 0, - BaseTextureCache: {}, - BaseTextureCacheIdGenerator: 0 + BaseTextureCache: {} };