123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- // Copyright 2019 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "android_webview/browser/gfx/root_frame_sink.h"
- #include "android_webview/browser/gfx/child_frame.h"
- #include "android_webview/browser/gfx/display_scheduler_webview.h"
- #include "android_webview/browser/gfx/viz_compositor_thread_runner_webview.h"
- #include "base/memory/raw_ptr.h"
- #include "base/trace_event/trace_event.h"
- #include "components/viz/common/features.h"
- #include "components/viz/common/surfaces/frame_sink_id_allocator.h"
- #include "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
- #include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
- #include "components/viz/service/surfaces/surface.h"
- namespace android_webview {
- namespace {
- viz::FrameSinkId AllocateParentSinkId() {
- static viz::FrameSinkIdAllocator allocator(0u);
- return allocator.NextFrameSinkId();
- }
- } // namespace
- class RootFrameSink::ChildCompositorFrameSink
- : public viz::mojom::CompositorFrameSinkClient {
- public:
- ChildCompositorFrameSink(RootFrameSink* owner,
- uint32_t layer_tree_frame_sink_id,
- viz::FrameSinkId frame_sink_id)
- : owner_(owner),
- layer_tree_frame_sink_id_(layer_tree_frame_sink_id),
- frame_sink_id_(frame_sink_id),
- support_(std::make_unique<viz::CompositorFrameSinkSupport>(
- this,
- owner->GetFrameSinkManager(),
- frame_sink_id,
- false)) {
- support_->SetBeginFrameSource(nullptr);
- }
- void DidReceiveCompositorFrameAck(
- std::vector<viz::ReturnedResource> resources) override {
- ReclaimResources(std::move(resources));
- }
- void OnBeginFrame(const viz::BeginFrameArgs& args,
- const viz::FrameTimingDetailsMap& feedbacks) override {}
- void OnBeginFramePausedChanged(bool paused) override {}
- void ReclaimResources(std::vector<viz::ReturnedResource> resources) override {
- owner_->ReturnResources(frame_sink_id_, layer_tree_frame_sink_id_,
- std::move(resources));
- }
- void OnCompositorFrameTransitionDirectiveProcessed(
- uint32_t sequence_id) override {
- owner_->OnCompositorFrameTransitionDirectiveProcessed(
- frame_sink_id_, layer_tree_frame_sink_id_, sequence_id);
- }
- const viz::FrameSinkId frame_sink_id() { return frame_sink_id_; }
- uint32_t layer_tree_frame_sink_id() { return layer_tree_frame_sink_id_; }
- viz::CompositorFrameSinkSupport* support() { return support_.get(); }
- gfx::Size size() { return size_; }
- void SubmitCompositorFrame(
- const viz::LocalSurfaceId& local_surface_id,
- viz::CompositorFrame frame,
- absl::optional<viz::HitTestRegionList> hit_test_region_list) {
- size_ = frame.size_in_pixels();
- support()->SubmitCompositorFrame(local_surface_id, std::move(frame),
- std::move(hit_test_region_list));
- }
- void EvictSurface(viz::SurfaceId surface_id) {
- if (surface_id.frame_sink_id() == frame_sink_id_)
- support_->EvictSurface(surface_id.local_surface_id());
- }
- private:
- const raw_ptr<RootFrameSink> owner_;
- const uint32_t layer_tree_frame_sink_id_;
- const viz::FrameSinkId frame_sink_id_;
- std::unique_ptr<viz::CompositorFrameSinkSupport> support_;
- gfx::Size size_;
- };
- RootFrameSink::RootFrameSink(RootFrameSinkClient* client)
- : root_frame_sink_id_(AllocateParentSinkId()),
- client_(client),
- use_new_invalidate_heuristic_(base::FeatureList::IsEnabled(
- features::kWebViewNewInvalidateHeuristic)) {
- constexpr bool is_root = true;
- GetFrameSinkManager()->RegisterFrameSinkId(root_frame_sink_id_,
- false /* report_activationa */);
- support_ = std::make_unique<viz::CompositorFrameSinkSupport>(
- this, GetFrameSinkManager(), root_frame_sink_id_, is_root);
- begin_frame_source_ = std::make_unique<viz::ExternalBeginFrameSource>(this);
- GetFrameSinkManager()->RegisterBeginFrameSource(begin_frame_source_.get(),
- root_frame_sink_id_);
- // Note, that this technically not part of the new heuristic. Without this
- // line root CF will "request" BeginFrames for delivery of presentation
- // feedback that we don't care about which leads to more begin frame requested
- // than necessary. But to avoid any side effects on invalidation, fixing this
- // is gates under same feature flag.
- if (use_new_invalidate_heuristic_)
- support_->SetBeginFrameSource(nullptr);
- }
- RootFrameSink::~RootFrameSink() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- GetFrameSinkManager()->UnregisterBeginFrameSource(begin_frame_source_.get());
- begin_frame_source_.reset();
- support_.reset();
- GetFrameSinkManager()->InvalidateFrameSinkId(root_frame_sink_id_);
- }
- viz::FrameSinkManagerImpl* RootFrameSink::GetFrameSinkManager() {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // FrameSinkManagerImpl is global and not owned by this class, which is
- // per-AwContents.
- return VizCompositorThreadRunnerWebView::GetInstance()->GetFrameSinkManager();
- }
- const viz::LocalSurfaceId& RootFrameSink::SubmitRootCompositorFrame(
- viz::CompositorFrame frame) {
- frame.metadata.frame_token = ++next_root_frame_token_;
- if (!root_local_surface_id_allocator_.HasValidLocalSurfaceId() ||
- root_surface_size_ != frame.size_in_pixels() ||
- root_device_scale_factor_ != frame.device_scale_factor()) {
- root_local_surface_id_allocator_.GenerateId();
- root_surface_size_ = frame.size_in_pixels();
- root_device_scale_factor_ = frame.device_scale_factor();
- }
- const auto& local_surface_id =
- root_local_surface_id_allocator_.GetCurrentLocalSurfaceId();
- support_->SubmitCompositorFrame(local_surface_id, std::move(frame));
- return local_surface_id;
- }
- void RootFrameSink::EvictRootSurface(
- const viz::LocalSurfaceId& local_surface_id) {
- const auto& current_local_surface_id =
- root_local_surface_id_allocator_.GetCurrentLocalSurfaceId();
- DLOG_IF(FATAL, !current_local_surface_id.IsSameOrNewerThan(local_surface_id))
- << "Evicting newer surface: " << local_surface_id.ToString()
- << " old: " << current_local_surface_id.ToString();
- if (current_local_surface_id == local_surface_id) {
- root_surface_size_ = gfx::Size();
- root_device_scale_factor_ = 0.0f;
- }
- support_->EvictSurface(local_surface_id);
- }
- void RootFrameSink::DidReceiveCompositorFrameAck(
- std::vector<viz::ReturnedResource> resources) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- ReclaimResources(std::move(resources));
- }
- void RootFrameSink::ReclaimResources(
- std::vector<viz::ReturnedResource> resources) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // Root surface should have no resources to return.
- CHECK(resources.empty());
- }
- void RootFrameSink::OnNeedsBeginFrames(bool needs_begin_frames) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- TRACE_EVENT_INSTANT1("android_webview", "RootFrameSink::OnNeedsBeginFrames",
- TRACE_EVENT_SCOPE_THREAD, "needs_begin_frames",
- needs_begin_frames);
- clients_need_begin_frames_ = needs_begin_frames;
- // Old heuristic doesn't need extra begin frames, so just forward client
- // needs.
- if (!use_new_invalidate_heuristic_) {
- UpdateNeedsBeginFrames(clients_need_begin_frames_);
- return;
- }
- // Make sure that we subscribed to BF if client needs them. We don't
- // unsubscribe from BF here to make sure that we invalidated for the latest
- // frames in necessary. We will stop observing them later in BeginFrame()
- // once we are done.
- if (clients_need_begin_frames_)
- UpdateNeedsBeginFrames(true);
- }
- void RootFrameSink::AddChildFrameSinkId(const viz::FrameSinkId& frame_sink_id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- child_frame_sink_ids_.insert(frame_sink_id);
- GetFrameSinkManager()->RegisterFrameSinkHierarchy(root_frame_sink_id_,
- frame_sink_id);
- }
- void RootFrameSink::RemoveChildFrameSinkId(
- const viz::FrameSinkId& frame_sink_id) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- child_frame_sink_ids_.erase(frame_sink_id);
- GetFrameSinkManager()->UnregisterFrameSinkHierarchy(root_frame_sink_id_,
- frame_sink_id);
- }
- void RootFrameSink::SetContainedSurfaces(
- const base::flat_set<viz::SurfaceId>& ids) {
- contained_surfaces_ = ids;
- for (auto it = last_invalidated_frame_id_.begin();
- it != last_invalidated_frame_id_.end();) {
- if (!contained_surfaces_.contains(it->first))
- it = last_invalidated_frame_id_.erase(it);
- else
- ++it;
- }
- }
- void RootFrameSink::UpdateNeedsBeginFrames(bool needs_begin_frames) {
- if (needs_begin_frames_ != needs_begin_frames) {
- needs_begin_frames_ = needs_begin_frames;
- if (client_)
- client_->SetNeedsBeginFrames(needs_begin_frames_);
- }
- }
- bool RootFrameSink::HasPendingDependency(const viz::SurfaceId& surface_id) {
- auto* surface =
- GetFrameSinkManager()->surface_manager()->GetSurfaceForId(surface_id);
- if (!surface || !surface->HasActiveFrame())
- return true;
- for (auto& range : surface->GetActiveFrame().metadata.referenced_surfaces) {
- if (HasPendingDependency(range.end()))
- return true;
- }
- return false;
- }
- bool RootFrameSink::ProcessVisibleSurfacesInvalidation() {
- if (!use_new_invalidate_heuristic_) {
- // This handles only invalidation of sub clients, root client invalidation
- // is handled by Invalidate() from cc to |SynchronousLayerTreeFrameSink|. So
- // we return false unless we already have damage.
- return needs_draw_;
- }
- bool invalidate = false;
- // There are few possible cases:
- // * viz::Surface is visible (i.e was embedded last frame and any scheduled
- // draws don't change that). In this case surface is in `contained_surfaces`
- // and we need to invalidate for any CompositorFrame that we haven't
- // invalidated yet. This is a steady state.
- // * viz::Surface is visible, but there are scheduled draws that remove it. In
- // this case surface is in `contained_surfaces`, but technically there is no
- // need to invalidate it. We can't know that it will disappear, so we
- // invalidate anyway.
- // * viz::Surface is visible, but has pending dependencies (embedded surfaces
- // without active frame). In this case surface is in `contained_surfaces`, but
- // the dependents aren't. Invalidate in this case pessimistically assuming
- // there are uncommitted frames that can be activated on commit in dependent
- // frames.
- // * viz::Surface is not visible yet, but there is a pending draw that will
- // embed it. In this case the surface is not in `contained_surfaces` yet, so
- // we can't process it here. After the draw will happen it's possible that
- // there are uncommitted frames that are already scheduled to draw, but have
- // not been processed here. This can cause extra invalidation.
- // * viz::Surface is not visible and there is no pending draw. This shouldn't
- // be possible because the only way to embed a child surface is for the root
- // renderer to submit a compositor frame and invalidation of it is handled
- // separately.
- // If there is pending dependency, invalidate.
- if (root_local_surface_id_allocator_.HasValidLocalSurfaceId()) {
- auto surface_id = viz::SurfaceId(
- root_frame_sink_id(),
- root_local_surface_id_allocator_.GetCurrentLocalSurfaceId());
- invalidate = invalidate || HasPendingDependency(surface_id);
- }
- for (auto& surface_id : contained_surfaces_) {
- auto* surface =
- GetFrameSinkManager()->surface_manager()->GetSurfaceForId(surface_id);
- if (surface) {
- // Track last frame_id that we invalidated for. Note, that this doesn't
- // take into account what current frame is or what display compositor will
- // draw. The intent here is to invalidate once for each surface we see.
- auto& last_invalidated_id = last_invalidated_frame_id_[surface_id];
- auto uncommited_frame_id =
- last_invalidated_id.IsSequenceValid()
- ? surface->GetUncommitedFrameIdNewerThan(last_invalidated_id)
- : surface->GetFirstUncommitedFrameId();
- if (uncommited_frame_id.has_value()) {
- invalidate = true;
- last_invalidated_id = uncommited_frame_id.value();
- }
- }
- }
- return invalidate;
- }
- bool RootFrameSink::BeginFrame(const viz::BeginFrameArgs& args,
- bool had_input_event) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- // We call ProcessVisibleSurfacesInvalidation() to make sure heuristic updated
- // it's state (e.g last invalidated begin frame args).
- bool invalidate = ProcessVisibleSurfacesInvalidation() || had_input_event;
- TRACE_EVENT_INSTANT1("android_webview", "RootFrameSink::BeginFrame",
- TRACE_EVENT_SCOPE_THREAD, "invalidate", invalidate);
- if (clients_need_begin_frames_) {
- begin_frame_source_->OnBeginFrame(args);
- } else if (!invalidate) {
- if (use_new_invalidate_heuristic_) {
- // Client don't need begin frames and we didn't invalidate, so we don't
- // need them either.
- UpdateNeedsBeginFrames(false);
- }
- }
- return invalidate;
- }
- void RootFrameSink::SetBeginFrameSourcePaused(bool paused) {
- DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
- begin_frame_source_->OnSetBeginFrameSourcePaused(paused);
- }
- void RootFrameSink::SetNeedsDraw(bool needs_draw) {
- // Only old heuristic needs this.
- DCHECK(!use_new_invalidate_heuristic_);
- needs_draw_ = needs_draw;
- // It's possible that client submitted last frame and unsubscribed from
- // BeginFrames, but we haven't draw it yet.
- if (!needs_begin_frames_ && needs_draw) {
- if (client_)
- client_->Invalidate();
- }
- }
- void RootFrameSink::OnNewUncommittedFrame(const viz::SurfaceId& surface_id) {
- // Only new heurstic needs this.
- if (!use_new_invalidate_heuristic_)
- return;
- // If there is new uncommitted frame in visible surface, make sure we request
- // a begin frame to check if we need to invalidate next frame.
- if (contained_surfaces_.contains(surface_id))
- UpdateNeedsBeginFrames(true);
- }
- bool RootFrameSink::IsChildSurface(const viz::FrameSinkId& frame_sink_id) {
- return child_frame_sink_ids_.contains(frame_sink_id);
- }
- void RootFrameSink::ReturnResources(
- viz::FrameSinkId frame_sink_id,
- uint32_t layer_tree_frame_sink_id,
- std::vector<viz::ReturnedResource> resources) {
- if (client_)
- client_->ReturnResources(frame_sink_id, layer_tree_frame_sink_id,
- std::move(resources));
- }
- void RootFrameSink::OnCompositorFrameTransitionDirectiveProcessed(
- viz::FrameSinkId frame_sink_id,
- uint32_t layer_tree_frame_sink_id,
- uint32_t sequence_id) {
- if (client_) {
- client_->OnCompositorFrameTransitionDirectiveProcessed(
- frame_sink_id, layer_tree_frame_sink_id, sequence_id);
- }
- }
- void RootFrameSink::DettachClient() {
- client_ = nullptr;
- }
- void RootFrameSink::SubmitChildCompositorFrame(ChildFrame* child_frame) {
- DCHECK(child_frame->frame);
- DCHECK(child_frame->local_surface_id.is_valid());
- if (!child_sink_support_ ||
- child_sink_support_->frame_sink_id() != child_frame->frame_sink_id ||
- child_sink_support_->layer_tree_frame_sink_id() !=
- child_frame->layer_tree_frame_sink_id) {
- child_sink_support_.reset();
- child_sink_support_ = std::make_unique<ChildCompositorFrameSink>(
- this, child_frame->layer_tree_frame_sink_id,
- child_frame->frame_sink_id);
- }
- child_sink_support_->SubmitCompositorFrame(
- child_frame->local_surface_id, std::move(*child_frame->frame),
- std::move(child_frame->hit_test_region_list));
- child_frame->frame.reset();
- }
- viz::FrameTimingDetailsMap RootFrameSink::TakeChildFrameTimingDetailsMap() {
- // Take timing for root CompositorFrameSinkSupport to avoid them accumulating.
- // We don't use them anyhow.
- std::ignore = support_->TakeFrameTimingDetailsMap();
- if (child_sink_support_)
- return child_sink_support_->support()->TakeFrameTimingDetailsMap();
- return viz::FrameTimingDetailsMap();
- }
- gfx::Size RootFrameSink::GetChildFrameSize() {
- // TODO(vasilyt): This is not going to work with VizFrameSubmissionForWebView.
- if (child_sink_support_) {
- return child_sink_support_->size();
- }
- return gfx::Size();
- }
- void RootFrameSink::EvictChildSurface(const viz::SurfaceId& surface_id) {
- DCHECK(child_sink_support_);
- child_sink_support_->EvictSurface(surface_id);
- }
- void RootFrameSink::OnCaptureStarted(const viz::FrameSinkId& frame_sink_id) {
- auto it = std::find_if(contained_surfaces_.begin(), contained_surfaces_.end(),
- [frame_sink_id](const viz::SurfaceId& surface_id) {
- return surface_id.frame_sink_id() == frame_sink_id;
- });
- if (it == contained_surfaces_.end())
- return;
- // When a capture is started we need to force an invalidate.
- if (client_)
- client_->Invalidate();
- }
- } // namespace android_webview
|