@@ -52,6 +52,7 @@ use record::ApiRecordingReceiver;
52
52
use render_backend:: RenderBackend ;
53
53
use scene_builder:: { SceneBuilder , LowPrioritySceneBuilder } ;
54
54
use shade:: Shaders ;
55
+ use smallvec:: SmallVec ;
55
56
use render_task:: { RenderTask , RenderTaskKind , RenderTaskTree } ;
56
57
use resource_cache:: ResourceCache ;
57
58
use util:: drain_filter;
@@ -786,13 +787,38 @@ impl SourceTextureResolver {
786
787
assert ! ( self . saved_textures. is_empty( ) ) ;
787
788
}
788
789
789
- fn end_frame ( & mut self ) {
790
+ fn end_frame ( & mut self , device : & mut Device , frame_id : FrameId ) {
790
791
// return the cached targets to the pool
791
792
self . end_pass ( None , None ) ;
792
793
// return the global alpha texture
793
794
self . render_target_pool . extend ( self . shared_alpha_texture . take ( ) ) ;
794
795
// return the saved targets as well
795
796
self . render_target_pool . extend ( self . saved_textures . drain ( ..) ) ;
797
+
798
+ // GC the render target pool.
799
+ //
800
+ // We use a simple scheme whereby we drop any texture that hasn't been used
801
+ // in the last 30 frames. This should generally prevent any sustained build-
802
+ // up of unused textures, unless we don't generate frames for a long period.
803
+ // This can happen when the window is minimized, and we probably want to
804
+ // flush all the WebRender caches in that case [1].
805
+ //
806
+ // [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1494099
807
+ self . retain_targets ( device, |texture| texture. used_recently ( frame_id, 30 ) ) ;
808
+ }
809
+
810
+ /// Drops all targets from the render target pool that do not satisfy the predicate.
811
+ pub fn retain_targets < F : Fn ( & Texture ) -> bool > ( & mut self , device : & mut Device , f : F ) {
812
+ // We can't just use retain() because `Texture` requires manual cleanup.
813
+ let mut tmp = SmallVec :: < [ Texture ; 8 ] > :: new ( ) ;
814
+ for target in self . render_target_pool . drain ( ..) {
815
+ if f ( & target) {
816
+ tmp. push ( target) ;
817
+ } else {
818
+ device. delete_texture ( target) ;
819
+ }
820
+ }
821
+ self . render_target_pool . extend ( tmp) ;
796
822
}
797
823
798
824
fn end_pass (
@@ -896,6 +922,21 @@ impl SourceTextureResolver {
896
922
}
897
923
}
898
924
}
925
+
926
+ fn report_memory ( & self ) -> MemoryReport {
927
+ let mut report = MemoryReport :: default ( ) ;
928
+
929
+ // We're reporting GPU memory rather than heap-allocations, so we don't
930
+ // use size_of_op.
931
+ for t in self . cache_texture_map . iter ( ) {
932
+ report. texture_cache_textures += t. size_in_bytes ( ) ;
933
+ }
934
+ for t in self . render_target_pool . iter ( ) {
935
+ report. render_target_textures += t. size_in_bytes ( ) ;
936
+ }
937
+
938
+ report
939
+ }
899
940
}
900
941
901
942
#[ derive( Debug , Copy , Clone , PartialEq ) ]
@@ -1970,17 +2011,27 @@ impl Renderer {
1970
2011
}
1971
2012
ResultMsg :: UpdateResources {
1972
2013
updates,
1973
- cancel_rendering ,
2014
+ memory_pressure ,
1974
2015
} => {
1975
2016
self . pending_texture_updates . push ( updates) ;
1976
2017
self . device . begin_frame ( ) ;
1977
2018
self . update_texture_cache ( ) ;
2019
+
2020
+ // Flush the render target pool on memory pressure.
2021
+ //
2022
+ // This needs to be separate from the block below because
2023
+ // the device module asserts if we delete textures while
2024
+ // not in a frame.
2025
+ if memory_pressure {
2026
+ self . texture_resolver . retain_targets ( & mut self . device , |_| false ) ;
2027
+ }
2028
+
1978
2029
self . device . end_frame ( ) ;
1979
2030
// If we receive a `PublishDocument` message followed by this one
1980
2031
// within the same update we need to cancel the frame because we
1981
2032
// might have deleted the resources in use in the frame due to a
1982
2033
// memory pressure event.
1983
- if cancel_rendering {
2034
+ if memory_pressure {
1984
2035
self . active_documents . clear ( ) ;
1985
2036
}
1986
2037
}
@@ -3869,7 +3920,7 @@ impl Renderer {
3869
3920
) ;
3870
3921
}
3871
3922
3872
- self . texture_resolver . end_frame ( ) ;
3923
+ self . texture_resolver . end_frame ( & mut self . device , frame_id ) ;
3873
3924
3874
3925
#[ cfg( feature = "debug_renderer" ) ]
3875
3926
{
@@ -4176,15 +4227,30 @@ impl Renderer {
4176
4227
/// Collects a memory report.
4177
4228
pub fn report_memory ( & self ) -> MemoryReport {
4178
4229
let mut report = MemoryReport :: default ( ) ;
4230
+
4231
+ // GPU cache CPU memory.
4179
4232
if let CacheBus :: PixelBuffer { ref cpu_blocks, ..} = self . gpu_cache_texture . bus {
4180
4233
report. gpu_cache_cpu_mirror += self . size_of ( cpu_blocks. as_ptr ( ) ) ;
4181
4234
}
4182
4235
4236
+ // GPU cache GPU memory.
4237
+ report. gpu_cache_textures += self . gpu_cache_texture . texture . size_in_bytes ( ) ;
4238
+
4239
+ // Render task CPU memory.
4183
4240
for ( _id, doc) in & self . active_documents {
4184
4241
report. render_tasks += self . size_of ( doc. frame . render_tasks . tasks . as_ptr ( ) ) ;
4185
4242
report. render_tasks += self . size_of ( doc. frame . render_tasks . task_data . as_ptr ( ) ) ;
4186
4243
}
4187
4244
4245
+ // Vertex data GPU memory.
4246
+ report. vertex_data_textures += self . prim_header_f_texture . texture . size_in_bytes ( ) ;
4247
+ report. vertex_data_textures += self . prim_header_i_texture . texture . size_in_bytes ( ) ;
4248
+ report. vertex_data_textures += self . transforms_texture . texture . size_in_bytes ( ) ;
4249
+ report. vertex_data_textures += self . render_task_texture . texture . size_in_bytes ( ) ;
4250
+
4251
+ // Texture cache and render target GPU memory.
4252
+ report += self . texture_resolver . report_memory ( ) ;
4253
+
4188
4254
report
4189
4255
}
4190
4256
0 commit comments