@@ -106,6 +106,12 @@ struct TileCacheImpl {
106106 timestamp : u64 ,
107107 total_memory : usize ,
108108 cache_key : CacheKey ,
109+ texture_cache_resolution : UVec2 ,
110+ /// Pool of textures of the same size: `texture_cache_resolution`.
111+ /// Reusing textures reduces the wgpu allocation pressure,
112+ /// which is a problem on web since we have to wait for
113+ /// the browser to garbage collect unused textures, eating up memory.
114+ texture_cache : Vec < Arc < wgpu:: Texture > > ,
109115}
110116
111117#[ derive( Clone , Default , dyn_any:: DynAny , Debug ) ]
@@ -214,6 +220,36 @@ impl TileCacheImpl {
214220 self . regions . clear ( ) ;
215221 self . total_memory = 0 ;
216222 }
223+
224+ pub fn request_texture ( & mut self , size : UVec2 , device : & wgpu:: Device ) -> Arc < wgpu:: Texture > {
225+ if self . texture_cache_resolution != size {
226+ self . texture_cache_resolution = size;
227+ self . texture_cache . clear ( ) ;
228+ }
229+ self . texture_cache . truncate ( 5 ) ;
230+ for texture in & self . texture_cache {
231+ if Arc :: strong_count ( texture) == 1 {
232+ return Arc :: clone ( texture) ;
233+ }
234+ }
235+ let texture = Arc :: new ( device. create_texture ( & wgpu:: TextureDescriptor {
236+ label : Some ( "viewport_output" ) ,
237+ size : wgpu:: Extent3d {
238+ width : size. x ,
239+ height : size. y ,
240+ depth_or_array_layers : 1 ,
241+ } ,
242+ mip_level_count : 1 ,
243+ sample_count : 1 ,
244+ dimension : wgpu:: TextureDimension :: D2 ,
245+ format : wgpu:: TextureFormat :: Rgba8Unorm ,
246+ usage : wgpu:: TextureUsages :: RENDER_ATTACHMENT | wgpu:: TextureUsages :: COPY_DST | wgpu:: TextureUsages :: COPY_SRC | wgpu:: TextureUsages :: TEXTURE_BINDING ,
247+ view_formats : & [ ] ,
248+ } ) ) ;
249+ self . texture_cache . push ( texture. clone ( ) ) ;
250+
251+ texture
252+ }
217253}
218254
219255impl TileCache {
@@ -224,6 +260,10 @@ impl TileCache {
224260 pub fn store_regions ( & self , regions : Vec < CachedRegion > ) {
225261 self . 0 . lock ( ) . unwrap ( ) . store_regions ( regions) ;
226262 }
263+
264+ pub fn request_texture ( & self , size : UVec2 , device : & wgpu:: Device ) -> Arc < wgpu:: Texture > {
265+ self . 0 . lock ( ) . unwrap ( ) . request_texture ( size, device)
266+ }
227267}
228268
229269fn group_into_regions ( tiles : & [ TileCoord ] , max_region_area : u32 ) -> Vec < RenderRegion > {
@@ -413,7 +453,11 @@ pub async fn render_output_cache<'a: 'n>(
413453 }
414454
415455 let exec = editor_api. application_io . as_ref ( ) . unwrap ( ) . gpu_executor ( ) . unwrap ( ) ;
416- let ( output_texture, combined_metadata) = composite_cached_regions ( & all_regions, physical_resolution, & device_origin_offset, & footprint. transform , exec) ;
456+
457+ let device = & exec. context . device ;
458+ let output_texture = tile_cache. request_texture ( physical_resolution, device) ;
459+
460+ let combined_metadata = composite_cached_regions ( & all_regions, output_texture. as_ref ( ) , & device_origin_offset, & footprint. transform , exec) ;
417461
418462 RenderOutput {
419463 data : RenderOutputType :: Texture ( ImageTexture { texture : output_texture } ) ,
@@ -462,7 +506,7 @@ where
462506 let memory_size = ( region_pixel_size. x * region_pixel_size. y ) as usize * BYTES_PER_PIXEL ;
463507
464508 CachedRegion {
465- texture : rendered_texture. texture ,
509+ texture : rendered_texture. texture . as_ref ( ) . clone ( ) ,
466510 texture_size : region_pixel_size,
467511 tiles : region. tiles . clone ( ) ,
468512 metadata : result. metadata ,
@@ -473,29 +517,14 @@ where
473517
474518fn composite_cached_regions (
475519 regions : & [ CachedRegion ] ,
476- output_resolution : UVec2 ,
520+ output_texture : & wgpu :: Texture ,
477521 device_origin_offset : & DVec2 ,
478522 viewport_transform : & DAffine2 ,
479523 exec : & wgpu_executor:: WgpuExecutor ,
480- ) -> ( wgpu :: Texture , rendering:: RenderMetadata ) {
524+ ) -> rendering:: RenderMetadata {
481525 let device = & exec. context . device ;
482526 let queue = & exec. context . queue ;
483-
484- // TODO: Use texture pool to reuse existing unused textures instead of allocating fresh ones every time
485- let output_texture = device. create_texture ( & wgpu:: TextureDescriptor {
486- label : Some ( "viewport_output" ) ,
487- size : wgpu:: Extent3d {
488- width : output_resolution. x ,
489- height : output_resolution. y ,
490- depth_or_array_layers : 1 ,
491- } ,
492- mip_level_count : 1 ,
493- sample_count : 1 ,
494- dimension : wgpu:: TextureDimension :: D2 ,
495- format : wgpu:: TextureFormat :: Rgba8Unorm ,
496- usage : wgpu:: TextureUsages :: RENDER_ATTACHMENT | wgpu:: TextureUsages :: COPY_DST | wgpu:: TextureUsages :: COPY_SRC | wgpu:: TextureUsages :: TEXTURE_BINDING ,
497- view_formats : & [ ] ,
498- } ) ;
527+ let output_resolution = UVec2 :: new ( output_texture. width ( ) , output_texture. height ( ) ) ;
499528
500529 let mut encoder = device. create_command_encoder ( & wgpu:: CommandEncoderDescriptor { label : Some ( "composite" ) } ) ;
501530 let mut combined_metadata = rendering:: RenderMetadata :: default ( ) ;
@@ -548,5 +577,5 @@ fn composite_cached_regions(
548577 }
549578
550579 queue. submit ( [ encoder. finish ( ) ] ) ;
551- ( output_texture , combined_metadata)
580+ combined_metadata
552581}
0 commit comments