@@ -3,6 +3,7 @@ var THREE = require('../lib/three');
33var utils = require ( '../utils/' ) ;
44var isHLS = require ( '../utils/material' ) . isHLS ;
55
6+ var bind = utils . bind ;
67var debug = utils . debug ;
78var error = debug ( 'components:texture:error' ) ;
89var TextureLoader = new THREE . TextureLoader ( ) ;
@@ -14,15 +15,24 @@ TextureLoader.setCrossOrigin('anonymous');
1415 * System for material component.
1516 * Handle material registration, updates (for fog), and texture caching.
1617 *
17- * @member materials {object} - Registered materials.
18- * @member textureCache {object} - Texture cache for:
18+ * @member {object} materials - Registered materials.
19+ * @member {object} textureCounts - Number of times each texture is used. Tracked
20+ * separately from textureCache, because the cache (1) is populated in
21+ * multiple places, and (2) may be cleared at any time.
22+ * @member {object} textureCache - Texture cache for:
1923 * - Images: textureCache has mapping of src -> repeat -> cached three.js texture.
2024 * - Videos: textureCache has mapping of videoElement -> cached three.js texture.
2125 */
2226module . exports . System = registerSystem ( 'material' , {
2327 init : function ( ) {
2428 this . materials = { } ;
29+ this . textureCounts = { } ;
2530 this . textureCache = { } ;
31+
32+ this . sceneEl . addEventListener (
33+ 'materialtextureloaded' ,
34+ bind ( this . onMaterialTextureLoaded , this )
35+ ) ;
2636 } ,
2737
2838 clearTextureCache : function ( ) {
@@ -189,12 +199,26 @@ module.exports.System = registerSystem('material', {
189199 } ,
190200
191201 /**
192- * Stop tracking material.
202+ * Stop tracking material, and dispose of any textures not being used by
203+ * another material component.
193204 *
194205 * @param {object } material
195206 */
196207 unregisterMaterial : function ( material ) {
197208 delete this . materials [ material . uuid ] ;
209+
210+ // If any textures on this material are no longer in use, dispose of them.
211+ var textureCounts = this . textureCounts ;
212+ Object . keys ( material )
213+ . filter ( function ( propName ) {
214+ return material [ propName ] && material [ propName ] . isTexture ;
215+ } )
216+ . forEach ( function ( mapName ) {
217+ textureCounts [ material [ mapName ] . uuid ] -- ;
218+ if ( textureCounts [ material [ mapName ] . uuid ] <= 0 ) {
219+ material [ mapName ] . dispose ( ) ;
220+ }
221+ } ) ;
198222 } ,
199223
200224 /**
@@ -205,6 +229,21 @@ module.exports.System = registerSystem('material', {
205229 Object . keys ( materials ) . forEach ( function ( uuid ) {
206230 materials [ uuid ] . needsUpdate = true ;
207231 } ) ;
232+ } ,
233+
234+ /**
235+ * Track textures used by material components, so that they can be safely
236+ * disposed when no longer in use. Textures must be registered here, and not
237+ * through registerMaterial(), because textures may not be attached at the
238+ * time the material is registered.
239+ *
240+ * @param {Event } e
241+ */
242+ onMaterialTextureLoaded : function ( e ) {
243+ if ( ! this . textureCounts [ e . detail . texture . uuid ] ) {
244+ this . textureCounts [ e . detail . texture . uuid ] = 0 ;
245+ }
246+ this . textureCounts [ e . detail . texture . uuid ] ++ ;
208247 }
209248} ) ;
210249
0 commit comments