chromium/ash/frame_sink/frame_sink_holder.h

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_FRAME_SINK_FRAME_SINK_HOLDER_H_
#define ASH_FRAME_SINK_FRAME_SINK_HOLDER_H_

#include <cstdint>
#include <memory>
#include <vector>

#include "ash/ash_export.h"
#include "ash/frame_sink/frame_sink_host.h"
#include "ash/frame_sink/ui_resource_manager.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_observation.h"
#include "cc/scheduler/scheduler.h"
#include "cc/trees/layer_tree_frame_sink_client.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "ui/aura/window_observer.h"

namespace cc {
class LayerTreeFrameSink;
}  // namespace cc

namespace ash {

class FrameSinkHolderTestApi;

// Holds the LayerTreeFrameSink and manages all the interactions with the
// LayerTreeFrameSink. It provides an API to submit compositor frames either
// synchronously or asynchronously. We have this holder class so that, if
// needed, we can make LayerTreeFrameSink outlive the frame_sink_host in order
// to reclaim any exported resources to display compositor. Note: The class is
// intended to be used by the FrameSinkHost class.
class ASH_EXPORT FrameSinkHolder final : public cc::LayerTreeFrameSinkClient,
                                         public viz::BeginFrameObserverBase,
                                         public aura::WindowObserver {
 public:
  using PresentationCallback =
      base::RepeatingCallback<void(const gfx::PresentationFeedback&)>;

  // Refer to declaration of `FrameSinkHost::CreateCompositorFrame` for a
  // detailed comment.
  using GetCompositorFrameCallback =
      base::RepeatingCallback<std::unique_ptr<viz::CompositorFrame>(
          const viz::BeginFrameAck& begin_frame_ack,
          UiResourceManager& resource_manager,
          bool auto_update,
          const gfx::Size& last_submitted_frame_size,
          float last_submitted_frame_dsf)>;
  // Refer to declaration of `FrameSinkHost::OnFirstFrameRequested` for a
  // detailed comment.
  using OnFirstFrameRequestedCallback = base::RepeatingCallback<void()>;

  FrameSinkHolder(
      std::unique_ptr<cc::LayerTreeFrameSink> frame_sink,
      GetCompositorFrameCallback get_compositor_frame_callback,
      OnFirstFrameRequestedCallback on_first_frame_requested_callback);

  FrameSinkHolder(const FrameSinkHolder&) = delete;
  FrameSinkHolder& operator=(const FrameSinkHolder&) = delete;

  ~FrameSinkHolder() override;

  // Delete `frame_sink_holder` after having reclaimed all exported resources.
  // Returns true if the holder will be deleted immediately.
  // TODO(reveman): Find a better way to handle deletion of in-flight resources.
  // https://crbug.com/765763
  static bool DeleteWhenLastResourceHasBeenReclaimed(
      std::unique_ptr<FrameSinkHolder> frame_sink_holder,
      aura::Window* host_window);

  void set_presentation_callback(PresentationCallback callback) {
    presentation_callback_ = std::move(callback);
  }

  base::WeakPtr<FrameSinkHolder> GetWeakPtr() {
    return weak_ptr_factory_.GetWeakPtr();
  }

  // When auto-update mode is on, we keep on submitting frames asynchronously to
  // display compositor without a request to submit a frame via
  // `SubmitCompositorFrame()`.
  void SetAutoUpdateMode(bool mode);

  UiResourceManager& resource_manager() { return resources_manager_; }

  // Submits a single compositor frame to display compositor. Auto-submit
  // mode must be off to use this method. If synchronous_draw is true, we try to
  // submit frame to display compositor right away. Otherwise we will submit the
  // frame next time display compositor requests a new frame.
  // Note: In certain cases when we cannot submit frames right away, synchronous
  // requests will be changed to asynchronous requests.
  void SubmitCompositorFrame(bool synchronous_draw);

  // Overridden from cc::LayerTreeFrameSinkClient:
  void SetBeginFrameSource(viz::BeginFrameSource* source) override;
  std::optional<viz::HitTestRegionList> BuildHitTestData() override;
  void ReclaimResources(std::vector<viz::ReturnedResource> resources) override;
  void SetTreeActivationCallback(base::RepeatingClosure callback) override;
  void DidReceiveCompositorFrameAck() override;
  void DidPresentCompositorFrame(
      uint32_t frame_token,
      const viz::FrameTimingDetails& details) override;
  void DidLoseLayerTreeFrameSink() override;
  void OnDraw(const gfx::Transform& transform,
              const gfx::Rect& viewport,
              bool resourceless_software_draw,
              bool skip_draw) override;
  void SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) override;
  void SetExternalTilePriorityConstraints(
      const gfx::Rect& viewport_rect,
      const gfx::Transform& transform) override;

  // Overridden from viz::BeginFrameObserverBase:
  void OnBeginFrameSourcePausedChanged(bool paused) override;
  bool OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) override;

  // Overridden from aura::WindowObserver
  void OnWindowDestroying(aura::Window* window) override;

 private:
  friend class FrameSinkHolderTestApi;

  void ObserveBeginFrameSource(bool start);

  // If we have not consecutively produced a frame in response to OnBeginFrame
  // events from the compositor, we can stop observing the
  // `begin_frame_source_`. This is because continuous polling from the
  // compositor and receiving DidNotProduceFrame responses from the client is
  // unnecessary work and can cause power regression.
  void MaybeStopObservingBeingFrameSource();

  void DidNotProduceFrame(viz::BeginFrameAck&& begin_frame_ack,
                          cc::FrameSkippedReason reason);

  // Create an empty frame that has dsf and size of the last submitted frame.
  viz::CompositorFrame CreateEmptyFrame();

  void SubmitCompositorFrameInternal(
      std::unique_ptr<viz::CompositorFrame> frame);

  void ScheduleDelete();

  // Returns true if we are waiting to reclaim all the exported resources after
  // which we schedule a delete task for the holder.
  bool WaitingToScheduleDelete() const;

  // Extend the lifetime of `this` by adding it as a observer to `root_window`.
  void SetRootWindowForDeletion(aura::Window* root_window);

  // True when the display compositor has already asked for a compositor
  // frame. This signifies that the gpu process has been fully initialized.
  bool first_frame_requested_ = false;

  // The layer tree frame sink created from `host_window_.
  std::unique_ptr<cc::LayerTreeFrameSink> frame_sink_;

  // The currently observed `BeginFrameSource` which will notify us with
  // `OnBeginFrameDerivedImpl()`.
  raw_ptr<viz::BeginFrameSource> begin_frame_source_ = nullptr;

  // True if we submitted a compositor frame and are waiting for a call to
  // `DidReceiveCompositorFrameAck()`.
  bool pending_compositor_frame_ack_ = false;

  // True if we asynchronously need to submit a compositor frame i.e submit a
  // frame next time display compositor requests for a new frame via
  // `OnBeginFrameDerivedImpl`.
  bool pending_compositor_frame_ = false;

  // The pixel size and the DSF of the most recently submitted compositor frame.
  // If either changes, we'll need to allocate a new local surface ID.
  gfx::Size last_frame_size_in_pixels_;
  float last_frame_device_scale_factor_ = 1.0f;

  // Keeps track of resources that are currently available to be reused in a
  // compositor frame and the resources that are in-use by the display
  // compositor.
  UiResourceManager resources_manager_;

  // Generates a frame token for the next compositor frame we create.
  viz::FrameTokenGenerator compositor_frame_token_generator_;

  // True if `this` is scheduled to be deleted.
  bool delete_pending_ = false;

  // When true, continuously submit frames asynchronously in the background.
  bool auto_update_ = false;

  // The callback to notify the client when surface contents have been
  // presented.
  PresentationCallback presentation_callback_;

  // The callback to generate the next compositor frame.
  GetCompositorFrameCallback get_compositor_frame_callback_;

  // The callback invoked when the display compositor asks for a compositor
  // frame for the first time.
  OnFirstFrameRequestedCallback on_first_frame_requested_callback_;

  // Observation of the root window to which this holder becomes an observer to
  // extend its lifespan till all the in-flight resource to display compositor
  // are reclaimed.
  base::ScopedObservation<aura::Window, aura::WindowObserver>
      root_window_observation_{this};
  base::ScopedObservation<viz::BeginFrameSource, viz::BeginFrameObserver>
      begin_frame_observation_{this};

  // The number of DidNotProduceFrame responses since the last time when a frame
  // is submitted.
  int consecutive_begin_frames_produced_no_frame_count_ = 0;

  base::WeakPtrFactory<FrameSinkHolder> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // ASH_FRAME_SINK_FRAME_SINK_HOLDER_H_