blob: 121a1b751192fbc15881d107d56ef92609fe7d14 [file] [log] [blame]
// 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.
#include "ash/frame_sink/frame_sink_holder.h"
#include <algorithm>
#include <memory>
#include "ash/frame_sink/frame_sink_host.h"
#include "ash/frame_sink/ui_resource_manager.h"
#include "base/check.h"
#include "base/task/single_thread_task_runner.h"
#include "cc/trees/layer_tree_frame_sink.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/frame_timing_details.h"
#include "components/viz/common/hit_test/hit_test_region_list.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "ui/aura/window.h"
namespace ash {
FrameSinkHolder::FrameSinkHolder(
std::unique_ptr<cc::LayerTreeFrameSink> frame_sink,
const GetCompositorFrameCallback callback)
: frame_sink_(std::move(frame_sink)),
get_compositor_frame_callback_(std::move(callback)) {
frame_sink_->BindToClient(this);
}
FrameSinkHolder::~FrameSinkHolder() {
if (frame_sink_) {
frame_sink_->DetachFromClient();
}
if (root_window_for_deletion_) {
root_window_for_deletion_->RemoveObserver(this);
}
}
// static.
bool FrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::unique_ptr<FrameSinkHolder> frame_sink_holder,
aura::Window* host_window) {
UiResourceManager& resource_manager = frame_sink_holder->resource_manager();
if (frame_sink_holder->last_frame_size_in_pixels_.IsEmpty()) {
// Delete sink holder immediately if no frame has been submitted.
DCHECK(resource_manager.exported_resources_count() == 0);
return true;
}
// Submit an empty frame to ensure that pending release callbacks will be
// processed in a finite amount of time.
frame_sink_holder->frame_sink_->SubmitCompositorFrame(
frame_sink_holder->CreateEmptyFrame(),
/*hit_test_data_changed=*/true);
// Delete sink holder immediately if not waiting for exported resources to
// be reclaimed.
if (resource_manager.exported_resources_count() == 0) {
return true;
}
// If we have exported resources to reclaim then extend the lifetime of
// holder by deleting it later.
DCHECK(host_window);
aura::Window* root_window = host_window->GetRootWindow();
// This can be null during shutdown.
if (!root_window) {
// Since we are in shutdown process, we will not be able to recover the
// exported resources so just let the resources be marked as lost.
resource_manager.LostExportedResources();
return true;
}
// If we have exported resources to reclaim then extend the lifetime of
// holder.
frame_sink_holder.release()->SetRootWindowForDeletion(root_window);
return false;
}
viz::CompositorFrame FrameSinkHolder::CreateEmptyFrame() {
viz::CompositorFrame frame;
frame.metadata.begin_frame_ack.frame_id =
viz::BeginFrameId(viz::BeginFrameArgs::kManualSourceId,
viz::BeginFrameArgs::kStartingFrameNumber);
frame.metadata.begin_frame_ack.has_damage = true;
frame.metadata.device_scale_factor = last_frame_device_scale_factor_;
frame.metadata.frame_token = ++compositor_frame_token_generator_;
auto pass = viz::CompositorRenderPass::Create();
pass->SetNew(viz::CompositorRenderPassId{1},
gfx::Rect(last_frame_size_in_pixels_),
gfx::Rect(last_frame_size_in_pixels_), gfx::Transform());
frame.render_pass_list.push_back(std::move(pass));
return frame;
}
void FrameSinkHolder::SetRootWindowForDeletion(aura::Window* root_window) {
// The holder will delete itself when the root window is removed or when all
// exported resources have been reclaimed.
DCHECK(root_window);
root_window_for_deletion_ = root_window;
root_window->AddObserver(this);
}
void FrameSinkHolder::SubmitCompositorFrame(bool synchronous_draw) {
// We cannot request to submit a frame via `SubmitCompositorFrame()` if we are
// in auto_update mode.
DCHECK(!auto_update_);
// Once the lifetime of FrameSinkHolder is extended, we should not submit new
// frames since the `get_compositor_frame_callback_` can become null.
DCHECK(!WaitingToScheduleDelete());
if (delete_pending_ || auto_update_ || WaitingToScheduleDelete()) {
return;
}
// If we are already submitted a frame we cannot submit a new frame until we
// get an acknowledgement from display compositor and we fall back to
// asynchronous drawing.
// Some FrameSinkHosts can request to submit a frame synchronously, even
// before viz thread is fully enabled therefore we wait till display
// compositor asks for the first frame therefore we fall to asynchronous
// drawing till signaled.
if (!synchronous_draw || pending_compositor_frame_ack_ ||
!first_frame_requested_) {
pending_compositor_frame_ = true;
return;
}
std::unique_ptr<viz::CompositorFrame> frame =
get_compositor_frame_callback_.Run(
viz::BeginFrameAck::CreateManualAckWithDamage(), resources_manager_,
auto_update_, last_frame_size_in_pixels_,
last_frame_device_scale_factor_);
if (!frame) {
return;
}
SubmitCompositorFrameInternal(std::move(frame));
}
void FrameSinkHolder::SubmitCompositorFrameInternal(
std::unique_ptr<viz::CompositorFrame> frame) {
pending_compositor_frame_ = false;
pending_compositor_frame_ack_ = true;
last_frame_size_in_pixels_ = frame->size_in_pixels();
last_frame_device_scale_factor_ = frame->metadata.device_scale_factor;
frame->metadata.frame_token = ++compositor_frame_token_generator_;
frame_sink_->SubmitCompositorFrame(std::move(*frame),
/*hit_test_data_changed=*/true);
}
void FrameSinkHolder::OnBeginFrameSourcePausedChanged(bool paused) {}
bool FrameSinkHolder::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) {
// Once the lifetime of FrameSinkHolder is extended, we should not submit new
// frames asynchronously since the `get_compositor_frame_callback_` can become
// null.
if (WaitingToScheduleDelete()) {
return false;
}
viz::BeginFrameAck current_begin_frame_ack(args, false);
first_frame_requested_ = true;
if (pending_compositor_frame_ack_ ||
!(pending_compositor_frame_ || auto_update_)) {
cc::FrameSkippedReason reason = pending_compositor_frame_ack_
? cc::FrameSkippedReason::kWaitingOnMain
: cc::FrameSkippedReason::kNoDamage;
frame_sink_->DidNotProduceFrame(current_begin_frame_ack, reason);
return false;
}
std::unique_ptr<viz::CompositorFrame> frame =
get_compositor_frame_callback_.Run(
current_begin_frame_ack, resources_manager_, auto_update_,
last_frame_size_in_pixels_, last_frame_device_scale_factor_);
if (!frame) {
// Failure to produce a frame is treated as if there was no damage.
frame_sink_->DidNotProduceFrame(current_begin_frame_ack,
cc::FrameSkippedReason::kNoDamage);
return false;
}
SubmitCompositorFrameInternal(std::move(frame));
return true;
}
void FrameSinkHolder::SetBeginFrameSource(viz::BeginFrameSource* source) {
if (source == begin_frame_source_) {
return;
}
if (begin_frame_source_) {
begin_frame_source_->RemoveObserver(this);
}
begin_frame_source_ = source;
if (begin_frame_source_) {
begin_frame_source_->AddObserver(this);
}
}
absl::optional<viz::HitTestRegionList> FrameSinkHolder::BuildHitTestData() {
return absl::nullopt;
}
void FrameSinkHolder::ReclaimResources(
std::vector<viz::ReturnedResource> resources) {
if (delete_pending_) {
return;
}
resource_manager().ReclaimResources(resources);
if (WaitingToScheduleDelete() &&
resource_manager().exported_resources_count() == 0) {
ScheduleDelete();
}
}
void FrameSinkHolder::SetTreeActivationCallback(
base::RepeatingClosure callback) {}
void FrameSinkHolder::DidReceiveCompositorFrameAck() {
pending_compositor_frame_ack_ = false;
}
void FrameSinkHolder::DidPresentCompositorFrame(
uint32_t frame_token,
const viz::FrameTimingDetails& details) {
if (!presentation_callback_.is_null()) {
presentation_callback_.Run(details.presentation_feedback);
}
}
void FrameSinkHolder::DidLoseLayerTreeFrameSink() {
resource_manager().LostExportedResources();
if (WaitingToScheduleDelete()) {
ScheduleDelete();
}
}
void FrameSinkHolder::OnDraw(const gfx::Transform& transform,
const gfx::Rect& viewport,
bool resourceless_software_draw,
bool skip_draw) {}
void FrameSinkHolder::SetMemoryPolicy(const cc::ManagedMemoryPolicy& policy) {}
void FrameSinkHolder::SetExternalTilePriorityConstraints(
const gfx::Rect& viewport_rect,
const gfx::Transform& transform) {}
void FrameSinkHolder::OnWindowDestroying(aura::Window* window) {
// Since we are destroying the root_window_for_deletion_ via which we were
// extending the lifetime of the layer_sink_holder, after this point we cannot
// recover the exported resources therefore just mark the exported resources
// as lost.
resources_manager_.LostExportedResources();
root_window_for_deletion_->RemoveObserver(this);
root_window_for_deletion_ = nullptr;
// Detaching client from `frame_sink_` ensures that display_compositor does
// not call methods on `this` after we have scheduled the deletion of this
// holder.
frame_sink_->DetachFromClient();
frame_sink_.reset();
ScheduleDelete();
}
void FrameSinkHolder::ScheduleDelete() {
if (delete_pending_) {
return;
}
delete_pending_ = true;
base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(FROM_HERE,
this);
}
bool FrameSinkHolder::WaitingToScheduleDelete() const {
// We only set root_window_for_deletion_, after calling
// FrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed. Non-null
// root_window_for_deletion_ means that we are waiting for all the exported
// resources to be returned before we can delete `this` frame sink holder.
return root_window_for_deletion_;
}
} // namespace ash