blob: 1f1e7af322d31f958ba649c470c8c04a38f108f7 [file] [log] [blame]
// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/viz/service/surfaces/surface.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <limits>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/debug/alias.h"
#include "base/debug/crash_logging.h"
#include "base/debug/dump_without_crashing.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/time/tick_clock.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/traced_value.h"
#include "components/viz/common/features.h"
#include "components/viz/common/quads/compositor_render_pass.h"
#include "components/viz/common/resources/returned_resource.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/common/surfaces/parent_local_surface_id_allocator.h"
#include "components/viz/service/surfaces/referenced_surface_tracker.h"
#include "components/viz/service/surfaces/surface_allocation_group.h"
#include "components/viz/service/surfaces/surface_client.h"
#include "components/viz/service/surfaces/surface_manager.h"
#include "components/viz/service/viz_service_export.h"
#include "third_party/perfetto/include/perfetto/tracing/track.h"
#include "ui/gfx/presentation_feedback.h"
#include "ui/gfx/swap_result.h"
namespace viz {
namespace {
// Adds the given |request| to the requests of the given |render_pass|, removing
// any duplicate requests made by the same source.
void RequestCopyOfOutputOnRenderPass(std::unique_ptr<CopyOutputRequest> request,
CompositorRenderPass& render_pass) {
if (request->has_source()) {
const base::UnguessableToken& source = request->source();
// Remove existing CopyOutputRequests made on the Surface by the same
// source.
std::erase_if(render_pass.copy_requests,
[&source](const std::unique_ptr<CopyOutputRequest>& x) {
return x->has_source() && x->source() == source;
});
}
render_pass.copy_requests.push_back(std::move(request));
}
std::string PendingFrameDataChangeReasonToString(
Surface::PendingFrameDataChangeReason reason) {
switch (reason) {
case Surface::PendingFrameDataChangeReason::kNotSet:
return "NotSet";
case Surface::PendingFrameDataChangeReason::kCommitFrameReset:
return "CommitFrameReset";
case Surface::PendingFrameDataChangeReason::kCommitFrameDependencies:
return "CommitFrameDependencies";
case Surface::PendingFrameDataChangeReason::kActivatePendingFrameReset:
return "ActivatePendingFrameReset";
case Surface::PendingFrameDataChangeReason::kSurfaceDestruction:
return "SurfaceDestruction";
}
}
std::string FrameActivationReasonToString(
Surface::FrameActivationReason reason) {
switch (reason) {
case Surface::FrameActivationReason::kNotSet:
return "NotSet";
case Surface::FrameActivationReason::kDependencyResolved:
return "DependencyResolved";
case Surface::FrameActivationReason::kDeadline:
return "Deadline";
case Surface::FrameActivationReason::kCommitWithNoDependencies:
return "CommitWithNoDependencies";
}
}
} // namespace
Surface::PresentationHelper::PresentationHelper(
base::WeakPtr<SurfaceClient> surface_client,
uint32_t frame_token)
: surface_client_(std::move(surface_client)), frame_token_(frame_token) {}
Surface::PresentationHelper::~PresentationHelper() {
// The class that called TakePresentationHelperForPresentNotification
// should have called present on this helper. If not, give a Failure feedback
// to the appropriate surface client.
DidPresent(base::TimeTicks(), gfx::SwapTimings(),
gfx::PresentationFeedback::Failure());
}
void Surface::PresentationHelper::DidPresent(
base::TimeTicks draw_start_timestamp,
const gfx::SwapTimings& swap_timings,
const gfx::PresentationFeedback& feedback) {
if (surface_client_) {
surface_client_->OnSurfacePresented(frame_token_, draw_start_timestamp,
swap_timings, feedback);
}
surface_client_ = nullptr;
}
Surface::Surface(const SurfaceInfo& surface_info,
SurfaceManager* surface_manager,
SurfaceAllocationGroup* allocation_group,
base::WeakPtr<SurfaceClient> surface_client,
const SurfaceId& pending_copy_surface_id,
size_t max_uncommitted_frames)
: surface_info_(surface_info),
surface_manager_(surface_manager),
surface_client_(std::move(surface_client)),
pending_copy_surface_id_(pending_copy_surface_id),
allocation_group_(allocation_group),
max_uncommitted_frames_(max_uncommitted_frames) {
TRACE_EVENT_BEGIN(TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"),
"Surface", perfetto::Track::FromPointer(this),
"surface_info", surface_info.ToString());
allocation_group_->RegisterSurface(this);
is_fallback_ =
allocation_group_->GetLastReference().IsNewerThan(surface_id());
}
Surface::~Surface() {
ClearCopyRequests();
surface_manager_->SurfaceDestroyed(this);
for (auto& frame : uncommitted_frames_) {
UnrefFrameResourcesAndRunCallbacks(std::move(frame));
}
pending_frame_data_change_reason_ =
PendingFrameDataChangeReason::kSurfaceDestruction;
UnrefFrameResourcesAndRunCallbacks(std::move(pending_frame_data_));
UnrefFrameResourcesAndRunCallbacks(std::move(active_frame_data_));
// Unregister this surface as the embedder of all the allocation groups that
// it references.
for (SurfaceAllocationGroup* group : referenced_allocation_groups_)
group->UnregisterActiveEmbedder(this);
for (SurfaceAllocationGroup* group : blocking_allocation_groups_)
group->UnregisterBlockedEmbedder(this, false /* did_activate */);
DCHECK(deadline_);
deadline_->Cancel();
TRACE_EVENT_END(
TRACE_DISABLED_BY_DEFAULT("viz.surface_lifetime"), /* Surface */
perfetto::Track::FromPointer(this), "surface_info",
surface_info_.ToString());
allocation_group_->UnregisterSurface(this);
if (surface_client_) {
surface_client_->OnSurfaceDestroyed(this);
}
}
// FrameSinkObserver implementation
void Surface::OnViewTransitionSaved(
const blink::ViewTransitionToken& transition_token) {
if (!view_transition_dependencies_.contains(transition_token)) {
// Return early since the dependency was never added for this view
// transition token.
return;
}
// Since the transition's Save directive is fulfilled, we can remove it as
// dependency.
view_transition_dependencies_.erase(transition_token);
// Return early since there are still dependencies to be fulfilled.
if (!view_transition_dependencies_.empty() ||
!activation_dependencies_.empty()) {
return;
}
ActivatePendingFrame();
}
void Surface::SetDependencyDeadline(
std::unique_ptr<SurfaceDependencyDeadline> deadline) {
deadline_ = std::move(deadline);
}
void Surface::SetPreviousFrameSurface(Surface* surface) {
DCHECK(surface && (HasActiveFrame() || HasPendingFrame()));
previous_frame_surface_id_ = surface->surface_id();
}
void Surface::UpdateSurfaceReferences() {
const base::flat_set<SurfaceId>& existing_referenced_surfaces =
surface_manager_->GetSurfacesReferencedByParent(surface_id());
// Populate list of surface references to add and remove by getting the
// difference between existing surface references and surface references for
// latest activated CompositorFrame.
std::vector<SurfaceReference> references_to_add;
std::vector<SurfaceReference> references_to_remove;
GetSurfaceReferenceDifference(surface_id(), existing_referenced_surfaces,
active_referenced_surfaces(),
&references_to_add, &references_to_remove);
// Modify surface references stored in SurfaceManager.
if (!references_to_add.empty())
surface_manager_->AddSurfaceReferences(references_to_add);
if (!references_to_remove.empty())
surface_manager_->RemoveSurfaceReferences(references_to_remove);
}
void Surface::OnChildActivatedForActiveFrame(const SurfaceId& activated_id) {
DCHECK(HasActiveFrame());
for (auto& surface_range : GetActiveFrame().metadata.referenced_surfaces) {
if (surface_range.IsInRangeInclusive(activated_id)) {
// If |activated_id| is included in any of the surface reference then
// recompute the active surface references. This must handle the case
// where a SurfaceId is included in multiple surface ranges.
RecomputeActiveReferencedSurfaces();
return;
}
}
}
void Surface::SetIsFallbackAndMaybeActivate() {
is_fallback_ = true;
if (HasPendingFrame())
ActivatePendingFrameForDeadline();
}
void Surface::ActivateIfDeadlinePassed() {
DCHECK(HasPendingFrame());
if (!deadline_->HasDeadlinePassed())
return;
TRACE_EVENT1("viz", "Surface deadline passed", "FrameSinkId",
surface_id().frame_sink_id().ToString());
ActivatePendingFrameForDeadline();
}
Surface::QueueFrameResult Surface::QueueFrame(
CompositorFrame frame,
uint32_t frame_index,
base::ScopedClosureRunner frame_rejected_callback) {
if (frame.size_in_pixels() != surface_info_.size_in_pixels() ||
frame.device_scale_factor() != surface_info_.device_scale_factor()) {
TRACE_EVENT_INSTANT0("viz", "Surface invariants violation",
TRACE_EVENT_SCOPE_THREAD);
return QueueFrameResult::REJECTED;
}
// Receive and track the resources referenced from the CompositorFrame
// regardless of whether it's pending or active.
surface_client_->ReceiveFromChild(frame.resource_list);
QueueFrameResult result = QueueFrameResult::ACCEPTED_PENDING;
if (!max_uncommitted_frames_) {
result = CommitFrame(FrameData(std::move(frame), frame_index));
} else {
// Return oldest frame if uncommitted queue is full.
DCHECK_LE(uncommitted_frames_.size(), max_uncommitted_frames_);
if (uncommitted_frames_.size() == max_uncommitted_frames_) {
TRACE_EVENT_INSTANT1("viz", "DropUncommitedFrame",
TRACE_EVENT_SCOPE_THREAD, "queue_length",
uncommitted_frames_.size());
UnrefFrameResourcesAndRunCallbacks(
std::move(uncommitted_frames_.front()));
uncommitted_frames_.pop_front();
}
uncommitted_frames_.push_back(FrameData(std::move(frame), frame_index));
// If we still have space in queue we should send ack the client because we
// can receive another frame without dropping it.
if (uncommitted_frames_.size() < max_uncommitted_frames_) {
TRACE_EVENT_INSTANT1("viz", "AckingUncommitedFrame",
TRACE_EVENT_SCOPE_THREAD, "queue_length",
uncommitted_frames_.size());
uncommitted_frames_.back().SendAckIfNeeded(surface_client_.get());
}
surface_manager_->OnSurfaceHasNewUncommittedFrame(this);
}
// The frame should not fail to display beyond this point. Release the
// callback so it is not called.
std::ignore = frame_rejected_callback.Release();
return result;
}
Surface::QueueFrameResult Surface::CommitFrame(FrameData frame) {
TRACE_EVENT1("viz", "Surface::CommitFrame", "SurfaceId",
surface_id().ToString());
is_latency_info_taken_ = false;
if (active_frame_data_ || pending_frame_data_)
previous_frame_surface_id_ = surface_id();
TakePendingLatencyInfo(&frame.frame.metadata.latency_info);
pending_frame_data_change_reason_ =
PendingFrameDataChangeReason::kCommitFrameReset;
std::optional<FrameData> previous_pending_frame_data =
std::move(pending_frame_data_);
pending_frame_data_.reset();
view_transition_dependencies_.clear();
if (features::ShouldAckCOREarlyForViewTransition()) {
for (const auto& directive : frame.frame.metadata.transition_directives) {
const auto& token = directive.transition_token();
// If there is no SurfaceAnimationManager for the `token` and an Animate
// directive has been issued, then previous frame is held up and has not
// performed Save directive yet for a cross-document view transition. So
// add this token as dependency for new document's surface which needs to
// be resolved for activation.
if (directive.type() ==
CompositorFrameTransitionDirective::Type::kAnimateRenderer &&
!surface_manager_->FrameSinkManagerHasViewTransitionToken(token)) {
// Observe FrameSinkManager if we're not already observing.
if (!frame_sink_manager_observation_.IsObserving()) {
frame_sink_manager_observation_.Observe(surface_manager_);
}
view_transition_dependencies_.insert(token);
}
}
}
UpdateActivationDependencies(frame.frame);
QueueFrameResult result = QueueFrameResult::ACCEPTED_ACTIVE;
if (activation_dependencies_.empty() &&
view_transition_dependencies_.empty()) {
// If there are no blockers, then immediately activate the frame.
ActivateFrame(std::move(frame));
frame_activation_reason_ = FrameActivationReason::kCommitWithNoDependencies;
} else {
pending_frame_data_change_reason_ =
PendingFrameDataChangeReason::kCommitFrameDependencies;
pending_frame_data_ = std::move(frame);
auto traced_value = std::make_unique<base::trace_event::TracedValue>();
traced_value->BeginArray("Pending");
for (auto& it : activation_dependencies_)
traced_value->AppendString(it.ToString());
traced_value->EndArray();
TRACE_EVENT_BEGIN("viz", "SurfaceQueuedPending",
perfetto::Track::FromPointer(this), "LocalSurfaceId",
surface_info_.id().ToString(), "ActivationDependencies",
std::move(traced_value));
deadline_->Set(ResolveFrameDeadline(pending_frame_data_->frame));
if (deadline_->HasDeadlinePassed()) {
ActivatePendingFrameForDeadline();
} else {
// If we are blocked on another Surface, and its latest frame is unacked,
// we send the Ack now. This will allow frame production to continue for
// that client, leading to the group being unblocked.
for (SurfaceAllocationGroup* it : blocking_allocation_groups_) {
it->AckLastestActiveUnAckedFrame();
}
result = QueueFrameResult::ACCEPTED_PENDING;
}
}
// Returns resources for the previous pending frame.
UnrefFrameResourcesAndRunCallbacks(std::move(previous_pending_frame_data));
if (surface_client_)
surface_client_->OnSurfaceCommitted(this);
return result;
}
void Surface::RequestCopyOfOutput(
PendingCopyOutputRequest pending_copy_output_request) {
TRACE_EVENT1("viz", "Surface::RequestCopyOfOutput", "has_active_frame_data",
!!active_frame_data_);
if (!pending_copy_output_request.subtree_capture_id.is_valid()) {
RequestCopyOfOutputOnRootRenderPass(
std::move(pending_copy_output_request.copy_output_request));
return;
}
if (!active_frame_data_)
return;
for (auto& render_pass : GetActiveFrame().render_pass_list) {
if (render_pass->subtree_capture_id ==
pending_copy_output_request.subtree_capture_id) {
RequestCopyOfOutputOnRenderPass(
std::move(pending_copy_output_request.copy_output_request),
*render_pass);
return;
}
}
}
void Surface::RequestCopyOfOutputOnRootRenderPass(
std::unique_ptr<CopyOutputRequest> copy_request) {
TRACE_EVENT1("viz", "Surface::RequestCopyOfOutputOnRootRenderPass",
"has_active_frame_data", !!active_frame_data_);
if (!active_frame_data_)
return; // |copy_request| auto-sends empty result on out-of-scope.
RequestCopyOfOutputOnRenderPass(std::move(copy_request),
*GetActiveFrame().render_pass_list.back());
}
bool Surface::RequestCopyOfOutputOnActiveFrameRenderPassId(
std::unique_ptr<CopyOutputRequest> copy_request,
CompositorRenderPassId render_pass_id) {
TRACE_EVENT1("viz", "Surface::RequestCopyOfOurpurRenderPassId",
"has_active_frame_data", !!active_frame_data_);
if (!active_frame_data_)
return false;
// Find a render pass with a given id, and attach the copy output request on
// it.
for (auto& render_pass : GetActiveFrame().render_pass_list) {
if (render_pass->id == render_pass_id) {
RequestCopyOfOutputOnRenderPass(std::move(copy_request), *render_pass);
return true;
}
}
return false;
}
void Surface::OnActivationDependencyResolved(
const SurfaceId& activation_dependency,
SurfaceAllocationGroup* group) {
DCHECK(activation_dependencies_.count(activation_dependency));
activation_dependencies_.erase(activation_dependency);
blocking_allocation_groups_.erase(group);
if (!activation_dependencies_.empty() ||
!view_transition_dependencies_.empty()) {
return;
}
TRACE_EVENT_END(
"viz", /* SurfaceQueuedPending */ perfetto::Track::FromPointer(this));
// All blockers have been cleared. The surface can be activated now.
ActivatePendingFrame();
frame_activation_reason_ = FrameActivationReason::kDependencyResolved;
}
void Surface::ActivatePendingFrameForDeadline() {
if (!pending_frame_data_)
return;
if (!activation_dependencies_.empty()) {
TRACE_EVENT_END(
"viz", /* SurfaceQueuedPending */ perfetto::Track::FromPointer(this));
}
// If a frame is being activated because of a deadline, then clear its set
// of blockers.
activation_dependencies_.clear();
view_transition_dependencies_.clear();
ActivatePendingFrame();
frame_activation_reason_ = FrameActivationReason::kDeadline;
}
Surface::FrameData::FrameData(CompositorFrame&& frame, uint32_t frame_index)
: frame(std::move(frame)), frame_index(frame_index) {}
Surface::FrameData::FrameData(FrameData&& other) = default;
Surface::FrameData& Surface::FrameData::operator=(FrameData&& other) = default;
Surface::FrameData::~FrameData() = default;
void Surface::FrameData::SendAckIfNeeded(SurfaceClient* client) {
if (!frame_acked) {
frame_acked = true;
if (client)
client->SendCompositorFrameAck();
}
}
void Surface::ActivatePendingFrame() {
SCOPED_CRASH_KEY_STRING32(
"viz", "Pending frame data change reason",
PendingFrameDataChangeReasonToString(pending_frame_data_change_reason_));
SCOPED_CRASH_KEY_STRING32(
"viz", "Last frame activation reason",
FrameActivationReasonToString(frame_activation_reason_));
CHECK(pending_frame_data_);
pending_frame_data_change_reason_ =
PendingFrameDataChangeReason::kActivatePendingFrameReset;
FrameData frame_data = std::move(*pending_frame_data_);
pending_frame_data_.reset();
std::optional<base::TimeDelta> duration = deadline_->Cancel();
if (duration.has_value()) {
TRACE_EVENT_INSTANT2("viz", "SurfaceSynchronizationEvent",
TRACE_EVENT_SCOPE_THREAD, "surface_id",
surface_info_.id().ToString(), "duration_ms",
duration.value().InMilliseconds());
}
ActivateFrame(std::move(frame_data));
}
void Surface::CommitFramesRecursively(const CommitPredicate& predicate) {
TRACE_EVENT1("viz", "Surface::CommitFramesRecursively", "SurfaceId",
surface_id().ToString());
// This should only be called if we use uncommitted frames queue.
DCHECK_GT(max_uncommitted_frames_, 0u);
while (!uncommitted_frames_.empty()) {
const auto& ack =
uncommitted_frames_.front().frame.metadata.begin_frame_ack;
if (!predicate(surface_id(), ack.frame_id)) {
break;
}
CommitFrame(std::move(uncommitted_frames_.front()));
uncommitted_frames_.pop_front();
}
if (HasPendingFrame()) {
for (auto& range : pending_frame_data_->frame.metadata.referenced_surfaces)
surface_manager_->CommitFramesInRangeRecursively(range, predicate);
}
if (HasActiveFrame()) {
for (auto& range : active_frame_data_->frame.metadata.referenced_surfaces)
surface_manager_->CommitFramesInRangeRecursively(range, predicate);
}
// If we freed up some space in queue send ack for the last frame if it's
// still unacked, so client can continue producing frames.
if (uncommitted_frames_.size() < max_uncommitted_frames_) {
if (!uncommitted_frames_.empty())
uncommitted_frames_.back().SendAckIfNeeded(surface_client_.get());
// Only last frame can be unacked because we ack frames as we put them in
// queue if queue isn't full. If we acked frame above, now verify that
// they all are acked, to ensure we ack frame in order.
#if DCHECK_IS_ON()
for (auto& frames : uncommitted_frames_) {
DCHECK(frames.frame_acked);
}
#endif
}
}
std::optional<uint32_t> Surface::GetFirstUncommitedFrameIndex() {
if (uncommitted_frames_.empty())
return std::nullopt;
return uncommitted_frames_.front().frame_index;
}
std::optional<uint32_t> Surface::GetUncommitedFrameIndexNewerThan(
uint32_t frame_index) {
for (auto& frame : uncommitted_frames_) {
if (frame.frame_index > frame_index) {
return frame.frame_index;
}
}
return std::nullopt;
}
void Surface::ResetPendingCopySurfaceId() {
CHECK(pending_copy_surface_id_.is_valid());
pending_copy_surface_id_ = SurfaceId();
// It's an error to compute the surface references if the current surface does
// not have an active frame.
if (HasActiveFrame()) {
RecomputeActiveReferencedSurfaces();
}
}
void Surface::ClearNonRootCopyRequests() {
ClearCopyRequests(/*keep_root=*/true);
}
void Surface::UpdateReferencedAllocationGroups(
std::vector<SurfaceAllocationGroup*> new_referenced_allocation_groups) {
base::flat_set<raw_ptr<SurfaceAllocationGroup, CtnExperimental>> new_set(
new_referenced_allocation_groups.begin(),
new_referenced_allocation_groups.end());
for (SurfaceAllocationGroup* group : referenced_allocation_groups_) {
if (!new_set.count(group))
group->UnregisterActiveEmbedder(this);
}
for (SurfaceAllocationGroup* group : new_set) {
if (!referenced_allocation_groups_.count(group))
group->RegisterActiveEmbedder(this);
}
referenced_allocation_groups_ = std::move(new_set);
}
void Surface::RecomputeActiveReferencedSurfaces() {
// Extract the latest in flight surface from the ranges in the frame then
// notify SurfaceManager of the new references.
active_referenced_surfaces_.clear();
std::vector<SurfaceAllocationGroup*> new_referenced_allocation_groups;
for (const SurfaceRange& surface_range :
active_frame_data_->frame.metadata.referenced_surfaces) {
// Figure out what surface in the |surface_range| needs to be referenced.
Surface* surface =
surface_manager_->GetLatestInFlightSurface(surface_range);
if (surface) {
active_referenced_surfaces_.insert(surface->surface_id());
}
// The allocation group for the end of the SurfaceRange should always be
// referenced.
SurfaceAllocationGroup* end_allocation_group =
surface_manager_->GetOrCreateAllocationGroupForSurfaceId(
surface_range.end());
if (end_allocation_group) {
new_referenced_allocation_groups.push_back(end_allocation_group);
end_allocation_group->UpdateLastActiveReferenceAndMaybeActivate(
surface_range.end());
}
// Only reference the allocation group for the start of SurfaceRange if the
// current referenced surface is a part of it.
if (surface_range.HasDifferentEmbedTokens() &&
(!surface ||
surface->surface_id().HasSameEmbedTokenAs(*surface_range.start()))) {
SurfaceAllocationGroup* start_allocation_group =
surface_manager_->GetOrCreateAllocationGroupForSurfaceId(
*surface_range.start());
if (start_allocation_group) {
new_referenced_allocation_groups.push_back(start_allocation_group);
start_allocation_group->UpdateLastActiveReferenceAndMaybeActivate(
*surface_range.start());
}
}
}
// Makes sure `pending_copy_surface_id_` is reachable from `this` during
// aggregation.
if (pending_copy_surface_id_.is_valid()) {
active_referenced_surfaces_.insert(pending_copy_surface_id_);
}
UpdateReferencedAllocationGroups(std::move(new_referenced_allocation_groups));
UpdateSurfaceReferences();
}
// A frame is activated if all its Surface ID dependencies are active or a
// deadline has hit and the frame was forcibly activated.
void Surface::ActivateFrame(FrameData frame_data) {
TRACE_EVENT1("viz", "Surface::ActivateFrame", "SurfaceId",
surface_id().ToString());
// Reset observation since the pending frame got activated.
frame_sink_manager_observation_.Reset();
// Save root pass copy requests.
std::vector<std::unique_ptr<CopyOutputRequest>> old_copy_requests;
if (active_frame_data_) {
std::swap(old_copy_requests,
active_frame_data_->frame.render_pass_list.back()->copy_requests);
}
ClearCopyRequests();
TakeActiveLatencyInfo(&frame_data.frame.metadata.latency_info);
std::optional<FrameData> previous_frame_data = std::move(active_frame_data_);
active_frame_data_ = std::move(frame_data);
// We no longer have a pending frame, so unregister self from
// |blocking_allocation_groups_|.
for (SurfaceAllocationGroup* group : blocking_allocation_groups_)
group->UnregisterBlockedEmbedder(this, true /* did_activate */);
blocking_allocation_groups_.clear();
RecomputeActiveReferencedSurfaces();
for (auto& copy_request : old_copy_requests)
RequestCopyOfOutputOnRootRenderPass(std::move(copy_request));
UnrefFrameResourcesAndRunCallbacks(std::move(previous_frame_data));
// This should happen before calling SurfaceManager::FirstSurfaceActivation(),
// as that notifies observers which may have side effects for
// |surface_client_|. See https://crbug.com/821855.
if (surface_client_)
surface_client_->OnSurfaceActivated(this);
if (!seen_first_frame_activation_) {
TRACE_EVENT_WITH_FLOW2(
TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
"LocalSurfaceId.Submission.Flow",
TRACE_ID_GLOBAL(
surface_info_.id().local_surface_id().submission_trace_id()),
TRACE_EVENT_FLAG_FLOW_IN, "step", "FirstSurfaceActivation",
"surface_id", surface_info_.id().ToString());
seen_first_frame_activation_ = true;
allocation_group_->OnFirstSurfaceActivation(this);
surface_manager_->FirstSurfaceActivation(surface_info_);
}
surface_manager_->SurfaceActivated(this);
// Defer notifying the embedder of an updated token until the frame has been
// completely processed.
const auto& metadata = GetActiveFrameMetadata();
if (surface_client_ && metadata.send_frame_token_to_embedder) {
if (!FrameTokenGT(metadata.frame_token, last_sent_frame_token_)) {
uint32_t current_token = metadata.frame_token;
uint32_t last_token = last_sent_frame_token_;
base::debug::Alias(&current_token);
base::debug::Alias(&last_token);
base::debug::DumpWithoutCrashing();
}
last_sent_frame_token_ = metadata.frame_token;
surface_client_->OnFrameTokenChanged(metadata.frame_token);
}
}
FrameDeadline Surface::ResolveFrameDeadline(
const CompositorFrame& current_frame) {
// Fallback surfaces should activate immediately so that the client receives
// the ack and can submit a frame to the primary surface.
if (is_fallback_)
return FrameDeadline::MakeZero();
// If there is an embedder of this surface that has already activated, that
// means the embedder doesn't wish to block on this surface, i.e. either it
// had a zero deadline or its deadline has already passed. If we don't have an
// active frame already, active this frame immediately so we have something to
// show.
if (!HasActiveFrame() &&
allocation_group_->GetLastActiveReference() == surface_id()) {
return FrameDeadline::MakeZero();
}
const std::optional<uint32_t>& default_deadline =
surface_manager_->activation_deadline_in_frames();
const FrameDeadline& deadline = current_frame.metadata.deadline;
uint32_t deadline_in_frames = deadline.deadline_in_frames();
// If no default deadline is available then all deadlines are treated as
// effectively infinite deadlines.
if (!default_deadline || deadline.use_default_lower_bound_deadline()) {
deadline_in_frames = std::max(
deadline_in_frames,
default_deadline.value_or(std::numeric_limits<uint32_t>::max()));
}
return FrameDeadline(deadline.frame_start_time(), deadline_in_frames,
deadline.frame_interval(),
false /* use_default_lower_bound_deadline */);
}
void Surface::UpdateActivationDependencies(
const CompositorFrame& current_frame) {
for (SurfaceAllocationGroup* group : blocking_allocation_groups_)
group->UnregisterBlockedEmbedder(this, false /* did_activate */);
blocking_allocation_groups_.clear();
activation_dependencies_.clear();
// If the client has specified a deadline of zero, there is no need to figure
// out the activation dependencies since the frame will activate immediately.
if (current_frame.metadata.deadline.IsZero())
return;
bool should_block_on_dependencies =
!current_frame.metadata.is_handling_interaction;
if (!should_block_on_dependencies) {
return;
}
base::flat_set<raw_ptr<SurfaceAllocationGroup, CtnExperimental>>
new_blocking_allocation_groups;
std::vector<SurfaceId> new_activation_dependencies;
for (const SurfaceId& surface_id :
current_frame.metadata.activation_dependencies) {
SurfaceAllocationGroup* group =
surface_manager_->GetOrCreateAllocationGroupForSurfaceId(surface_id);
if (base::Contains(new_blocking_allocation_groups, group))
continue;
if (group)
group->UpdateLastPendingReferenceAndMaybeActivate(surface_id);
Surface* dependency = surface_manager_->GetSurfaceForId(surface_id);
if (dependency && dependency->HasActiveFrame()) {
// Normally every creation of SurfaceAllocationGroup should be followed by
// a call to Register* to keep it alive. However, since this one already
// has a registered surface, we don't have to do that.
DCHECK(!group->IsReadyToDestroy());
continue;
}
if (group) {
group->RegisterBlockedEmbedder(this, surface_id);
new_blocking_allocation_groups.insert(group);
}
TRACE_EVENT_WITH_FLOW2(
TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
"LocalSurfaceId.Embed.Flow",
TRACE_ID_GLOBAL(surface_id.local_surface_id().embed_trace_id()),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
"AddedActivationDependency", "child_surface_id", surface_id.ToString());
new_activation_dependencies.push_back(surface_id);
}
activation_dependencies_ = std::move(new_activation_dependencies);
blocking_allocation_groups_ = std::move(new_blocking_allocation_groups);
}
void Surface::TakeCopyOutputRequests(Surface::CopyRequestsMap* copy_requests) {
DCHECK(copy_requests->empty());
if (!active_frame_data_)
return;
for (const auto& render_pass : GetActiveFrame().render_pass_list) {
for (auto& request : render_pass->copy_requests) {
copy_requests->insert(
std::make_pair(render_pass->id, std::move(request)));
}
render_pass->copy_requests.clear();
}
MarkAsDrawn();
}
void Surface::TakeCopyOutputRequestsFromClient() {
if (!surface_client_)
return;
for (PendingCopyOutputRequest& request_params :
surface_client_->TakeCopyOutputRequests(
surface_id().local_surface_id())) {
RequestCopyOfOutput(std::move(request_params));
}
}
bool Surface::HasCopyOutputRequests() const {
return active_frame_data_ && GetActiveFrame().HasCopyOutputRequests();
}
const CompositorFrame& Surface::GetActiveFrame() const {
DCHECK(active_frame_data_);
return active_frame_data_->frame;
}
const CompositorFrameMetadata& Surface::GetActiveFrameMetadata() const {
DCHECK(active_frame_data_);
return active_frame_data_->frame.metadata;
}
const FrameIntervalInputs& Surface::GetFrameIntervalInputs() const {
DCHECK(active_frame_data_);
return active_frame_data_->frame.metadata.frame_interval_inputs;
}
void Surface::SetActiveFrameForViewTransition(CompositorFrame frame) {
CHECK(active_frame_data_.has_value());
active_frame_data_->frame = std::move(frame);
if (features::ShouldAckCOREarlyForViewTransition()) {
// We need to recompute these as there can be undrawn surfaces as referenced
// surfaces for cross-doc view transitions on shared element replacement.
RecomputeActiveReferencedSurfaces();
}
}
const CompositorFrame& Surface::GetPendingFrame() {
DCHECK(pending_frame_data_);
return pending_frame_data_->frame;
}
void Surface::TakeActiveLatencyInfo(
std::vector<ui::LatencyInfo>* latency_info) {
if (!active_frame_data_)
return;
TakeLatencyInfoFromFrame(&active_frame_data_->frame, latency_info);
}
void Surface::TakeActiveAndPendingLatencyInfo(
std::vector<ui::LatencyInfo>* latency_info) {
TakeActiveLatencyInfo(latency_info);
TakePendingLatencyInfo(latency_info);
is_latency_info_taken_ = true;
}
std::unique_ptr<Surface::PresentationHelper>
Surface::TakePresentationHelperForPresentNotification() {
if (active_frame_data_ &&
!active_frame_data_->will_be_notified_of_presentation) {
active_frame_data_->will_be_notified_of_presentation = true;
return std::make_unique<PresentationHelper>(
client(), active_frame_data_->frame.metadata.frame_token);
}
return nullptr;
}
void Surface::SendAckToClient() {
if (active_frame_data_)
active_frame_data_->SendAckIfNeeded(surface_client_.get());
}
void Surface::MarkAsDrawn() {
if (!active_frame_data_)
return;
active_frame_data_->frame_drawn = true;
if (surface_client_)
surface_client_->OnSurfaceWillDraw(this);
}
void Surface::NotifyAggregatedDamage(const gfx::Rect& damage_rect,
base::TimeTicks expected_display_time) {
if (!active_frame_data_ || !surface_client_)
return;
surface_client_->OnSurfaceAggregatedDamage(
this, surface_id().local_surface_id(), active_frame_data_->frame,
damage_rect, expected_display_time);
}
bool Surface::IsVideoCaptureOnFromClient() {
if (!surface_client_)
return false;
return surface_client_->IsVideoCaptureStarted();
}
std::vector<Thread> Surface::GetThreads() {
if (!surface_client_)
return {};
return surface_client_->GetThreads();
}
void Surface::UnrefFrameResourcesAndRunCallbacks(
std::optional<FrameData> frame_data) {
if (!frame_data || !surface_client_)
return;
std::vector<ReturnedResource> resources =
TransferableResource::ReturnResources(frame_data->frame.resource_list);
// No point in returning same sync token to sender.
for (auto& resource : resources)
resource.sync_token.Clear();
surface_client_->UnrefResources(std::move(resources));
frame_data->SendAckIfNeeded(surface_client_.get());
// If we won't be getting a presented notification, we'll notify the client
// when the frame is unref'd.
if (!frame_data->will_be_notified_of_presentation && surface_client_) {
surface_client_->OnSurfacePresented(frame_data->frame.metadata.frame_token,
base::TimeTicks(), gfx::SwapTimings(),
gfx::PresentationFeedback::Failure());
}
// Usually the LatencyInfo was already taken during aggregation or when the
// surface was replaced. If neither happened, terminate the LatencyInfo now.
for (ui::LatencyInfo& info : frame_data->frame.metadata.latency_info)
info.Terminate();
}
void Surface::ClearCopyRequests(bool keep_root) {
if (active_frame_data_) {
const auto& render_pass_list = GetActiveFrame().render_pass_list;
for (const auto& render_pass : render_pass_list) {
if (keep_root && render_pass == render_pass_list.back()) {
break;
}
// When the container is cleared, all copy requests within it will
// auto-send an empty result as they are being destroyed.
render_pass->copy_requests.clear();
}
}
}
void Surface::TakePendingLatencyInfo(
std::vector<ui::LatencyInfo>* latency_info) {
if (!pending_frame_data_)
return;
TakeLatencyInfoFromFrame(&pending_frame_data_->frame, latency_info);
}
// static
void Surface::TakeLatencyInfoFromFrame(
CompositorFrame* frame,
std::vector<ui::LatencyInfo>* latency_info) {
if (latency_info->empty()) {
frame->metadata.latency_info.swap(*latency_info);
return;
}
std::ranges::copy(frame->metadata.latency_info,
std::back_inserter(*latency_info));
frame->metadata.latency_info.clear();
if (!ui::LatencyInfo::Verify(*latency_info,
"Surface::TakeLatencyInfoFromFrame")) {
for (auto& info : *latency_info) {
info.Terminate();
}
latency_info->clear();
}
}
void Surface::OnWillBeDrawn() {
if (!seen_first_surface_embedding_) {
seen_first_surface_embedding_ = true;
TRACE_EVENT_WITH_FLOW2(
TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
"LocalSurfaceId.Embed.Flow",
TRACE_ID_GLOBAL(surface_info_.id().local_surface_id().embed_trace_id()),
TRACE_EVENT_FLAG_FLOW_IN, "step", "FirstSurfaceEmbedding", "surface_id",
surface_info_.id().ToString());
}
surface_manager_->SurfaceWillBeDrawn(this);
MarkAsDrawn();
}
void Surface::ActivatePendingFrameForInheritedDeadline() {
// Deadline inheritance implies that this surface was blocking the embedder,
// so there shouldn't be an active frame.
DCHECK(!HasActiveFrame());
ActivatePendingFrameForDeadline();
}
} // namespace viz