blob: a469d9e14df74170199376cdd0b36458558c641f [file] [log] [blame]
// Copyright 2016 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 "components/viz/service/frame_sinks/compositor_frame_sink_support.h"
#include <algorithm>
#include <utility>
#include "base/metrics/histogram_macros.h"
#include "base/stl_util.h"
#include "base/time/time.h"
#include "components/viz/common/frame_sinks/begin_frame_source.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/surfaces/surface_info.h"
#include "components/viz/service/display/display.h"
#include "components/viz/service/display/shared_bitmap_manager.h"
#include "components/viz/service/frame_sinks/frame_sink_manager_impl.h"
#include "components/viz/service/surfaces/surface.h"
#include "components/viz/service/surfaces/surface_reference.h"
#include "mojo/public/cpp/system/platform_handle.h"
namespace viz {
CompositorFrameSinkSupport::CompositorFrameSinkSupport(
mojom::CompositorFrameSinkClient* client,
FrameSinkManagerImpl* frame_sink_manager,
const FrameSinkId& frame_sink_id,
bool is_root,
bool needs_sync_tokens)
: client_(client),
frame_sink_manager_(frame_sink_manager),
surface_manager_(frame_sink_manager->surface_manager()),
frame_sink_id_(frame_sink_id),
surface_resource_holder_(this),
is_root_(is_root),
needs_sync_tokens_(needs_sync_tokens),
allow_copy_output_requests_(is_root),
weak_factory_(this) {
// This may result in SetBeginFrameSource() being called.
frame_sink_manager_->RegisterCompositorFrameSinkSupport(frame_sink_id_, this);
}
CompositorFrameSinkSupport::~CompositorFrameSinkSupport() {
// Unregister |this| as a BeginFrameObserver so that the
// BeginFrameSource does not call into |this| after it's deleted.
callback_received_begin_frame_ = true;
callback_received_receive_ack_ = true;
presentation_feedbacks_.clear();
SetNeedsBeginFrame(false);
// For display root surfaces the surface is no longer going to be visible.
// Make it unreachable from the top-level root.
if (referenced_local_surface_id_.has_value()) {
auto reference = MakeTopLevelRootReference(
SurfaceId(frame_sink_id_, referenced_local_surface_id_.value()));
surface_manager_->RemoveSurfaceReferences({reference});
}
if (last_activated_surface_id_.is_valid())
EvictLastActiveSurface();
if (last_created_surface_id_.is_valid())
surface_manager_->DestroySurface(last_created_surface_id_);
frame_sink_manager_->UnregisterCompositorFrameSinkSupport(frame_sink_id_);
// The display compositor has ownership of shared memory for each
// SharedBitmapId that has been reported from the client. Since the client is
// gone that memory can be freed. If we don't then it would leak.
for (const auto& id : owned_bitmaps_)
frame_sink_manager_->shared_bitmap_manager()->ChildDeletedSharedBitmap(id);
// No video capture clients should remain after calling
// UnregisterCompositorFrameSinkSupport().
DCHECK(capture_clients_.empty());
DCHECK(!added_frame_observer_);
}
void CompositorFrameSinkSupport::SetUpHitTest(
LatestLocalSurfaceIdLookupDelegate* local_surface_id_lookup_delegate) {
DCHECK(is_root_);
hit_test_aggregator_ = std::make_unique<HitTestAggregator>(
frame_sink_manager_->hit_test_manager(), frame_sink_manager_,
local_surface_id_lookup_delegate, frame_sink_id_);
}
void CompositorFrameSinkSupport::SetAggregatedDamageCallbackForTesting(
AggregatedDamageCallback callback) {
aggregated_damage_callback_ = std::move(callback);
}
void CompositorFrameSinkSupport::SetBeginFrameSource(
BeginFrameSource* begin_frame_source) {
if (begin_frame_source_ && added_frame_observer_) {
begin_frame_source_->RemoveObserver(this);
added_frame_observer_ = false;
}
begin_frame_source_ = begin_frame_source;
UpdateNeedsBeginFramesInternal();
}
void CompositorFrameSinkSupport::OnSurfaceActivated(Surface* surface) {
DCHECK(surface);
DCHECK(surface->HasActiveFrame());
const LocalSurfaceId& local_surface_id =
surface->surface_id().local_surface_id();
const LocalSurfaceId& last_activated_local_surface_id =
last_activated_surface_id_.local_surface_id();
if (!last_activated_surface_id_.is_valid() ||
local_surface_id > last_activated_local_surface_id) {
if (last_activated_surface_id_.is_valid()) {
CHECK_GE(local_surface_id.parent_sequence_number(),
last_activated_local_surface_id.parent_sequence_number());
CHECK_GE(local_surface_id.child_sequence_number(),
last_activated_local_surface_id.child_sequence_number());
Surface* prev_surface =
surface_manager_->GetSurfaceForId(last_activated_surface_id_);
DCHECK(prev_surface);
surface->SetPreviousFrameSurface(prev_surface);
surface_manager_->DestroySurface(prev_surface->surface_id());
}
last_activated_surface_id_ = surface->surface_id();
} else if (surface->surface_id() < last_activated_surface_id_) {
// We can get into a situation where a child-initiated synchronization is
// deferred until after a parent-initiated synchronization happens resulting
// in activations happening out of order. In that case, we simply discard
// the stale surface.
surface_manager_->DestroySurface(surface->surface_id());
}
DCHECK(surface->HasActiveFrame());
// Check if this is a display root surface and the SurfaceId is changing.
if (is_root_ && (!referenced_local_surface_id_ ||
*referenced_local_surface_id_ !=
last_activated_surface_id_.local_surface_id())) {
UpdateDisplayRootReference(surface);
}
MaybeEvictSurfaces();
}
void CompositorFrameSinkSupport::OnFrameTokenChanged(uint32_t frame_token) {
frame_sink_manager_->OnFrameTokenChanged(frame_sink_id_, frame_token);
}
void CompositorFrameSinkSupport::OnSurfaceProcessed(Surface* surface) {
DidReceiveCompositorFrameAck();
}
void CompositorFrameSinkSupport::OnSurfaceAggregatedDamage(
Surface* surface,
const LocalSurfaceId& local_surface_id,
const CompositorFrame& frame,
const gfx::Rect& damage_rect,
base::TimeTicks expected_display_time) {
DCHECK(!damage_rect.IsEmpty());
const gfx::Size& frame_size_in_pixels = frame.size_in_pixels();
if (aggregated_damage_callback_) {
aggregated_damage_callback_.Run(local_surface_id, frame_size_in_pixels,
damage_rect, expected_display_time);
}
for (CapturableFrameSink::Client* client : capture_clients_) {
client->OnFrameDamaged(frame_size_in_pixels, damage_rect,
expected_display_time, frame.metadata);
}
}
void CompositorFrameSinkSupport::OnSurfaceDiscarded(Surface* surface) {
if (surface->surface_id() == last_activated_surface_id_)
last_activated_surface_id_ = SurfaceId();
if (surface->surface_id() == last_created_surface_id_)
last_created_surface_id_ = SurfaceId();
}
void CompositorFrameSinkSupport::RefResources(
const std::vector<TransferableResource>& resources) {
surface_resource_holder_.RefResources(resources);
}
void CompositorFrameSinkSupport::UnrefResources(
const std::vector<ReturnedResource>& resources) {
surface_resource_holder_.UnrefResources(resources);
}
void CompositorFrameSinkSupport::ReturnResources(
const std::vector<ReturnedResource>& resources) {
if (resources.empty())
return;
if (!ack_pending_count_ && client_) {
client_->ReclaimResources(resources);
return;
}
std::copy(resources.begin(), resources.end(),
std::back_inserter(surface_returned_resources_));
}
void CompositorFrameSinkSupport::ReceiveFromChild(
const std::vector<TransferableResource>& resources) {
surface_resource_holder_.ReceiveFromChild(resources);
}
std::vector<std::unique_ptr<CopyOutputRequest>>
CompositorFrameSinkSupport::TakeCopyOutputRequests(
const LocalSurfaceId& latest_local_id) {
std::vector<std::unique_ptr<CopyOutputRequest>> results;
for (auto it = copy_output_requests_.begin();
it != copy_output_requests_.end();) {
// Requests with a non-valid local id should be satisfied as soon as
// possible.
if (!it->first.is_valid() || it->first <= latest_local_id) {
results.push_back(std::move(it->second));
it = copy_output_requests_.erase(it);
} else {
++it;
}
}
return results;
}
void CompositorFrameSinkSupport::EvictSurface(const LocalSurfaceId& id) {
DCHECK_GE(id.parent_sequence_number(), last_evicted_parent_sequence_number_);
last_evicted_parent_sequence_number_ = id.parent_sequence_number();
surface_manager_->DropTemporaryReference(SurfaceId(frame_sink_id_, id));
MaybeEvictSurfaces();
}
void CompositorFrameSinkSupport::MaybeEvictSurfaces() {
if (last_activated_surface_id_.is_valid() &&
last_activated_surface_id_.local_surface_id().parent_sequence_number() <=
last_evicted_parent_sequence_number_) {
EvictLastActiveSurface();
}
if (last_created_surface_id_.is_valid() &&
last_created_surface_id_.local_surface_id().parent_sequence_number() <=
last_evicted_parent_sequence_number_) {
surface_manager_->DestroySurface(last_created_surface_id_);
last_created_surface_id_ = SurfaceId();
}
}
void CompositorFrameSinkSupport::EvictLastActiveSurface() {
SurfaceId to_destroy_surface_id = last_activated_surface_id_;
if (last_created_surface_id_ == last_activated_surface_id_)
last_created_surface_id_ = SurfaceId();
last_activated_surface_id_ = SurfaceId();
surface_manager_->DestroySurface(to_destroy_surface_id);
// For display root surfaces the surface is no longer going to be visible.
// Make it unreachable from the top-level root.
if (referenced_local_surface_id_.has_value()) {
auto reference = MakeTopLevelRootReference(
SurfaceId(frame_sink_id_, referenced_local_surface_id_.value()));
surface_manager_->RemoveSurfaceReferences({reference});
referenced_local_surface_id_.reset();
}
}
void CompositorFrameSinkSupport::SetNeedsBeginFrame(bool needs_begin_frame) {
client_needs_begin_frame_ = needs_begin_frame;
UpdateNeedsBeginFramesInternal();
}
void CompositorFrameSinkSupport::SetWantsAnimateOnlyBeginFrames() {
wants_animate_only_begin_frames_ = true;
}
bool CompositorFrameSinkSupport::WantsAnimateOnlyBeginFrames() const {
return wants_animate_only_begin_frames_;
}
void CompositorFrameSinkSupport::DidNotProduceFrame(const BeginFrameAck& ack) {
TRACE_EVENT2("viz", "CompositorFrameSinkSupport::DidNotProduceFrame",
"ack.source_id", ack.source_id, "ack.sequence_number",
ack.sequence_number);
DCHECK_GE(ack.sequence_number, BeginFrameArgs::kStartingFrameNumber);
// Override the has_damage flag (ignoring invalid data from clients).
BeginFrameAck modified_ack(ack);
modified_ack.has_damage = false;
if (last_activated_surface_id_.is_valid())
surface_manager_->SurfaceModified(last_activated_surface_id_, modified_ack);
if (begin_frame_source_)
begin_frame_source_->DidFinishFrame(this);
}
void CompositorFrameSinkSupport::SubmitCompositorFrame(
const LocalSurfaceId& local_surface_id,
CompositorFrame frame,
base::Optional<HitTestRegionList> hit_test_region_list,
uint64_t submit_time) {
const auto result = MaybeSubmitCompositorFrame(
local_surface_id, std::move(frame), std::move(hit_test_region_list),
submit_time,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback());
DCHECK_EQ(result, SubmitResult::ACCEPTED);
}
bool CompositorFrameSinkSupport::DidAllocateSharedBitmap(
mojo::ScopedSharedBufferHandle buffer,
const SharedBitmapId& id) {
if (!frame_sink_manager_->shared_bitmap_manager()->ChildAllocatedSharedBitmap(
std::move(buffer), id))
return false;
owned_bitmaps_.insert(id);
return true;
}
void CompositorFrameSinkSupport::DidDeleteSharedBitmap(
const SharedBitmapId& id) {
frame_sink_manager_->shared_bitmap_manager()->ChildDeletedSharedBitmap(id);
owned_bitmaps_.erase(id);
}
SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrameInternal(
const LocalSurfaceId& local_surface_id,
CompositorFrame frame,
base::Optional<HitTestRegionList> hit_test_region_list,
uint64_t submit_time,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback callback) {
TRACE_EVENT1("viz", "CompositorFrameSinkSupport::MaybeSubmitCompositorFrame",
"FrameSinkId", frame_sink_id_.ToString());
TRACE_EVENT_WITH_FLOW1(
"viz,benchmark", "Graphics.Pipeline",
TRACE_ID_GLOBAL(frame.metadata.begin_frame_ack.trace_id),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
"ReceiveCompositorFrame");
TRACE_EVENT_FLOW_END0(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"),
"SubmitCompositorFrame", local_surface_id.hash());
bool tracing_enabled;
TRACE_EVENT_CATEGORY_GROUP_ENABLED(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"),
&tracing_enabled);
if (tracing_enabled) {
base::TimeDelta elapsed = base::TimeTicks::Now().since_origin() -
base::TimeDelta::FromMicroseconds(submit_time);
TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("cc.debug.ipc"),
"SubmitCompositorFrame::TimeElapsed",
TRACE_EVENT_SCOPE_THREAD,
"elapsed time:", elapsed.InMicroseconds());
}
DCHECK(local_surface_id.is_valid());
DCHECK(!frame.render_pass_list.empty());
DCHECK(!frame.size_in_pixels().IsEmpty());
CHECK(callback_received_begin_frame_);
CHECK(callback_received_receive_ack_);
++ack_pending_count_;
base::ScopedClosureRunner frame_rejected_callback(
base::BindOnce(&CompositorFrameSinkSupport::DidRejectCompositorFrame,
weak_factory_.GetWeakPtr(), frame.metadata.frame_token,
frame.resource_list));
compositor_frame_callback_ = std::move(callback);
if (compositor_frame_callback_) {
callback_received_begin_frame_ = false;
callback_received_receive_ack_ = false;
UpdateNeedsBeginFramesInternal();
}
// Ensure no CopyOutputRequests have been submitted if they are banned.
if (!allow_copy_output_requests_ && frame.HasCopyOutputRequests()) {
TRACE_EVENT_INSTANT0("viz", "CopyOutputRequests not allowed",
TRACE_EVENT_SCOPE_THREAD);
return SubmitResult::COPY_OUTPUT_REQUESTS_NOT_ALLOWED;
}
// TODO(crbug.com/846739): It should be possible to use
// |frame.metadata.frame_token| instead of maintaining a |last_frame_index_|.
uint64_t frame_index = ++last_frame_index_;
// Override the has_damage flag (ignoring invalid data from clients).
frame.metadata.begin_frame_ack.has_damage = true;
DCHECK_LE(BeginFrameArgs::kStartingFrameNumber,
frame.metadata.begin_frame_ack.sequence_number);
if (!ui::LatencyInfo::Verify(frame.metadata.latency_info,
"RenderWidgetHostImpl::OnSwapCompositorFrame")) {
std::vector<ui::LatencyInfo>().swap(frame.metadata.latency_info);
}
for (ui::LatencyInfo& latency : frame.metadata.latency_info) {
if (latency.latency_components().size() > 0) {
latency.AddLatencyNumber(ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT);
}
}
Surface* prev_surface =
surface_manager_->GetSurfaceForId(last_created_surface_id_);
Surface* current_surface = nullptr;
if (prev_surface &&
local_surface_id == last_created_surface_id_.local_surface_id()) {
current_surface = prev_surface;
} else {
TRACE_EVENT_WITH_FLOW2(
TRACE_DISABLED_BY_DEFAULT("viz.surface_id_flow"),
"LocalSurfaceId.Submission.Flow",
TRACE_ID_GLOBAL(local_surface_id.submission_trace_id()),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT, "step",
"ReceiveCompositorFrame", "local_surface_id",
local_surface_id.ToString());
SurfaceId surface_id(frame_sink_id_, local_surface_id);
SurfaceInfo surface_info(surface_id, frame.device_scale_factor(),
frame.size_in_pixels());
// LocalSurfaceIds should be monotonically increasing. This ID is used
// to determine the freshness of a surface at aggregation time.
const LocalSurfaceId& last_created_local_surface_id =
last_created_surface_id_.local_surface_id();
bool last_surface_has_dependent_frame =
prev_surface && prev_surface->HasDependentFrame();
bool child_initiated_synchronization_event =
last_created_local_surface_id.is_valid() &&
local_surface_id.child_sequence_number() >
last_created_local_surface_id.child_sequence_number();
// Neither sequence numbers of the LocalSurfaceId can decrease and at least
// one must increase.
bool monotonically_increasing_id =
(local_surface_id.parent_sequence_number() >=
last_created_local_surface_id.parent_sequence_number() &&
local_surface_id.child_sequence_number() >=
last_created_local_surface_id.child_sequence_number()) &&
(local_surface_id.parent_sequence_number() >
last_created_local_surface_id.parent_sequence_number() ||
child_initiated_synchronization_event);
if (!surface_info.is_valid() || !monotonically_increasing_id) {
TRACE_EVENT_INSTANT0("viz", "Surface Invariants Violation",
TRACE_EVENT_SCOPE_THREAD);
return SubmitResult::SURFACE_INVARIANTS_VIOLATION;
}
// If the last Surface doesn't have a dependent frame, and this frame
// corresponds to a child-initiated synchronization event then defer this
// Surface until a dependent frame arrives. This throttles child submission
// of CompositorFrames to the parent's embedding rate.
const bool block_activation_on_parent =
child_initiated_synchronization_event &&
!last_surface_has_dependent_frame;
// Don't recreate a surface that was previously evicted. Drop the
// CompositorFrame and return all its resources.
if (local_surface_id.parent_sequence_number() <=
last_evicted_parent_sequence_number_) {
TRACE_EVENT_INSTANT0("viz", "Submit rejected to evicted surface",
TRACE_EVENT_SCOPE_THREAD);
return SubmitResult::ACCEPTED;
}
current_surface = CreateSurface(surface_info, block_activation_on_parent);
last_created_surface_id_ = SurfaceId(frame_sink_id_, local_surface_id);
if (!current_surface) {
TRACE_EVENT_INSTANT0("viz", "Surface Invariants Violation",
TRACE_EVENT_SCOPE_THREAD);
return SubmitResult::SURFACE_INVARIANTS_VIOLATION;
}
surface_manager_->SurfaceDamageExpected(current_surface->surface_id(),
last_begin_frame_args_);
}
const int64_t trace_id = ~frame.metadata.begin_frame_ack.trace_id;
TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("viz.hit_testing_flow"),
"Event.Pipeline", TRACE_ID_GLOBAL(trace_id),
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
"step", "ReceiveHitTestData");
// QueueFrame can fail in unit tests, so SubmitHitTestRegionList has to be
// called before that.
frame_sink_manager()->SubmitHitTestRegionList(
last_created_surface_id_, frame_index, std::move(hit_test_region_list));
bool result = current_surface->QueueFrame(
std::move(frame), frame_index, std::move(frame_rejected_callback),
base::BindOnce(&CompositorFrameSinkSupport::DidPresentCompositorFrame,
weak_factory_.GetWeakPtr(), frame.metadata.frame_token));
if (!result) {
TRACE_EVENT_INSTANT0("viz", "QueueFrame failed", TRACE_EVENT_SCOPE_THREAD);
return SubmitResult::SURFACE_INVARIANTS_VIOLATION;
}
if (begin_frame_source_)
begin_frame_source_->DidFinishFrame(this);
return SubmitResult::ACCEPTED;
}
SurfaceReference CompositorFrameSinkSupport::MakeTopLevelRootReference(
const SurfaceId& surface_id) {
return SurfaceReference(surface_manager_->GetRootSurfaceId(), surface_id);
}
void CompositorFrameSinkSupport::HandleCallback() {
if (!compositor_frame_callback_ || !callback_received_begin_frame_ ||
!callback_received_receive_ack_) {
return;
}
std::move(compositor_frame_callback_)
.Run(std::move(surface_returned_resources_));
surface_returned_resources_.clear();
}
void CompositorFrameSinkSupport::DidReceiveCompositorFrameAck() {
DCHECK_GT(ack_pending_count_, 0);
ack_pending_count_--;
if (!client_)
return;
// If we have a callback, we only return the resource on onBeginFrame.
if (compositor_frame_callback_) {
callback_received_receive_ack_ = true;
UpdateNeedsBeginFramesInternal();
HandleCallback();
return;
}
client_->DidReceiveCompositorFrameAck(surface_returned_resources_);
surface_returned_resources_.clear();
}
void CompositorFrameSinkSupport::DidPresentCompositorFrame(
uint32_t presentation_token,
const gfx::PresentationFeedback& feedback) {
DCHECK(presentation_token);
DCHECK(!presentation_feedbacks_.count(presentation_token));
presentation_feedbacks_.emplace(presentation_token, feedback);
UpdateNeedsBeginFramesInternal();
}
void CompositorFrameSinkSupport::DidRejectCompositorFrame(
uint32_t presentation_token,
std::vector<TransferableResource> frame_resource_list) {
std::vector<ReturnedResource> resources =
TransferableResource::ReturnResources(frame_resource_list);
ReturnResources(resources);
DidReceiveCompositorFrameAck();
DidPresentCompositorFrame(presentation_token,
gfx::PresentationFeedback::Failure());
}
void CompositorFrameSinkSupport::UpdateDisplayRootReference(
const Surface* surface) {
// Make the new SurfaceId reachable from the top-level root.
surface_manager_->AddSurfaceReferences(
{MakeTopLevelRootReference(surface->surface_id())});
// Make the old SurfaceId unreachable from the top-level root if applicable.
if (referenced_local_surface_id_) {
surface_manager_->RemoveSurfaceReferences({MakeTopLevelRootReference(
SurfaceId(frame_sink_id_, *referenced_local_surface_id_))});
}
referenced_local_surface_id_ = surface->surface_id().local_surface_id();
}
void CompositorFrameSinkSupport::OnBeginFrame(const BeginFrameArgs& args) {
if (last_activated_surface_id_.is_valid())
surface_manager_->SurfaceDamageExpected(last_activated_surface_id_, args);
last_begin_frame_args_ = args;
if (compositor_frame_callback_) {
callback_received_begin_frame_ = true;
UpdateNeedsBeginFramesInternal();
HandleCallback();
}
if (client_ && ShouldSendBeginFrame(args.frame_time)) {
BeginFrameArgs copy_args = args;
copy_args.trace_id = ComputeTraceId();
TRACE_EVENT_WITH_FLOW1("viz,benchmark", "Graphics.Pipeline",
TRACE_ID_GLOBAL(copy_args.trace_id),
TRACE_EVENT_FLAG_FLOW_OUT, "step",
"IssueBeginFrame");
last_frame_time_ = args.frame_time;
client_->OnBeginFrame(copy_args, std::move(presentation_feedbacks_));
presentation_feedbacks_.clear();
UpdateNeedsBeginFramesInternal();
}
}
const BeginFrameArgs& CompositorFrameSinkSupport::LastUsedBeginFrameArgs()
const {
return last_begin_frame_args_;
}
void CompositorFrameSinkSupport::OnBeginFrameSourcePausedChanged(bool paused) {
if (client_)
client_->OnBeginFramePausedChanged(paused);
}
void CompositorFrameSinkSupport::UpdateNeedsBeginFramesInternal() {
if (!begin_frame_source_)
return;
// We require a begin frame if there's a callback pending, or if the client
// requested it, or if the client needs to get some presentation feedbacks.
bool needs_begin_frame =
client_needs_begin_frame_ || !presentation_feedbacks_.empty() ||
(compositor_frame_callback_ && !callback_received_begin_frame_);
if (needs_begin_frame == added_frame_observer_)
return;
added_frame_observer_ = needs_begin_frame;
if (needs_begin_frame)
begin_frame_source_->AddObserver(this);
else
begin_frame_source_->RemoveObserver(this);
}
Surface* CompositorFrameSinkSupport::CreateSurface(
const SurfaceInfo& surface_info,
bool block_activation_on_parent) {
return surface_manager_->CreateSurface(
weak_factory_.GetWeakPtr(), surface_info,
frame_sink_manager_->GetPrimaryBeginFrameSource(), needs_sync_tokens_,
block_activation_on_parent);
}
SubmitResult CompositorFrameSinkSupport::MaybeSubmitCompositorFrame(
const LocalSurfaceId& local_surface_id,
CompositorFrame frame,
base::Optional<HitTestRegionList> hit_test_region_list,
uint64_t submit_time,
mojom::CompositorFrameSink::SubmitCompositorFrameSyncCallback callback) {
SubmitResult result = MaybeSubmitCompositorFrameInternal(
local_surface_id, std::move(frame), std::move(hit_test_region_list),
submit_time, std::move(callback));
UMA_HISTOGRAM_ENUMERATION(
"Compositing.CompositorFrameSinkSupport.SubmitResult", result);
return result;
}
void CompositorFrameSinkSupport::AttachCaptureClient(
CapturableFrameSink::Client* client) {
DCHECK(!base::ContainsValue(capture_clients_, client));
capture_clients_.push_back(client);
}
void CompositorFrameSinkSupport::DetachCaptureClient(
CapturableFrameSink::Client* client) {
const auto it =
std::find(capture_clients_.begin(), capture_clients_.end(), client);
if (it != capture_clients_.end())
capture_clients_.erase(it);
}
gfx::Size CompositorFrameSinkSupport::GetActiveFrameSize() {
if (last_activated_surface_id_.is_valid()) {
Surface* current_surface =
surface_manager_->GetSurfaceForId(last_activated_surface_id_);
DCHECK(current_surface);
if (current_surface->HasActiveFrame()) {
DCHECK(current_surface->GetActiveFrame().size_in_pixels() ==
current_surface->size_in_pixels());
return current_surface->size_in_pixels();
}
}
return gfx::Size();
}
void CompositorFrameSinkSupport::RequestCopyOfOutput(
const LocalSurfaceId& local_surface_id,
std::unique_ptr<CopyOutputRequest> copy_request) {
copy_output_requests_.push_back(
std::make_pair(local_surface_id, std::move(copy_request)));
if (last_activated_surface_id_.is_valid()) {
BeginFrameAck ack;
ack.has_damage = true;
surface_manager_->SurfaceModified(last_activated_surface_id_, ack);
}
}
const CompositorFrameMetadata*
CompositorFrameSinkSupport::GetLastActivatedFrameMetadata() {
if (!last_activated_surface_id_.is_valid())
return nullptr;
Surface* surface =
surface_manager_->GetSurfaceForId(last_activated_surface_id_);
DCHECK(surface);
return &surface->GetActiveFrame().metadata;
}
HitTestAggregator* CompositorFrameSinkSupport::GetHitTestAggregator() {
DCHECK(is_root_);
return hit_test_aggregator_.get();
}
Surface* CompositorFrameSinkSupport::GetLastCreatedSurfaceForTesting() {
return surface_manager_->GetSurfaceForId(last_created_surface_id_);
}
// static
const char* CompositorFrameSinkSupport::GetSubmitResultAsString(
SubmitResult result) {
switch (result) {
case SubmitResult::ACCEPTED:
return "Accepted";
case SubmitResult::COPY_OUTPUT_REQUESTS_NOT_ALLOWED:
return "CopyOutputRequests not allowed";
case SubmitResult::SURFACE_INVARIANTS_VIOLATION:
return "Surface invariants violation";
}
NOTREACHED();
return nullptr;
}
int64_t CompositorFrameSinkSupport::ComputeTraceId() {
// This is losing some info, but should normally be sufficient to avoid
// collisions.
++trace_sequence_;
uint64_t client = (frame_sink_id_.client_id() & 0xffff);
uint64_t sink = (frame_sink_id_.sink_id() & 0xffff);
return (client << 48) | (sink << 32) | trace_sequence_;
}
bool CompositorFrameSinkSupport::ShouldSendBeginFrame(
base::TimeTicks frame_time) {
// If there are pending presentation feedbacks from the previous frame(s),
// then the client needs to receive the begin-frame.
if (!presentation_feedbacks_.empty())
return true;
if (!client_needs_begin_frame_)
return false;
if (!last_activated_surface_id_.is_valid())
return true;
Surface* surface =
surface_manager_->GetSurfaceForId(last_activated_surface_id_);
// If client has not submitted any frames, or the first frame submitted is
// yet to be embedded, then allow the begin-frame to be dispatched to the
// client.
if (!surface || !surface->seen_first_surface_embedding())
return true;
// Send begin-frames if the client has requested for it, and the previously
// submitted frame has already been drawn, or if the previous begin-frame was
// sent more than 1 second ago.
constexpr base::TimeDelta throttled_rate = base::TimeDelta::FromSeconds(1);
return !surface->HasUndrawnActiveFrame() ||
(frame_time - last_frame_time_) >= throttled_rate;
}
} // namespace viz