@@ -429,7 +429,7 @@ this.createjs = this.createjs||{};
429429 * @type {Number }
430430 * @default 0
431431 */
432- this . _lastTrackedCanvas = 0 ;
432+ this . _lastTrackedCanvas = - 1 ;
433433
434434 /**
435435 * Controls whether final rendering output of a {{#crossLink "cacheDraw"}}{{/crossLink}} is the canvas or a render
@@ -1100,9 +1100,10 @@ this.createjs = this.createjs||{};
11001100 * Textures in use, or to be used again shortly, should not be removed. This is simply for performance reasons.
11011101 * Removing a texture in use will cause the texture to have to be re-uploaded slowing rendering.
11021102 * @method releaseTexture
1103- * @param {DisplayObject | Texture | Image | Canvas } item An object that used the texture to be discarded.
1103+ * @param {DisplayObject | Texture | Image | Canvas } item An object that used the texture to be discarded.
1104+ * @param {Boolean } safe Should the release attempt to be "safe" and only delete this usage.
11041105 */
1105- p . releaseTexture = function ( item ) {
1106+ p . releaseTexture = function ( item , safe ) {
11061107 var i , l ;
11071108 if ( ! item ) { return ; }
11081109
@@ -1149,31 +1150,47 @@ this.createjs = this.createjs||{};
11491150 }
11501151
11511152 // remove it
1152- this . _killTextureObject ( this . _textureDictionary [ foundImage . _storeID ] ) ;
1153- foundImage . _storeID = undefined ;
1153+ var texture = this . _textureDictionary [ foundImage . _storeID ] ;
1154+ if ( safe ) {
1155+ var data = texture . _imageData ;
1156+ var index = data . indexOf ( foundImage ) ;
1157+ if ( index >= 0 ) { data . splice ( index , 1 ) ; }
1158+ foundImage . _storeID = undefined ;
1159+ if ( data . length === 0 ) { this . _killTextureObject ( texture ) ; }
1160+ } else {
1161+ this . _killTextureObject ( texture ) ;
1162+ }
11541163 } ;
11551164
11561165 /**
11571166 * Similar to {{#crossLink "releaseTexture"}}{{/crossLink}}, but this function differs by searching for textures to
11581167 * release. It works by assuming that it can purge any texture which was last used more than "count" draw calls ago.
1159- * Because this process is unaware of the objects and whether they may be used on your stage, false positives can
1168+ * Because this process is unaware of the objects and whether they may be used later on your stage, false positives can
11601169 * occur. It is recommended to manually manage your memory with {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}},
11611170 * however, there are many use cases where this is simpler and error-free. This process is also run by default under
11621171 * the hood to prevent leaks. To disable it see the {{#crossLink "StageGL/autoPurge:property"}}{{/crossLink}} property.
11631172 * @method purgeTextures
11641173 * @param {Number } [count=100] How many renders ago the texture was last used
11651174 */
11661175 p . purgeTextures = function ( count ) {
1167- if ( count == undefined ) { count = 100 ; }
1176+ if ( ! ( count >= 0 ) ) { count = 100 ; }
11681177
11691178 var dict = this . _textureDictionary ;
11701179 var l = dict . length ;
1171- for ( var i = 0 ; i < l ; i ++ ) {
1172- var item = dict [ i ] ;
1173- if ( ! item ) { continue ; }
1174- if ( item . _drawID + count <= this . _drawID ) { // use draw not batch as draw is more indicative of time
1175- this . _killTextureObject ( item ) ;
1180+ for ( var i = 0 ; i < l ; i ++ ) {
1181+ var data , texture = dict [ i ] ;
1182+ if ( ! texture || ! ( data = texture . _imageData ) ) { continue ; }
1183+
1184+ for ( var j = 0 ; j < data . length ; j ++ ) {
1185+ var item = data [ j ] ;
1186+ if ( item . _drawID + count <= this . _drawID ) {
1187+ item . _storeID = undefined ;
1188+ data . splice ( j , 1 ) ;
1189+ j -- ;
1190+ }
11761191 }
1192+
1193+ if ( ! data . length ) { this . _killTextureObject ( texture ) ; }
11771194 }
11781195 } ;
11791196
@@ -1450,6 +1467,27 @@ this.createjs = this.createjs||{};
14501467 } ;
14511468
14521469// private methods:
1470+ /**
1471+ * Returns a base texture that has no image or data loaded. Not intended for loading images. In some error cases,
1472+ * the texture creation will fail. This function differs from {{#crossLink "StageGL/getBaseTexture"}}{{/crossLink}}
1473+ * in that the failed textures will be replaced with a safe to render "nothing" texture.
1474+ * @method _getSafeTexture
1475+ * @param {uint } [w=1] The width of the texture in pixels, defaults to 1
1476+ * @param {uint } [h=1] The height of the texture in pixels, defaults to 1
1477+ */
1478+ p . _getSafeTexture = function ( w , h ) {
1479+ var texture = this . getBaseTexture ( w , h ) ;
1480+
1481+ if ( ! texture ) {
1482+ msg = "Problem creating texture, possible cause: using too much VRAM, please try releasing texture memory" ;
1483+ ( console . error && console . error ( msg ) ) || console . log ( msg ) ;
1484+
1485+ texture = this . _baseTextures [ 0 ] ;
1486+ }
1487+
1488+ return texture ;
1489+ } ;
1490+
14531491 /**
14541492 * Sets up and returns the WebGL context for the canvas. May return undefined in error scenarios. These can include
14551493 * situations where the canvas element already has a context, 2D or GL.
@@ -1727,10 +1765,12 @@ this.createjs = this.createjs||{};
17271765
17281766 // fill in blanks as it helps the renderer be stable while textures are loading and reduces need for safety code
17291767 for ( var i = 0 ; i < this . _batchTextureCount ; i ++ ) {
1730- var tex = this . getBaseTexture ( ) ;
1731- this . _baseTextures [ i ] = this . _batchTextures [ i ] = tex ;
1732- if ( ! tex ) {
1768+ var texture = this . getBaseTexture ( ) ;
1769+ this . _baseTextures [ i ] = this . _batchTextures [ i ] = texture ;
1770+ if ( ! texture ) {
17331771 throw "Problems creating basic textures, known causes include using too much VRAM by not releasing WebGL texture instances" ;
1772+ } else {
1773+ texture . _storeID = - 1 ;
17341774 }
17351775 }
17361776 } ;
@@ -1739,56 +1779,49 @@ this.createjs = this.createjs||{};
17391779 * Load a specific texture, accounting for potential delay, as it might not be preloaded.
17401780 * @method _loadTextureImage
17411781 * @param {WebGLRenderingContext } gl
1742- * @param {Image } image Actual image to be loaded
1782+ * @param {Image | Canvas } image Actual image to be loaded
17431783 * @return {WebGLTexture } The resulting Texture object
17441784 * @protected
17451785 */
17461786 p . _loadTextureImage = function ( gl , image ) {
1747- var src = image . src ;
1748-
1749- if ( ! src ) {
1750- // one time canvas property setup
1751- image . _isCanvas = true ;
1752- src = image . src = "canvas_" + this . _lastTrackedCanvas ++ ;
1787+ var srcPath , texture , msg ;
1788+ if ( image instanceof Image && image . src ) {
1789+ srcPath = image . src ;
1790+ } else if ( image instanceof HTMLCanvasElement ) {
1791+ image . _isCanvas = true ; //canvases are already loaded and assumed unique so note that
1792+ srcPath = "canvas_" + ( ++ this . _lastTrackedCanvas ) ;
1793+ } else {
1794+ msg = "Invalid image provided as source. Please ensure source is a correct DOM element." ;
1795+ ( console . error && console . error ( msg , image ) ) || console . log ( msg , image ) ;
1796+ return ;
17531797 }
17541798
1755- // put the texture into our storage system
1756- var storeID = this . _textureIDs [ src ] ;
1799+ // create the texture lookup and texture
1800+ var storeID = this . _textureIDs [ srcPath ] ;
17571801 if ( storeID === undefined ) {
1758- storeID = this . _textureIDs [ src ] = this . _textureDictionary . length ;
1759- }
1760- if ( this . _textureDictionary [ storeID ] === undefined ) {
1761- this . _textureDictionary [ storeID ] = this . getBaseTexture ( ) ;
1802+ this . _textureIDs [ srcPath ] = storeID = this . _textureDictionary . length ;
1803+ image . _storeID = storeID ;
1804+ image . _invalid = ! image . isCanvas ;
1805+ texture = this . _getSafeTexture ( ) ;
1806+ this . _textureDictionary [ storeID ] = texture ;
1807+ } else {
1808+ image . _storeID = storeID ;
1809+ texture = this . _textureDictionary [ storeID ] ;
17621810 }
17631811
1764- var texture = this . _textureDictionary [ storeID ] ;
1765-
1766- if ( texture ) {
1767- // get texture params all set up
1768- texture . _batchID = this . _batchID ;
1812+ // allow the texture to track its references for cleanup, if it's not an error ref
1813+ if ( texture . _storeID != - 1 ) {
17691814 texture . _storeID = storeID ;
1770- texture . _imageData = image ;
1771- this . _insertTextureInBatch ( gl , texture ) ;
1772-
1773- // get the data into the texture or wait for it to load
1774- image . _storeID = storeID ;
1775- if ( image . complete || image . naturalWidth || image . _isCanvas ) { // is it already loaded
1776- this . _updateTextureImageData ( gl , image ) ;
1777- } else {
1778- image . addEventListener ( "load" , this . _updateTextureImageData . bind ( this , gl , image ) ) ;
1815+ if ( texture . _imageData ) {
1816+ texture . _imageData . push ( image ) ;
1817+ } else {
1818+ texture . _imageData = [ image ] ;
17791819 }
1780- } else {
1781- // we really really should have a texture, try to recover the error by using a saved empty texture so we don't crash
1782- var msg = "Problem creating desired texture, known causes include using too much VRAM by not releasing WebGL texture instances" ;
1783- ( console . error && console . error ( msg ) ) || console . log ( msg ) ;
1784-
1785- texture = this . _baseTextures [ 0 ] ;
1786- texture . _batchID = this . _batchID ;
1787- texture . _storeID = - 1 ;
1788- texture . _imageData = texture ;
1789- this . _insertTextureInBatch ( gl , texture ) ;
17901820 }
17911821
1822+ // insert texture into batch
1823+ this . _insertTextureInBatch ( gl , texture ) ;
1824+
17921825 return texture ;
17931826 } ;
17941827
@@ -1801,6 +1834,11 @@ this.createjs = this.createjs||{};
18011834 * @protected
18021835 */
18031836 p . _updateTextureImageData = function ( gl , image ) {
1837+ // the image isn't loaded and isn't ready to be updated, because we don't set the invalid flag we should try again later
1838+ if ( ! ( image . complete || image . _isCanvas || image . naturalWidth ) ) {
1839+ return ;
1840+ }
1841+
18041842 // the bitwise & is intentional, cheap exponent 2 check
18051843 var isNPOT = ( image . width & image . width - 1 ) || ( image . height & image . height - 1 ) ;
18061844 var texture = this . _textureDictionary [ image . _storeID ] ;
@@ -1831,8 +1869,8 @@ this.createjs = this.createjs||{};
18311869 texture . _h = image . height ;
18321870
18331871 if ( this . vocalDebug ) {
1834- if ( isNPOT ) {
1835- console . warn ( "NPOT(Non Power of Two) Texture: " + image . src ) ;
1872+ if ( isNPOT && this . _antialias ) {
1873+ console . warn ( "NPOT(Non Power of Two) Texture w/ antialias on : " + image . src ) ;
18361874 }
18371875 if ( image . width > gl . MAX_TEXTURE_SIZE || image . height > gl . MAX_TEXTURE_SIZE ) {
18381876 console && console . error ( "Oversized Texture: " + image . width + "x" + image . height + " vs " + gl . MAX_TEXTURE_SIZE + "max" ) ;
@@ -1848,8 +1886,7 @@ this.createjs = this.createjs||{};
18481886 * @protected
18491887 */
18501888 p . _insertTextureInBatch = function ( gl , texture ) {
1851- // if it wasn't used last batch
1852- if ( this . _batchTextures [ texture . _activeIndex ] !== texture ) {
1889+ if ( this . _batchTextures [ texture . _activeIndex ] !== texture ) { // if it wasn't used last batch
18531890 // we've got to find it a a spot.
18541891 var found = - 1 ;
18551892 var start = ( this . _lastTextureInsert + 1 ) % this . _batchTextureCount ;
@@ -1867,24 +1904,25 @@ this.createjs = this.createjs||{};
18671904 this . batchReason = "textureOverflow" ;
18681905 this . _drawBuffers ( gl ) ; // <------------------------------------------------------------------------
18691906 this . batchCardCount = 0 ;
1870- found = start ;
1907+ found = start ; //TODO: how do we optimize this to be smarter?
18711908 }
18721909
18731910 // lets put it into that spot
18741911 this . _batchTextures [ found ] = texture ;
18751912 texture . _activeIndex = found ;
1876- var image = texture . _imageData ;
1877- if ( image && image . _invalid && texture . _drawID !== undefined ) {
1913+ var image = texture . _imageData && texture . _imageData [ 0 ] ; // first come first served, potentially problematic
1914+ if ( image && image . _invalid ) {
18781915 this . _updateTextureImageData ( gl , image ) ;
18791916 } else {
18801917 gl . activeTexture ( gl . TEXTURE0 + found ) ;
18811918 gl . bindTexture ( gl . TEXTURE_2D , texture ) ;
18821919 this . setTextureParams ( gl ) ;
18831920 }
18841921 this . _lastTextureInsert = found ;
1885- } else {
1886- var image = texture . _imageData ;
1887- if ( texture . _storeID != undefined && image && image . _invalid ) {
1922+
1923+ } else if ( texture . _drawID !== this . _drawID ) { // hanging around from previous draws means the content might be out of date
1924+ var image = texture . _imageData && texture . _imageData [ 0 ] ;
1925+ if ( image && image . _invalid ) {
18881926 this . _updateTextureImageData ( gl , image ) ;
18891927 }
18901928 }
@@ -1898,40 +1936,41 @@ this.createjs = this.createjs||{};
18981936 * {{#crossLink "StageGL/releaseTexture"}}{{/crossLink}} instead as it will call this with the correct texture object(s).
18991937 * Note: Testing shows this may not happen immediately, have to wait frames for WebGL to have actually adjust memory.
19001938 * @method _killTextureObject
1901- * @param {Texture } tex The texture to be cleaned out
1939+ * @param {Texture } texture The texture to be cleaned out
19021940 * @protected
19031941 */
1904- p . _killTextureObject = function ( tex ) {
1905- if ( ! tex ) { return ; }
1942+ p . _killTextureObject = function ( texture ) {
1943+ if ( ! texture ) { return ; }
19061944 var gl = this . _webGLContext ;
19071945
19081946 // remove linkage
1909- if ( tex . _storeID !== undefined && tex . _storeID >= 0 ) {
1910- this . _textureDictionary [ tex . _storeID ] = undefined ;
1947+ if ( texture . _storeID !== undefined && texture . _storeID >= 0 ) {
1948+ this . _textureDictionary [ texture . _storeID ] = undefined ;
19111949 for ( var n in this . _textureIDs ) {
1912- if ( this . _textureIDs [ n ] == tex . _storeID ) { delete this . _textureIDs [ n ] ; }
1950+ if ( this . _textureIDs [ n ] == texture . _storeID ) { delete this . _textureIDs [ n ] ; }
19131951 }
1914- if ( tex . _imageData ) { tex . _imageData . _storeID = undefined ; }
1915- tex . _imageData = tex . _storeID = undefined ;
1952+ var data = texture . _imageData ;
1953+ for ( var i = data . length - 1 ; i >= 0 ; i -- ) { data [ i ] . _storeID = undefined ; }
1954+ texture . _imageData = texture . _storeID = undefined ;
19161955 }
19171956
19181957 // make sure to drop it out of an active slot
1919- if ( tex . _activeIndex !== undefined && this . _batchTextures [ tex . _activeIndex ] === tex ) {
1920- this . _batchTextures [ tex . _activeIndex ] = this . _baseTextures [ tex . _activeIndex ] ;
1958+ if ( texture . _activeIndex !== undefined && this . _batchTextures [ texture . _activeIndex ] === texture ) {
1959+ this . _batchTextures [ texture . _activeIndex ] = this . _baseTextures [ texture . _activeIndex ] ;
19211960 }
19221961
19231962 // remove buffers if present
19241963 try {
1925- if ( tex . _frameBuffer ) { gl . deleteFramebuffer ( tex . _frameBuffer ) ; }
1926- tex . _frameBuffer = undefined ;
1964+ if ( texture . _frameBuffer ) { gl . deleteFramebuffer ( texture . _frameBuffer ) ; }
1965+ texture . _frameBuffer = undefined ;
19271966 } catch ( e ) {
19281967 /* suppress delete errors because it's already gone or didn't need deleting probably */
19291968 if ( this . vocalDebug ) { console . log ( e ) ; }
19301969 }
19311970
19321971 // remove entry
19331972 try {
1934- gl . deleteTexture ( tex ) ;
1973+ gl . deleteTexture ( texture ) ;
19351974 } catch ( e ) {
19361975 /* suppress delete errors because it's already gone or didn't need deleting probably */
19371976 if ( this . vocalDebug ) { console . log ( e ) ; }
@@ -2229,32 +2268,33 @@ this.createjs = this.createjs||{};
22292268 var uvRect , texIndex , image , frame , texture , src ;
22302269 var useCache = item . cacheCanvas && ! ignoreCache ;
22312270
2271+ // get the image data, or abort if not present
22322272 if ( item . _webGLRenderStyle === 2 || useCache ) { // BITMAP / Cached Canvas
22332273 image = ( ignoreCache ?false :item . cacheCanvas ) || item . image ;
2234- } else if ( item . _webGLRenderStyle === 1 ) { // SPRITE
2274+ } else if ( item . _webGLRenderStyle === 1 ) { // SPRITE
22352275 frame = item . spriteSheet . getFrame ( item . currentFrame ) ; //TODO: Faster way?
22362276 if ( frame === null ) { continue ; }
22372277 image = frame . image ;
2238- } else { // MISC (DOM objects render themselves later)
2278+ } else { // MISC (DOM objects render themselves later)
22392279 continue ;
22402280 }
2281+ if ( ! image ) { continue ; }
22412282
22422283 var uvs = this . _uvs ;
22432284 var vertices = this . _vertices ;
22442285 var texI = this . _indices ;
22452286 var alphas = this . _alphas ;
22462287
22472288 // calculate texture
2248- if ( ! image ) { continue ; }
22492289 if ( image . _storeID === undefined ) {
22502290 // this texture is new to us so load it and add it to the batch
22512291 texture = this . _loadTextureImage ( gl , image ) ;
2252- this . _insertTextureInBatch ( gl , texture ) ;
22532292 } else {
22542293 // fetch the texture (render textures know how to look themselves up to simplify this logic)
22552294 texture = this . _textureDictionary [ image . _storeID ] ;
2256- if ( ! texture ) {
2257- if ( this . vocalDebug ) { console . log ( "Texture should not be looked up while not being stored." ) ; }
2295+
2296+ if ( ! texture ) { //TODO: this should really not occur but has due to bugs, hopefully this can be removed eventually
2297+ if ( this . vocalDebug ) { console . log ( "Image source should not be lookup a non existent texture, please report a bug." ) ; }
22582298 continue ;
22592299 }
22602300
@@ -2264,6 +2304,7 @@ this.createjs = this.createjs||{};
22642304 }
22652305 }
22662306 texIndex = texture . _activeIndex ;
2307+ image . _drawID = this . _drawID ;
22672308
22682309 if ( item . _webGLRenderStyle === 2 || useCache ) { // BITMAP / Cached Canvas
22692310 if ( ! useCache && item . sourceRect ) {
0 commit comments