[CodeHealth] Fix dangling CompositorRenderPass* in FixedPassData

We now make sure the CompositorRenderPass* is only valid during the
aggregation. The ptr is set at the start of the aggregation and reset
back to null at the end of the aggregation.

Thanks to kylechar@.

Bug: 1512711
Change-Id: I9b541ad5da709dd5e641f489a492446743c55371
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5132533
Reviewed-by: Kyle Charbonneau <kylechar@chromium.org>
Commit-Queue: William Liu <liuwilliam@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1243446}
diff --git a/components/viz/service/display/resolved_frame_data.cc b/components/viz/service/display/resolved_frame_data.cc
index 25312cd..4aed39fa2 100644
--- a/components/viz/service/display/resolved_frame_data.cc
+++ b/components/viz/service/display/resolved_frame_data.cc
@@ -63,11 +63,26 @@
 ResolvedPassData& ResolvedPassData::operator=(ResolvedPassData&& other) =
     default;
 
+const CompositorRenderPass& ResolvedPassData::render_pass() const {
+  CHECK(fixed_.render_pass);
+  return *fixed_.render_pass;
+}
+
 void ResolvedPassData::CopyAndResetPersistentPassData() {
   previous_persistent_data_ = current_persistent_data_;
   current_persistent_data_ = PersistentPassData();
 }
 
+void ResolvedPassData::SetCompositorRenderPass(CompositorRenderPass* pass) {
+  CHECK(pass);
+  CHECK_EQ(pass->id, fixed_.render_pass_id);
+  fixed_.render_pass = pass;
+}
+
+void ResolvedPassData::ResetCompositorRenderPass() {
+  fixed_.render_pass = nullptr;
+}
+
 ResolvedFrameData::ResolvedFrameData(DisplayResourceProvider* resource_provider,
                                      Surface* surface,
                                      uint64_t previous_frame_index,
@@ -252,6 +267,7 @@
   for (auto& resolved_pass : resolved_passes_) {
     resolved_pass.aggregation().Reset();
     resolved_pass.CopyAndResetPersistentPassData();
+    resolved_pass.ResetCompositorRenderPass();
   }
 
   previous_frame_index_ = frame_index_;
@@ -320,6 +336,21 @@
   return resolved_passes_.back().render_pass().output_rect;
 }
 
+void ResolvedFrameData::SetRenderPassPointers() {
+  const CompositorRenderPassList& render_pass_list =
+      surface_->GetActiveFrame().render_pass_list;
+
+  // `render_pass_list` and `resolved_passes_` should have the same size and
+  // order.
+  CHECK_EQ(render_pass_list.size(), resolved_passes_.size());
+  for (size_t i = 0; i < resolved_passes_.size(); ++i) {
+    ResolvedPassData& resolved_pass = resolved_passes_[i];
+    const auto& render_pass = render_pass_list[i];
+    CHECK_EQ(resolved_pass.render_pass_id(), render_pass->id);
+    resolved_pass.SetCompositorRenderPass(render_pass.get());
+  }
+}
+
 void ResolvedFrameData::RegisterWithResourceProvider() {
   child_resource_id_ = resource_provider_->CreateChild(
       base::BindRepeating(&SurfaceClient::UnrefResources, surface_->client()),
diff --git a/components/viz/service/display/resolved_frame_data.h b/components/viz/service/display/resolved_frame_data.h
index 12cf538..7116c875 100644
--- a/components/viz/service/display/resolved_frame_data.h
+++ b/components/viz/service/display/resolved_frame_data.h
@@ -43,8 +43,14 @@
   FixedPassData& operator=(FixedPassData&& other);
   ~FixedPassData();
 
-  raw_ptr<CompositorRenderPass, AcrossTasksDanglingUntriaged> render_pass =
-      nullptr;
+  // Only valid during aggregation: set at the beginning a new round of
+  // aggregation and reset to null at the end of each aggregation.
+  //
+  // This shouldn't be dangling anymore because CompositorFrames are never
+  // destroyed during aggregation so the pointer will remain valid for the
+  // duration of aggregation (until it's set to null).
+  raw_ptr<CompositorRenderPass> render_pass = nullptr;
+
   // DrawQuads in |render_pass| that can contribute additional damage (eg.
   // surface and render passes) that need to be visited during the prewalk phase
   // of aggregation. Stored in front-to-back order like in |render_pass|.
@@ -147,9 +153,7 @@
   ResolvedPassData(ResolvedPassData&& other);
   ResolvedPassData& operator=(ResolvedPassData&& other);
 
-  const CompositorRenderPass& render_pass() const {
-    return *fixed_.render_pass;
-  }
+  const CompositorRenderPass& render_pass() const;
   AggregatedRenderPassId remapped_id() const { return fixed_.remapped_id; }
   CompositorRenderPassId render_pass_id() const {
     return fixed_.render_pass_id;
@@ -186,6 +190,14 @@
 
   void CopyAndResetPersistentPassData();
 
+  // Set `fixed_.render_pass` to `pass`. Should be called at the beginning of an
+  // aggregation.
+  void SetCompositorRenderPass(CompositorRenderPass* pass);
+
+  // Set `fixed_.render_pass` back to null, to avoid the dangling pointer
+  // after aggregation. Should be called at the end of an aggregation.
+  void ResetCompositorRenderPass();
+
  private:
   friend class ResolvedFrameData;
 
@@ -300,6 +312,10 @@
   // Returns the root render pass output_rect.
   const gfx::Rect& GetOutputRect() const;
 
+  // Set `CompositorRenderPass` for all `resolved_passes_`. Each
+  // `ResolvedPassData` must have been aggregated before.
+  void SetRenderPassPointers();
+
  private:
   void RegisterWithResourceProvider();
   void MovePersistentPassDataFromPreviousFrame(
diff --git a/components/viz/service/display/surface_aggregator.cc b/components/viz/service/display/surface_aggregator.cc
index 4ee8bc5..ca0da76 100644
--- a/components/viz/service/display/surface_aggregator.cc
+++ b/components/viz/service/display/surface_aggregator.cc
@@ -733,13 +733,18 @@
     // Mark the frame as used this aggregation so it persists.
     resolved_frame.MarkAsUsedInAggregation();
 
-    // If there is a new CompositorFrame for `surface` compute resolved frame
-    // data for the new resolved CompositorFrame.
     if (resolved_frame.previous_frame_index() !=
         surface->GetActiveFrameIndex()) {
+      // If there is a new CompositorFrame for `surface` compute resolved frame
+      // data.
       base::ElapsedTimer timer;
       ProcessResolvedFrame(resolved_frame);
       stats_->declare_resources_time += timer.Elapsed();
+    } else if (resolved_frame.is_valid()) {
+      // The same `CompositorFrame` since last aggregation. Set the
+      // `CompositorRenderPass` pointer back to `ResolvedPassData`. Only
+      // applicable to valid `ResolvedFrameData`.
+      resolved_frame.SetRenderPassPointers();
     }
   }