diff --git a/src/core/const.js b/src/core/const.js index c14e9ab..48c0f8a 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -173,6 +173,27 @@ }, /** + * The wrap modes that are supported by pixi. + * + * The DEFAULT Garbage Collection mode for pixi textures is MANUAL + * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. + * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. + * Handy for mobile devices! + * This property only affects WebGL + * @static + * @constant + * @property {object} GC_MODES + * @property {number} GC_MODES.DEFAULT=DEFAULT + * @property {number} GC_MODES.AUTO Garbage collection will happen periodically automatically + * @property {number} GC_MODES.MANUAL Garbage collection will need to be called manually + */ + GC_MODES: { + DEFAULT: 1, + AUTO: 0, + MANUAL: 1, + }, + + /** * If set to true WebGL will attempt make textures mimpaped by default * Mipmapping will only succeed if the base texture uploaded has power of two dimensions * @static diff --git a/src/core/const.js b/src/core/const.js index c14e9ab..48c0f8a 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -173,6 +173,27 @@ }, /** + * The wrap modes that are supported by pixi. + * + * The DEFAULT Garbage Collection mode for pixi textures is MANUAL + * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. + * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. + * Handy for mobile devices! + * This property only affects WebGL + * @static + * @constant + * @property {object} GC_MODES + * @property {number} GC_MODES.DEFAULT=DEFAULT + * @property {number} GC_MODES.AUTO Garbage collection will happen periodically automatically + * @property {number} GC_MODES.MANUAL Garbage collection will need to be called manually + */ + GC_MODES: { + DEFAULT: 1, + AUTO: 0, + MANUAL: 1, + }, + + /** * If set to true WebGL will attempt make textures mimpaped by default * Mipmapping will only succeed if the base texture uploaded has power of two dimensions * @static diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js new file mode 100644 index 0000000..ca90cb0 --- /dev/null +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -0,0 +1,74 @@ + +var CONST = require('../../const'); + +/** + * @class + * @memberof PIXI + * @param renderer {PIXI.WebGLRenderer} The renderer this manager works for. + */ +function TextureGarbageCollector(renderer) +{ + this.renderer = renderer; + + this.count = 0; + this.checkCount = 0; + this.maxIdle = 60 * 60; + this.checkCountMax = 60 * 10; + + this.mode = CONST.GC_MODES.DEFAULT; +} + +TextureGarbageCollector.prototype.constructor = TextureGarbageCollector; +module.exports = TextureGarbageCollector; + +TextureGarbageCollector.prototype.update = function() +{ + this.count++; + if(this.mode === CONST.GC_MODES.MANUAL)return; + + this.checkCount++; + + + if(this.checkCount > this.checkCountMax) + { + this.checkCount = 0; + + this.run(); + } +}; + +TextureGarbageCollector.prototype.run = function() +{ + var tm = this.renderer.textureManager; + var managedTextures = tm._managedTextures; + var wasRemoved = false; + var i,j; + + for (i = 0; i < managedTextures.length; i++) + { + var texture = managedTextures[i]; + + // only supports non generated textures at the moment! + if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) + { + tm.destroyTexture(texture, true); + managedTextures[i] = null; + wasRemoved = true; + } + } + + if (wasRemoved) + { + j = 0; + + for (i = 0; i < managedTextures.length; i++) + { + if (managedTextures[i] !== null) + { + managedTextures[j++] = managedTextures[i]; + } + } + + managedTextures.length = j; + } +}; diff --git a/src/core/const.js b/src/core/const.js index c14e9ab..48c0f8a 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -173,6 +173,27 @@ }, /** + * The wrap modes that are supported by pixi. + * + * The DEFAULT Garbage Collection mode for pixi textures is MANUAL + * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. + * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. + * Handy for mobile devices! + * This property only affects WebGL + * @static + * @constant + * @property {object} GC_MODES + * @property {number} GC_MODES.DEFAULT=DEFAULT + * @property {number} GC_MODES.AUTO Garbage collection will happen periodically automatically + * @property {number} GC_MODES.MANUAL Garbage collection will need to be called manually + */ + GC_MODES: { + DEFAULT: 1, + AUTO: 0, + MANUAL: 1, + }, + + /** * If set to true WebGL will attempt make textures mimpaped by default * Mipmapping will only succeed if the base texture uploaded has power of two dimensions * @static diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js new file mode 100644 index 0000000..ca90cb0 --- /dev/null +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -0,0 +1,74 @@ + +var CONST = require('../../const'); + +/** + * @class + * @memberof PIXI + * @param renderer {PIXI.WebGLRenderer} The renderer this manager works for. + */ +function TextureGarbageCollector(renderer) +{ + this.renderer = renderer; + + this.count = 0; + this.checkCount = 0; + this.maxIdle = 60 * 60; + this.checkCountMax = 60 * 10; + + this.mode = CONST.GC_MODES.DEFAULT; +} + +TextureGarbageCollector.prototype.constructor = TextureGarbageCollector; +module.exports = TextureGarbageCollector; + +TextureGarbageCollector.prototype.update = function() +{ + this.count++; + if(this.mode === CONST.GC_MODES.MANUAL)return; + + this.checkCount++; + + + if(this.checkCount > this.checkCountMax) + { + this.checkCount = 0; + + this.run(); + } +}; + +TextureGarbageCollector.prototype.run = function() +{ + var tm = this.renderer.textureManager; + var managedTextures = tm._managedTextures; + var wasRemoved = false; + var i,j; + + for (i = 0; i < managedTextures.length; i++) + { + var texture = managedTextures[i]; + + // only supports non generated textures at the moment! + if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) + { + tm.destroyTexture(texture, true); + managedTextures[i] = null; + wasRemoved = true; + } + } + + if (wasRemoved) + { + j = 0; + + for (i = 0; i < managedTextures.length; i++) + { + if (managedTextures[i] !== null) + { + managedTextures[j++] = managedTextures[i]; + } + } + + managedTextures.length = j; + } +}; diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 8987c3d..50bcc6b 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -120,8 +120,8 @@ } } else - { - // the textur ealrady exists so we only need to update it.. + { + // the textur ealrady exists so we only need to update it.. if(isRenderTexture) { texture._glRenderTargets[this.renderer.CONTEXT_UID].resize(texture.width, texture.height); diff --git a/src/core/const.js b/src/core/const.js index c14e9ab..48c0f8a 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -173,6 +173,27 @@ }, /** + * The wrap modes that are supported by pixi. + * + * The DEFAULT Garbage Collection mode for pixi textures is MANUAL + * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. + * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. + * Handy for mobile devices! + * This property only affects WebGL + * @static + * @constant + * @property {object} GC_MODES + * @property {number} GC_MODES.DEFAULT=DEFAULT + * @property {number} GC_MODES.AUTO Garbage collection will happen periodically automatically + * @property {number} GC_MODES.MANUAL Garbage collection will need to be called manually + */ + GC_MODES: { + DEFAULT: 1, + AUTO: 0, + MANUAL: 1, + }, + + /** * If set to true WebGL will attempt make textures mimpaped by default * Mipmapping will only succeed if the base texture uploaded has power of two dimensions * @static diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js new file mode 100644 index 0000000..ca90cb0 --- /dev/null +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -0,0 +1,74 @@ + +var CONST = require('../../const'); + +/** + * @class + * @memberof PIXI + * @param renderer {PIXI.WebGLRenderer} The renderer this manager works for. + */ +function TextureGarbageCollector(renderer) +{ + this.renderer = renderer; + + this.count = 0; + this.checkCount = 0; + this.maxIdle = 60 * 60; + this.checkCountMax = 60 * 10; + + this.mode = CONST.GC_MODES.DEFAULT; +} + +TextureGarbageCollector.prototype.constructor = TextureGarbageCollector; +module.exports = TextureGarbageCollector; + +TextureGarbageCollector.prototype.update = function() +{ + this.count++; + if(this.mode === CONST.GC_MODES.MANUAL)return; + + this.checkCount++; + + + if(this.checkCount > this.checkCountMax) + { + this.checkCount = 0; + + this.run(); + } +}; + +TextureGarbageCollector.prototype.run = function() +{ + var tm = this.renderer.textureManager; + var managedTextures = tm._managedTextures; + var wasRemoved = false; + var i,j; + + for (i = 0; i < managedTextures.length; i++) + { + var texture = managedTextures[i]; + + // only supports non generated textures at the moment! + if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) + { + tm.destroyTexture(texture, true); + managedTextures[i] = null; + wasRemoved = true; + } + } + + if (wasRemoved) + { + j = 0; + + for (i = 0; i < managedTextures.length; i++) + { + if (managedTextures[i] !== null) + { + managedTextures[j++] = managedTextures[i]; + } + } + + managedTextures.length = j; + } +}; diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 8987c3d..50bcc6b 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -120,8 +120,8 @@ } } else - { - // the textur ealrady exists so we only need to update it.. + { + // the textur ealrady exists so we only need to update it.. if(isRenderTexture) { texture._glRenderTargets[this.renderer.CONTEXT_UID].resize(texture.width, texture.height); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9a29117..86fc3aa 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -5,6 +5,7 @@ RenderTarget = require('./utils/RenderTarget'), ObjectRenderer = require('./utils/ObjectRenderer'), TextureManager = require('./TextureManager'), + TextureGarbageCollector = require('./TextureGarbageCollector'), WebGLState = require('./WebGLState'), createContext = require('pixi-gl-core').createContext, mapWebGLDrawModesToPixi = require('./utils/mapWebGLDrawModesToPixi'), @@ -153,6 +154,8 @@ this._activeTexture = null; this.setBlendMode(0); + + } // constructor @@ -172,11 +175,13 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); this.rootRenderTarget = new RenderTarget(gl, this.width, this.height, null, this.resolution, true); this.rootRenderTarget.clearColor = this._backgroundColorRgba; + this.bindRenderTarget(this.rootRenderTarget); @@ -242,6 +247,8 @@ this.currentRenderer.flush(); //this.setObjectRenderer(this.emptyRenderer); + this.textureGC.update(); + this.emit('postrender'); }; @@ -442,9 +449,11 @@ { // this will also bind the texture.. this.textureManager.updateTexture(texture); + } else { + texture.touched = this.textureGC.count; // bind the current texture texture._glTextures[this.CONTEXT_UID].bind(); } diff --git a/src/core/const.js b/src/core/const.js index c14e9ab..48c0f8a 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -173,6 +173,27 @@ }, /** + * The wrap modes that are supported by pixi. + * + * The DEFAULT Garbage Collection mode for pixi textures is MANUAL + * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. + * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. + * Handy for mobile devices! + * This property only affects WebGL + * @static + * @constant + * @property {object} GC_MODES + * @property {number} GC_MODES.DEFAULT=DEFAULT + * @property {number} GC_MODES.AUTO Garbage collection will happen periodically automatically + * @property {number} GC_MODES.MANUAL Garbage collection will need to be called manually + */ + GC_MODES: { + DEFAULT: 1, + AUTO: 0, + MANUAL: 1, + }, + + /** * If set to true WebGL will attempt make textures mimpaped by default * Mipmapping will only succeed if the base texture uploaded has power of two dimensions * @static diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js new file mode 100644 index 0000000..ca90cb0 --- /dev/null +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -0,0 +1,74 @@ + +var CONST = require('../../const'); + +/** + * @class + * @memberof PIXI + * @param renderer {PIXI.WebGLRenderer} The renderer this manager works for. + */ +function TextureGarbageCollector(renderer) +{ + this.renderer = renderer; + + this.count = 0; + this.checkCount = 0; + this.maxIdle = 60 * 60; + this.checkCountMax = 60 * 10; + + this.mode = CONST.GC_MODES.DEFAULT; +} + +TextureGarbageCollector.prototype.constructor = TextureGarbageCollector; +module.exports = TextureGarbageCollector; + +TextureGarbageCollector.prototype.update = function() +{ + this.count++; + if(this.mode === CONST.GC_MODES.MANUAL)return; + + this.checkCount++; + + + if(this.checkCount > this.checkCountMax) + { + this.checkCount = 0; + + this.run(); + } +}; + +TextureGarbageCollector.prototype.run = function() +{ + var tm = this.renderer.textureManager; + var managedTextures = tm._managedTextures; + var wasRemoved = false; + var i,j; + + for (i = 0; i < managedTextures.length; i++) + { + var texture = managedTextures[i]; + + // only supports non generated textures at the moment! + if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) + { + tm.destroyTexture(texture, true); + managedTextures[i] = null; + wasRemoved = true; + } + } + + if (wasRemoved) + { + j = 0; + + for (i = 0; i < managedTextures.length; i++) + { + if (managedTextures[i] !== null) + { + managedTextures[j++] = managedTextures[i]; + } + } + + managedTextures.length = j; + } +}; diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 8987c3d..50bcc6b 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -120,8 +120,8 @@ } } else - { - // the textur ealrady exists so we only need to update it.. + { + // the textur ealrady exists so we only need to update it.. if(isRenderTexture) { texture._glRenderTargets[this.renderer.CONTEXT_UID].resize(texture.width, texture.height); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9a29117..86fc3aa 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -5,6 +5,7 @@ RenderTarget = require('./utils/RenderTarget'), ObjectRenderer = require('./utils/ObjectRenderer'), TextureManager = require('./TextureManager'), + TextureGarbageCollector = require('./TextureGarbageCollector'), WebGLState = require('./WebGLState'), createContext = require('pixi-gl-core').createContext, mapWebGLDrawModesToPixi = require('./utils/mapWebGLDrawModesToPixi'), @@ -153,6 +154,8 @@ this._activeTexture = null; this.setBlendMode(0); + + } // constructor @@ -172,11 +175,13 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); this.rootRenderTarget = new RenderTarget(gl, this.width, this.height, null, this.resolution, true); this.rootRenderTarget.clearColor = this._backgroundColorRgba; + this.bindRenderTarget(this.rootRenderTarget); @@ -242,6 +247,8 @@ this.currentRenderer.flush(); //this.setObjectRenderer(this.emptyRenderer); + this.textureGC.update(); + this.emit('postrender'); }; @@ -442,9 +449,11 @@ { // this will also bind the texture.. this.textureManager.updateTexture(texture); + } else { + texture.touched = this.textureGC.count; // bind the current texture texture._glTextures[this.CONTEXT_UID].bind(); } diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 56213c2..c894448 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -18,6 +18,8 @@ EventEmitter.call(this); this.uid = utils.uid(); + + this.touched = 0; /** * The Resolution of the texture. diff --git a/src/core/const.js b/src/core/const.js index c14e9ab..48c0f8a 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -173,6 +173,27 @@ }, /** + * The wrap modes that are supported by pixi. + * + * The DEFAULT Garbage Collection mode for pixi textures is MANUAL + * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. + * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. + * Handy for mobile devices! + * This property only affects WebGL + * @static + * @constant + * @property {object} GC_MODES + * @property {number} GC_MODES.DEFAULT=DEFAULT + * @property {number} GC_MODES.AUTO Garbage collection will happen periodically automatically + * @property {number} GC_MODES.MANUAL Garbage collection will need to be called manually + */ + GC_MODES: { + DEFAULT: 1, + AUTO: 0, + MANUAL: 1, + }, + + /** * If set to true WebGL will attempt make textures mimpaped by default * Mipmapping will only succeed if the base texture uploaded has power of two dimensions * @static diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js new file mode 100644 index 0000000..ca90cb0 --- /dev/null +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -0,0 +1,74 @@ + +var CONST = require('../../const'); + +/** + * @class + * @memberof PIXI + * @param renderer {PIXI.WebGLRenderer} The renderer this manager works for. + */ +function TextureGarbageCollector(renderer) +{ + this.renderer = renderer; + + this.count = 0; + this.checkCount = 0; + this.maxIdle = 60 * 60; + this.checkCountMax = 60 * 10; + + this.mode = CONST.GC_MODES.DEFAULT; +} + +TextureGarbageCollector.prototype.constructor = TextureGarbageCollector; +module.exports = TextureGarbageCollector; + +TextureGarbageCollector.prototype.update = function() +{ + this.count++; + if(this.mode === CONST.GC_MODES.MANUAL)return; + + this.checkCount++; + + + if(this.checkCount > this.checkCountMax) + { + this.checkCount = 0; + + this.run(); + } +}; + +TextureGarbageCollector.prototype.run = function() +{ + var tm = this.renderer.textureManager; + var managedTextures = tm._managedTextures; + var wasRemoved = false; + var i,j; + + for (i = 0; i < managedTextures.length; i++) + { + var texture = managedTextures[i]; + + // only supports non generated textures at the moment! + if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) + { + tm.destroyTexture(texture, true); + managedTextures[i] = null; + wasRemoved = true; + } + } + + if (wasRemoved) + { + j = 0; + + for (i = 0; i < managedTextures.length; i++) + { + if (managedTextures[i] !== null) + { + managedTextures[j++] = managedTextures[i]; + } + } + + managedTextures.length = j; + } +}; diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 8987c3d..50bcc6b 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -120,8 +120,8 @@ } } else - { - // the textur ealrady exists so we only need to update it.. + { + // the textur ealrady exists so we only need to update it.. if(isRenderTexture) { texture._glRenderTargets[this.renderer.CONTEXT_UID].resize(texture.width, texture.height); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9a29117..86fc3aa 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -5,6 +5,7 @@ RenderTarget = require('./utils/RenderTarget'), ObjectRenderer = require('./utils/ObjectRenderer'), TextureManager = require('./TextureManager'), + TextureGarbageCollector = require('./TextureGarbageCollector'), WebGLState = require('./WebGLState'), createContext = require('pixi-gl-core').createContext, mapWebGLDrawModesToPixi = require('./utils/mapWebGLDrawModesToPixi'), @@ -153,6 +154,8 @@ this._activeTexture = null; this.setBlendMode(0); + + } // constructor @@ -172,11 +175,13 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); this.rootRenderTarget = new RenderTarget(gl, this.width, this.height, null, this.resolution, true); this.rootRenderTarget.clearColor = this._backgroundColorRgba; + this.bindRenderTarget(this.rootRenderTarget); @@ -242,6 +247,8 @@ this.currentRenderer.flush(); //this.setObjectRenderer(this.emptyRenderer); + this.textureGC.update(); + this.emit('postrender'); }; @@ -442,9 +449,11 @@ { // this will also bind the texture.. this.textureManager.updateTexture(texture); + } else { + texture.touched = this.textureGC.count; // bind the current texture texture._glTextures[this.CONTEXT_UID].bind(); } diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 56213c2..c894448 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -18,6 +18,8 @@ EventEmitter.call(this); this.uid = utils.uid(); + + this.touched = 0; /** * The Resolution of the texture. diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index f507829..dfd3391 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -45,17 +45,24 @@ */ function RenderTexture(baseRenderTexture, frame) { - if(baseRenderTexture instanceof Number) - { - // we have an old render texture.. - console.warn('v4 RenderTexture now expects a new BaseRenderTexture. Please use RenderTexture.create(400, 400)') + // suport for legacy.. + this.legacyRenderer = null; - var width = baseRenderTexture; - var height = frame; + if( !(baseRenderTexture instanceof BaseRenderTexture) ) + { + var width = arguments[1]; + var height = arguments[2]; + + // we have an old render texture.. + console.warn('v4 RenderTexture now expects a new BaseRenderTexture. Please use RenderTexture.create('+width+', '+height+')'); + this.legacyRenderer = arguments[0]; + + frame = null; + baseRenderTexture = new BaseRenderTexture(width, height, 0, 1); - baseRenderTexture = new BaseRenderTexture(width, height); } + /** * The base texture object that this texture uses * diff --git a/src/core/const.js b/src/core/const.js index c14e9ab..48c0f8a 100644 --- a/src/core/const.js +++ b/src/core/const.js @@ -173,6 +173,27 @@ }, /** + * The wrap modes that are supported by pixi. + * + * The DEFAULT Garbage Collection mode for pixi textures is MANUAL + * If set to DEFAULT, the renderer will occasianally check textures usage. If they are not used for a specified period of time they will be removed from the GPU. + * They will of corse be uploaded again when they are required. This is a silent behind the scenes process that should ensure that the GPU does not get filled up. + * Handy for mobile devices! + * This property only affects WebGL + * @static + * @constant + * @property {object} GC_MODES + * @property {number} GC_MODES.DEFAULT=DEFAULT + * @property {number} GC_MODES.AUTO Garbage collection will happen periodically automatically + * @property {number} GC_MODES.MANUAL Garbage collection will need to be called manually + */ + GC_MODES: { + DEFAULT: 1, + AUTO: 0, + MANUAL: 1, + }, + + /** * If set to true WebGL will attempt make textures mimpaped by default * Mipmapping will only succeed if the base texture uploaded has power of two dimensions * @static diff --git a/src/core/renderers/webgl/TextureGarbageCollector.js b/src/core/renderers/webgl/TextureGarbageCollector.js new file mode 100644 index 0000000..ca90cb0 --- /dev/null +++ b/src/core/renderers/webgl/TextureGarbageCollector.js @@ -0,0 +1,74 @@ + +var CONST = require('../../const'); + +/** + * @class + * @memberof PIXI + * @param renderer {PIXI.WebGLRenderer} The renderer this manager works for. + */ +function TextureGarbageCollector(renderer) +{ + this.renderer = renderer; + + this.count = 0; + this.checkCount = 0; + this.maxIdle = 60 * 60; + this.checkCountMax = 60 * 10; + + this.mode = CONST.GC_MODES.DEFAULT; +} + +TextureGarbageCollector.prototype.constructor = TextureGarbageCollector; +module.exports = TextureGarbageCollector; + +TextureGarbageCollector.prototype.update = function() +{ + this.count++; + if(this.mode === CONST.GC_MODES.MANUAL)return; + + this.checkCount++; + + + if(this.checkCount > this.checkCountMax) + { + this.checkCount = 0; + + this.run(); + } +}; + +TextureGarbageCollector.prototype.run = function() +{ + var tm = this.renderer.textureManager; + var managedTextures = tm._managedTextures; + var wasRemoved = false; + var i,j; + + for (i = 0; i < managedTextures.length; i++) + { + var texture = managedTextures[i]; + + // only supports non generated textures at the moment! + if (!texture._glRenderTargets && this.count - texture.touched > this.maxIdle) + { + tm.destroyTexture(texture, true); + managedTextures[i] = null; + wasRemoved = true; + } + } + + if (wasRemoved) + { + j = 0; + + for (i = 0; i < managedTextures.length; i++) + { + if (managedTextures[i] !== null) + { + managedTextures[j++] = managedTextures[i]; + } + } + + managedTextures.length = j; + } +}; diff --git a/src/core/renderers/webgl/TextureManager.js b/src/core/renderers/webgl/TextureManager.js index 8987c3d..50bcc6b 100644 --- a/src/core/renderers/webgl/TextureManager.js +++ b/src/core/renderers/webgl/TextureManager.js @@ -120,8 +120,8 @@ } } else - { - // the textur ealrady exists so we only need to update it.. + { + // the textur ealrady exists so we only need to update it.. if(isRenderTexture) { texture._glRenderTargets[this.renderer.CONTEXT_UID].resize(texture.width, texture.height); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 9a29117..86fc3aa 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -5,6 +5,7 @@ RenderTarget = require('./utils/RenderTarget'), ObjectRenderer = require('./utils/ObjectRenderer'), TextureManager = require('./TextureManager'), + TextureGarbageCollector = require('./TextureGarbageCollector'), WebGLState = require('./WebGLState'), createContext = require('pixi-gl-core').createContext, mapWebGLDrawModesToPixi = require('./utils/mapWebGLDrawModesToPixi'), @@ -153,6 +154,8 @@ this._activeTexture = null; this.setBlendMode(0); + + } // constructor @@ -172,11 +175,13 @@ // create a texture manager... this.textureManager = new TextureManager(this); + this.textureGC = new TextureGarbageCollector(this); this.state.resetToDefault(); this.rootRenderTarget = new RenderTarget(gl, this.width, this.height, null, this.resolution, true); this.rootRenderTarget.clearColor = this._backgroundColorRgba; + this.bindRenderTarget(this.rootRenderTarget); @@ -242,6 +247,8 @@ this.currentRenderer.flush(); //this.setObjectRenderer(this.emptyRenderer); + this.textureGC.update(); + this.emit('postrender'); }; @@ -442,9 +449,11 @@ { // this will also bind the texture.. this.textureManager.updateTexture(texture); + } else { + texture.touched = this.textureGC.count; // bind the current texture texture._glTextures[this.CONTEXT_UID].bind(); } diff --git a/src/core/textures/BaseTexture.js b/src/core/textures/BaseTexture.js index 56213c2..c894448 100644 --- a/src/core/textures/BaseTexture.js +++ b/src/core/textures/BaseTexture.js @@ -18,6 +18,8 @@ EventEmitter.call(this); this.uid = utils.uid(); + + this.touched = 0; /** * The Resolution of the texture. diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index f507829..dfd3391 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -45,17 +45,24 @@ */ function RenderTexture(baseRenderTexture, frame) { - if(baseRenderTexture instanceof Number) - { - // we have an old render texture.. - console.warn('v4 RenderTexture now expects a new BaseRenderTexture. Please use RenderTexture.create(400, 400)') + // suport for legacy.. + this.legacyRenderer = null; - var width = baseRenderTexture; - var height = frame; + if( !(baseRenderTexture instanceof BaseRenderTexture) ) + { + var width = arguments[1]; + var height = arguments[2]; + + // we have an old render texture.. + console.warn('v4 RenderTexture now expects a new BaseRenderTexture. Please use RenderTexture.create('+width+', '+height+')'); + this.legacyRenderer = arguments[0]; + + frame = null; + baseRenderTexture = new BaseRenderTexture(width, height, 0, 1); - baseRenderTexture = new BaseRenderTexture(width, height); } + /** * The base texture object that this texture uses * diff --git a/src/deprecation.js b/src/deprecation.js index 63185ea..dc818f1 100644 --- a/src/deprecation.js +++ b/src/deprecation.js @@ -241,8 +241,9 @@ return this.generateCanvasTexture(scaleMode, resolution) } -core.RenderTexture.prototype.render = function() +core.RenderTexture.prototype.render = function(displayObject) { + this.legacyRenderer.render(displayObject, this) //displayObject, matrix, clear, updateTransform console.warn('RenderTexture.render is now deprecated, please use renderer.render(displayObject, renderTexture)'); };