Bug 1642072: Add a red line threshold to the render target GC to prevent memory exhaustion r=gw
authorcbrewster <connorbrewster@yahoo.com>
Wed, 03 Jun 2020 22:53:16 +0000
changeset 533795 6fbc33061f0de92c70e33981f4853d219d9aaf30
parent 533794 e69116203259ea13c0fa8ae07864b9237f650b6a
child 533796 bdb02303566d76b301d0a1477a013c2dc04ed86d
push id37478
push userabutkovits@mozilla.com
push dateThu, 04 Jun 2020 09:29:07 +0000
treeherdermozilla-central@e87e4800d332 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw
bugs1642072
milestone79.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1642072: Add a red line threshold to the render target GC to prevent memory exhaustion r=gw Differential Revision: https://phabricator.services.mozilla.com/D78072
gfx/wr/webrender/src/renderer.rs
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -1094,22 +1094,26 @@ impl TextureResolver {
 
         // GC the render target pool, if it's currently > 32 MB in size.
         //
         // We use a simple scheme whereby we drop any texture that hasn't been used
         // in the last 60 frames, until we are below the size threshold. This should
         // generally prevent any sustained build-up of unused textures, unless we don't
         // generate frames for a long period. This can happen when the window is
         // minimized, and we probably want to flush all the WebRender caches in that case [1].
+        // There is also a second "red line" memory threshold which prevents
+        // memory exhaustion if many render targets are allocated within a small
+        // number of frames. For now this is set at 320 MB (10x the normal memory threshold).
         //
         // [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1494099
         self.gc_targets(
             device,
             frame_id,
             32 * 1024 * 1024,
+            32 * 1024 * 1024 * 10,
             60,
         );
     }
 
     /// Transfers ownership of a render target back to the pool.
     fn return_to_pool(&mut self, device: &mut Device, target: Texture) {
         device.invalidate_render_target(&target);
         self.render_target_pool.push(target);
@@ -1127,16 +1131,17 @@ impl TextureResolver {
     }
 
     /// Drops all targets from the render target pool that do not satisfy the predicate.
     pub fn gc_targets(
         &mut self,
         device: &mut Device,
         current_frame_id: GpuFrameId,
         total_bytes_threshold: usize,
+        total_bytes_red_line_threshold: usize,
         frames_threshold: usize,
     ) {
         // Get the total GPU memory size used by the current render target pool
         let mut rt_pool_size_in_bytes: usize = self.render_target_pool
             .iter()
             .map(|t| t.size_in_bytes())
             .sum();
 
@@ -1152,18 +1157,19 @@ impl TextureResolver {
         // We can't just use retain() because `Texture` requires manual cleanup.
         let mut retained_targets = SmallVec::<[Texture; 8]>::new();
 
         for target in self.render_target_pool.drain(..) {
             // Drop oldest textures until we are under the allowed size threshold.
             // However, if it's been used in very recently, it is always kept around,
             // which ensures we don't thrash texture allocations on pages that do
             // require a very large render target pool and are regularly changing.
-            if rt_pool_size_in_bytes > total_bytes_threshold &&
-               !target.used_recently(current_frame_id, frames_threshold)
+            if (rt_pool_size_in_bytes > total_bytes_red_line_threshold) ||
+               (rt_pool_size_in_bytes > total_bytes_threshold &&
+                !target.used_recently(current_frame_id, frames_threshold))
             {
                 rt_pool_size_in_bytes -= target.size_in_bytes();
                 device.delete_texture(target);
             } else {
                 retained_targets.push(target);
             }
         }