// Copyright 2017 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/exo/surface_tree_host.h"
#include <algorithm>
#include <utility>
#include <vector>
#include "ash/shell.h"
#include "base/check.h"
#include "base/memory/raw_ptr.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "cc/mojo_embedder/async_layer_tree_frame_sink.h"
#include "cc/trees/layer_tree_frame_sink.h"
#include "chromeos/ui/base/window_properties.h"
#include "components/exo/layer_tree_frame_sink_holder.h"
#include "components/exo/shell_surface_base.h"
#include "components/exo/shell_surface_util.h"
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
#include "components/viz/common/gpu/raster_context_provider.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/quads/compositor_frame_metadata.h"
#include "components/viz/common/quads/compositor_render_pass.h"
#include "components/viz/common/quads/shared_quad_state.h"
#include "components/viz/common/quads/solid_color_draw_quad.h"
#include "components/viz/common/resources/transferable_resource.h"
#include "components/viz/common/surfaces/child_local_surface_id_allocator.h"
#include "components/viz/host/host_frame_sink_manager.h"
#include "gpu/command_buffer/client/raster_interface.h"
#include "third_party/skia/include/core/SkPath.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_occlusion_tracker.h"
#include "ui/aura/window_targeter.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/cursor/cursor.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/display/types/display_constants.h"
#include "ui/gfx/color_space.h"
#include "ui/gfx/display_color_spaces.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/geometry/point_f.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
#include "ui/gfx/geometry/rrect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/size_f.h"
#include "ui/gfx/presentation_feedback.h"
namespace exo {
BASE_FEATURE(kExoDisableBeginFrameAcks,
"ExoDisableBeginFrameAcks",
base::FEATURE_ENABLED_BY_DEFAULT);
namespace {
class CustomWindowTargeter : public aura::WindowTargeter {
public:
explicit CustomWindowTargeter(SurfaceTreeHost* surface_tree_host)
: surface_tree_host_(surface_tree_host) {}
CustomWindowTargeter(const CustomWindowTargeter&) = delete;
CustomWindowTargeter& operator=(const CustomWindowTargeter&) = delete;
~CustomWindowTargeter() override = default;
// Overridden from aura::WindowTargeter:
bool EventLocationInsideBounds(aura::Window* window,
const ui::LocatedEvent& event) const override {
if (window != surface_tree_host_->host_window())
return aura::WindowTargeter::EventLocationInsideBounds(window, event);
Surface* surface = surface_tree_host_->root_surface();
if (!surface)
return false;
gfx::Point local_point =
ConvertEventLocationToWindowCoordinates(window, event);
aura::Window::ConvertPointToTarget(window, surface->window(), &local_point);
return surface->HitTest(local_point);
}
ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
ui::Event* event) override {
aura::Window* window = static_cast<aura::Window*>(root);
if (window != surface_tree_host_->host_window())
return aura::WindowTargeter::FindTargetForEvent(root, event);
ui::EventTarget* target =
aura::WindowTargeter::FindTargetForEvent(root, event);
// Do not accept events in SurfaceTreeHost window.
return target != root ? target : nullptr;
}
private:
const raw_ptr<SurfaceTreeHost> surface_tree_host_;
};
} // namespace
////////////////////////////////////////////////////////////////////////////////
// SurfaceTreeHost, public:
SurfaceTreeHost::SurfaceTreeHost(const std::string& window_name)
: SurfaceTreeHost(window_name, nullptr) {}
SurfaceTreeHost::SurfaceTreeHost(const std::string& window_name,
std::unique_ptr<aura::Window> host_window)
: host_window_(host_window ? std::move(host_window)
: std::make_unique<aura::Window>(
nullptr,
aura::client::WINDOW_TYPE_CONTROL)),
frame_sink_holder_factory_(
base::BindRepeating(&SurfaceTreeHost::CreateLayerTreeFrameSinkHolder,
base::Unretained(this))) {
InitHostWindow(window_name);
context_provider_ = aura::Env::GetInstance()
->context_factory()
->SharedMainThreadRasterContextProvider();
DCHECK(context_provider_);
context_provider_->AddObserver(this);
display_manager_observation_.Observe(ash::Shell::Get()->display_manager());
}
SurfaceTreeHost::~SurfaceTreeHost() {
context_provider_->RemoveObserver(this);
SetRootSurface(nullptr);
LayerTreeFrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(layer_tree_frame_sink_holder_));
CleanUpCallbacks();
if (frame_sink_id_.is_valid()) {
auto* context_factory = aura::Env::GetInstance()->context_factory();
auto* host_frame_sink_manager = context_factory->GetHostFrameSinkManager();
host_frame_sink_manager->InvalidateFrameSinkId(frame_sink_id_,
host_window());
}
}
void SurfaceTreeHost::SetRootSurface(Surface* root_surface) {
if (root_surface == root_surface_)
return;
// This method applies multiple changes to the window tree. Use ScopedPause to
// ensure that occlusion isn't recomputed before all changes have been
// applied.
aura::WindowOcclusionTracker::ScopedPause pause_occlusion;
if (root_surface_) {
root_surface_->window()->Hide();
host_window_->RemoveChild(root_surface_->window());
viz::ScopedSurfaceIdAllocator scoped_suppression =
host_window()->GetSurfaceIdAllocator(base::NullCallback());
host_window_->SetBounds(
gfx::Rect(host_window_->bounds().origin(), gfx::Size()));
AllocateLocalSurfaceId();
if (!layer_tree_frame_sink_holder_->is_lost()) {
layer_tree_frame_sink_holder_->SetLocalSurfaceId(
GetCurrentLocalSurfaceId());
}
MaybeActivateSurface();
root_surface_->SetSurfaceDelegate(nullptr);
// Force recreating resources when the surface is added to a tree again.
root_surface_->SurfaceHierarchyResourcesLost();
root_surface_ = nullptr;
}
if (root_surface) {
root_surface_ = root_surface;
// If lacros happens to be below the version where augmented_surface changed
// to compositing-only, `root_surface_` will be augmented, and its
// content_size will be ignored, producing empty bounds. Hence, set
// set_is_augmented(false) forcibly.
// TODO(crbug.com/40058249): Remove when lacros/ash version skew
// window is passed.
root_surface_->set_is_augmented(false);
root_surface_->SetSurfaceDelegate(this);
if (client_submits_surfaces_in_pixel_coordinates_) {
SetScaleFactorTransform(GetScaleFactor());
}
host_window_->AddChild(root_surface_->window());
UpdateSurfaceLayerSizeAndRootSurfaceOrigin();
}
set_bounds_is_dirty(true);
}
bool SurfaceTreeHost::HasHitTestRegion() const {
return root_surface_ && root_surface_->HasHitTestRegion();
}
void SurfaceTreeHost::GetHitTestMask(SkPath* mask) const {
if (root_surface_)
root_surface_->GetHitTestMask(mask);
}
void SurfaceTreeHost::DidReceiveCompositorFrameAck() {
const base::TimeTicks now = base::TimeTicks::Now();
for (auto& callback : frame_callbacks_.front()) {
callback.Run(now);
}
frame_callbacks_.pop();
}
void SurfaceTreeHost::DidPresentCompositorFrame(
uint32_t presentation_token,
const gfx::PresentationFeedback& feedback) {
auto it = active_presentation_callbacks_.find(presentation_token);
if (it == active_presentation_callbacks_.end())
return;
for (auto callback : it->second)
callback.Run(feedback);
active_presentation_callbacks_.erase(it);
}
void SurfaceTreeHost::SetScaleFactor(float scale_factor) {
pending_scale_factor_ = scale_factor;
}
void SurfaceTreeHost::SetSecurityDelegate(SecurityDelegate* security_delegate) {
DCHECK(security_delegate_ == nullptr);
security_delegate_ = security_delegate;
}
void SurfaceTreeHost::SubmitCompositorFrameForTesting(
viz::CompositorFrame frame) {
// Make sure that every submission has an entry pushed into
// `frame_callbacks_`, which will be pop when ack is received.
frame_callbacks_.emplace();
active_presentation_callbacks_[frame.metadata.frame_token] =
PresentationCallbacks();
layer_tree_frame_sink_holder_->SubmitCompositorFrame(std::move(frame));
}
void SurfaceTreeHost::SetLayerTreeFrameSinkHolderFactoryForTesting(
LayerTreeFrameSinkHolderFactory frame_sink_holder_factory) {
DCHECK(frame_callbacks_.empty() && active_presentation_callbacks_.empty());
frame_sink_holder_factory_ = std::move(frame_sink_holder_factory);
layer_tree_frame_sink_holder_ = frame_sink_holder_factory_.Run();
}
////////////////////////////////////////////////////////////////////////////////
// SurfaceDelegate overrides:
void SurfaceTreeHost::OnSurfaceCommit() {
root_surface_->CommitSurfaceHierarchy(false);
UpdateSurfaceLayerSizeAndRootSurfaceOrigin();
}
bool SurfaceTreeHost::IsSurfaceSynchronized() const {
// To host a surface tree, the root surface has to be desynchronized.
DCHECK(root_surface_);
return false;
}
bool SurfaceTreeHost::IsInputEnabled(Surface*) const {
return true;
}
void SurfaceTreeHost::OnNewOutputAdded() {
UpdateDisplayOnTree();
}
SecurityDelegate* SurfaceTreeHost::GetSecurityDelegate() {
DCHECK(security_delegate_);
return security_delegate_;
}
////////////////////////////////////////////////////////////////////////////////
// display::DisplayManagerObserver:
void SurfaceTreeHost::OnDidProcessDisplayChanges(
const DisplayConfigurationChange& configuration_change) {
// The output of the surface may change when the primary display changes.
const bool primary_changed = base::ranges::any_of(
configuration_change.display_metrics_changes,
[](const DisplayManagerObserver::DisplayMetricsChange& change) {
return change.changed_metrics &
display::DisplayObserver::DISPLAY_METRIC_PRIMARY;
});
if (primary_changed) {
UpdateDisplayOnTree();
}
}
////////////////////////////////////////////////////////////////////////////////
// viz::ContextLostObserver overrides:
void SurfaceTreeHost::OnContextLost() {
// Handle context loss in a new stack frame to avoid bugs from re-entrant
// code.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&SurfaceTreeHost::HandleContextLost,
weak_ptr_factory_.GetWeakPtr()));
}
void SurfaceTreeHost::OnFrameSinkLost() {
// HandleContextLost() may happen during this period. If the frame_sink is
// still lost after 16ms, we need to resubmit to avoid blank content.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&SurfaceTreeHost::HandleFrameSinkLost,
weak_ptr_factory_.GetWeakPtr()),
base::Milliseconds(16));
}
////////////////////////////////////////////////////////////////////////////////
// SurfaceTreeHost, protected:
void SurfaceTreeHost::UpdateDisplayOnTree() {
auto display =
display::Screen::GetScreen()->GetDisplayNearestWindow(host_window());
if (output_display_id_ != display.id()) {
if (root_surface_) {
if (root_surface_->UpdateDisplay(output_display_id_, display.id())) {
output_display_id_ = display.id();
} else {
// The surface failed to update to the new display.
// Invalidate cached display id, so the surface always gets updated
// next time, even when it gets updated back to the previous display.
output_display_id_ = display::kInvalidDisplayId;
}
}
}
}
void SurfaceTreeHost::WillCommit() {
scale_factor_ = pending_scale_factor_;
}
void SurfaceTreeHost::SubmitCompositorFrame() {
viz::CompositorFrame frame = PrepareToSubmitCompositorFrame();
// TODO(1041932,1034876): Remove or early return once these issues
// are fixed or identified.
if (frame.size_in_pixels().IsEmpty()) {
aura::Window* toplevel = root_surface_->window()->GetToplevelWindow();
auto app_type = toplevel->GetProperty(chromeos::kAppTypeKey);
const std::string* app_id = GetShellApplicationId(toplevel);
const std::string* startup_id = GetShellStartupId(toplevel);
auto* shell_surface = GetShellSurfaceBaseForWindow(toplevel);
CHECK(!frame.size_in_pixels().IsEmpty())
<< " Title=" << shell_surface->GetWindowTitle()
<< ", AppType=" << static_cast<int>(app_type)
<< ", AppId=" << (app_id ? *app_id : "''")
<< ", StartupId=" << (startup_id ? *startup_id : "''");
}
const int64_t frame_trace_id = root_surface_->GetFrameTraceId();
if (frame_trace_id != -1) {
frame.metadata.begin_frame_ack.trace_id = frame_trace_id;
TRACE_EVENT_INSTANT(
"viz,benchmark,graphics.pipeline", "Graphics.Pipeline",
perfetto::Flow::Global(frame_trace_id),
[frame_trace_id](perfetto::EventContext ctx) {
auto* event = ctx.event<perfetto::protos::pbzero::ChromeTrackEvent>();
auto* data = event->set_chrome_graphics_pipeline();
data->set_step(perfetto::protos::pbzero::ChromeGraphicsPipeline::
StepName::STEP_EXO_CONSTRUCT_COMPOSITOR_FRAME);
data->set_display_trace_id(frame_trace_id);
});
}
std::list<Surface::FrameCallback> current_frame_callbacks;
PresentationCallbacks presentation_callbacks;
root_surface_->AppendSurfaceHierarchyCallbacks(¤t_frame_callbacks,
&presentation_callbacks);
frame_callbacks_.push(std::move(current_frame_callbacks));
const uint32_t frame_token = frame.metadata.frame_token;
DCHECK_EQ(active_presentation_callbacks_.count(frame_token), 0u);
active_presentation_callbacks_[frame_token] =
std::move(presentation_callbacks);
root_surface_->AppendSurfaceHierarchyContentsToFrame(
gfx::PointF(root_surface_origin_pixel_), /*to_parent_dp=*/gfx::PointF(),
layer_tree_frame_sink_holder_->NeedsFullDamageForNextFrame(),
layer_tree_frame_sink_holder_->resource_manager(),
client_submits_surfaces_in_pixel_coordinates()
? std::nullopt
: std::make_optional(GetScaleFactor()),
&frame);
// Update after resource is updated.
UpdateHostLayerOpacity();
std::vector<GLbyte*> sync_tokens;
// We track previously verified tokens and set them to be verified to avoid
// the considerable overhead of flush verification in
// 'VerifySyncTokensCHROMIUM'.
for (auto& resource : frame.resource_list) {
// Copy the token and set it as flush verified as the tokens, which
// |prev_frame_verified_tokens| has, have that flag set. If that is not done
// locally here, the comparison of the tokens fails as all fields of each
// tokens are compared during ::find().
auto tmp_sync_token = resource.sync_token();
tmp_sync_token.SetVerifyFlush();
if (prev_frame_verified_tokens_.find(tmp_sync_token) !=
prev_frame_verified_tokens_.end()) {
resource.mutable_sync_token().SetVerifyFlush();
}
sync_tokens.push_back(resource.mutable_sync_token().GetData());
}
gpu::InterfaceBase* rii = context_provider_->RasterInterface();
rii->VerifySyncTokensCHROMIUM(sync_tokens.data(), sync_tokens.size());
prev_frame_verified_tokens_.clear();
frame.metadata.content_color_usage = gfx::ContentColorUsage::kSRGB;
for (auto& resource : frame.resource_list) {
if (resource.sync_token().verified_flush()) {
prev_frame_verified_tokens_.insert(resource.sync_token());
}
frame.metadata.content_color_usage =
std::max(frame.metadata.content_color_usage,
resource.color_space.GetContentColorUsage());
}
frame.metadata.may_contain_video = root_surface_->ContainsVideo();
layer_tree_frame_sink_holder_->SubmitCompositorFrame(std::move(frame));
}
void SurfaceTreeHost::SubmitEmptyCompositorFrame() {
viz::CompositorFrame frame = PrepareToSubmitCompositorFrame();
const std::unique_ptr<viz::CompositorRenderPass>& render_pass =
frame.render_pass_list.back();
const gfx::Rect quad_rect = gfx::Rect(0, 0, 1, 1);
viz::SharedQuadState* quad_state =
render_pass->CreateAndAppendSharedQuadState();
quad_state->SetAll(gfx::Transform(), /*layer_rect=*/quad_rect,
/*visible_layer_rect=*/quad_rect,
/*filter_info=*/gfx::MaskFilterInfo(),
/*clip=*/std::nullopt,
/*contents_opaque=*/true, /*opacity_f=*/1.f,
/*blend=*/SkBlendMode::kSrcOver, /*sorting_context=*/0,
/*layer_id=*/0u, /*fast_rounded_corner=*/false);
viz::SolidColorDrawQuad* solid_quad =
render_pass->CreateAndAppendDrawQuad<viz::SolidColorDrawQuad>();
solid_quad->SetNew(quad_state, quad_rect, quad_rect, SkColors::kBlack,
/*anti_aliasing_off=*/false);
// Make sure that every submission has an entry pushed into
// `frame_callbacks_`, which will be pop when ack is received.
frame_callbacks_.emplace();
active_presentation_callbacks_[frame.metadata.frame_token] =
PresentationCallbacks();
layer_tree_frame_sink_holder_->SubmitCompositorFrame(std::move(frame),
/*submit_now=*/true);
}
void SurfaceTreeHost::UpdateSurfaceLayerSizeAndRootSurfaceOrigin() {
// This method applies multiple changes to the window tree. Use ScopedPause
// to ensure that occlusion isn't recomputed before all changes have been
// applied.
aura::WindowOcclusionTracker::ScopedPause pause_occlusion;
viz::ScopedSurfaceIdAllocator scoped_suppression =
host_window()->GetSurfaceIdAllocator(base::NullCallback());
ui::Layer* commit_target_layer = GetCommitTargetLayer();
const gfx::Rect& bounds = root_surface_->surface_hierarchy_content_bounds();
gfx::Size size = bounds.size();
if (client_submits_surfaces_in_pixel_coordinates_) {
size = gfx::ScaleToCeiledSize(size, 1.0f / GetScaleFactor());
}
gfx::Rect scaled_bounds(bounds.origin(), size);
if (commit_target_layer && scaled_bounds != commit_target_layer->bounds()) {
// DP size has changed, set new bounds.
commit_target_layer->SetBounds(
{commit_target_layer->bounds().origin(), size});
}
// TODO(yjliu): a) consolidate with ClientControlledShellSurface. b) use the
// scale factor the buffer is created for to set the transform for
// synchronization.
if (client_submits_surfaces_in_pixel_coordinates_) {
SetScaleFactorTransform(GetScaleFactor());
}
root_surface_origin_pixel_ = gfx::Point() - bounds.OffsetFromOrigin();
gfx::Point root_surface_origin_dp =
client_submits_surfaces_in_pixel_coordinates_
? ToFlooredPoint(
gfx::PointF() +
ScaleVector2d(root_surface_origin_pixel_.OffsetFromOrigin(),
1.f / GetScaleFactor()))
: root_surface_origin_pixel_;
const gfx::Rect& window_bounds = root_surface_->window()->bounds();
if (root_surface_origin_dp != window_bounds.origin()) {
// Set DP origin to root surface.
gfx::Rect updated_bounds(root_surface_origin_dp, window_bounds.size());
root_surface_->window()->SetBounds(updated_bounds);
}
UpdateHostWindowOpaqueRegion();
}
void SurfaceTreeHost::UpdateHostLayerOpacity() {
ui::Layer* commit_target_layer = GetCommitTargetLayer();
if (commit_target_layer == host_window_->layer()) {
UpdateHostWindowOpaqueRegion();
} else if (commit_target_layer) {
commit_target_layer->SetFillsBoundsOpaquely(
ContentsFillsHostWindowOpaquely());
}
}
void SurfaceTreeHost::UpdateHostWindowOpaqueRegion() {
if (ContentsFillsHostWindowOpaquely()) {
host_window_->SetOpaqueRegionsForOcclusion(
{gfx::Rect(host_window_->bounds().size())});
} else {
host_window_->SetOpaqueRegionsForOcclusion({});
}
}
////////////////////////////////////////////////////////////////////////////////
// SurfaceTreeHost, private:
bool SurfaceTreeHost::ContentsFillsHostWindowOpaquely() const {
const gfx::Rect& bounds = root_surface_->surface_hierarchy_content_bounds();
return gfx::SizeF(bounds.size()) == root_surface_->content_size() &&
root_surface_->FillsBoundsOpaquely();
}
void SurfaceTreeHost::InitHostWindow(const std::string& window_name) {
host_window_->SetName(window_name);
host_window_->Init(ui::LAYER_SOLID_COLOR);
host_window_->set_owned_by_parent(false);
host_window_->SetTransparent(true);
// The host window is a container of surface tree. It doesn't handle pointer
// events.
host_window_->SetEventTargetingPolicy(
aura::EventTargetingPolicy::kDescendantsOnly);
host_window_->SetEventTargeter(std::make_unique<CustomWindowTargeter>(this));
host_window_.get()->ui::LayerOwner::AddObserver(this);
layer_tree_frame_sink_holder_ = frame_sink_holder_factory_.Run();
}
std::unique_ptr<cc::mojo_embedder::AsyncLayerTreeFrameSink>
SurfaceTreeHost::CreateLayerTreeFrameSink() {
auto* context_factory = aura::Env::GetInstance()->context_factory();
auto* host_frame_sink_manager = context_factory->GetHostFrameSinkManager();
if (!frame_sink_id_.is_valid()) {
frame_sink_id_ = context_factory->AllocateFrameSinkId();
host_frame_sink_manager->RegisterFrameSinkId(
frame_sink_id_, host_window(), viz::ReportFirstSurfaceActivation::kNo);
host_window_->SetEmbedFrameSinkId(frame_sink_id_);
}
// For creating an async frame sink which connects to the viz display
// compositor.
mojo::PendingRemote<viz::mojom::CompositorFrameSink> sink_remote;
mojo::PendingReceiver<viz::mojom::CompositorFrameSink> sink_receiver =
sink_remote.InitWithNewPipeAndPassReceiver();
mojo::PendingRemote<viz::mojom::CompositorFrameSinkClient> client_remote;
mojo::PendingReceiver<viz::mojom::CompositorFrameSinkClient> client_receiver =
client_remote.InitWithNewPipeAndPassReceiver();
host_frame_sink_manager->CreateCompositorFrameSink(
frame_sink_id_, std::move(sink_receiver), std::move(client_remote));
cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams params;
params.gpu_memory_buffer_manager =
aura::Env::GetInstance()->context_factory()->GetGpuMemoryBufferManager();
params.pipes.compositor_frame_sink_remote = std::move(sink_remote);
params.pipes.client_receiver = std::move(client_receiver);
// Disable merge of frame acks with begin frame so that clients of exo can
// get frame callbacks and resources reclaimed as soon as possible.
if (base::FeatureList::IsEnabled(kExoDisableBeginFrameAcks)) {
params.wants_begin_frame_acks = false;
}
params.auto_needs_begin_frame =
base::FeatureList::IsEnabled(kExoReactiveFrameSubmission);
auto frame_sink =
std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>(
nullptr /* context_provider */, nullptr /* worker_context_provider */,
/*shared_image_interface=*/nullptr, ¶ms);
AllocateLocalSurfaceId();
CHECK(GetCurrentLocalSurfaceId().is_valid());
return frame_sink;
}
void SurfaceTreeHost::AllocateLocalSurfaceId() {
if (!child_local_surface_id_allocator_) {
child_local_surface_id_allocator_ =
std::make_unique<viz::ChildLocalSurfaceIdAllocator>();
child_local_surface_id_allocator_->UpdateFromParent(
host_window_->GetLocalSurfaceId());
}
child_local_surface_id_allocator_->GenerateId();
}
void SurfaceTreeHost::UpdateLocalSurfaceIdFromParent(
const viz::LocalSurfaceId& parent_local_surface_id) {
child_local_surface_id_allocator_->UpdateFromParent(parent_local_surface_id);
}
void SurfaceTreeHost::MaybeActivateSurface() {
ui::Layer* commit_target_layer = GetCommitTargetLayer();
if (!commit_target_layer) {
return;
}
if (commit_target_layer->GetSurfaceId() &&
!GetCurrentLocalSurfaceId().IsNewerThan(
commit_target_layer->GetSurfaceId()->local_surface_id())) {
return;
}
host_window_->UpdateLocalSurfaceIdFromEmbeddedClient(
GetCurrentLocalSurfaceId());
commit_target_layer->SetShowSurface(
GetSurfaceId(), commit_target_layer->bounds().size(), SK_ColorWHITE,
cc::DeadlinePolicy::UseDefaultDeadline(),
false /* stretch_content_to_fill_bounds */);
}
viz::SurfaceId SurfaceTreeHost::GetSurfaceId() const {
return viz::SurfaceId(frame_sink_id_, GetCurrentLocalSurfaceId());
}
const viz::LocalSurfaceId& SurfaceTreeHost::GetCurrentLocalSurfaceId() const {
return child_local_surface_id_allocator_->GetCurrentLocalSurfaceId();
}
ui::Layer* SurfaceTreeHost::GetCommitTargetLayer() {
return host_window_->layer();
}
const ui::Layer* SurfaceTreeHost::GetCommitTargetLayer() const {
return host_window_->layer();
}
void SurfaceTreeHost::OnLayerRecreated(ui::Layer* old_layer) {
// TODO(b/319939913): Remove this log when the issue is fixed.
old_layer->SetName(old_layer->name() + "-host");
CHECK(old_layer->parent());
CHECK(host_window()->layer()->parent());
}
viz::CompositorFrame SurfaceTreeHost::PrepareToSubmitCompositorFrame() {
DCHECK(root_surface_);
if (layer_tree_frame_sink_holder_->is_lost()) {
// We can immediately delete the old LayerTreeFrameSinkHolder because all of
// it's resources are lost anyways.
layer_tree_frame_sink_holder_ = frame_sink_holder_factory_.Run();
CleanUpCallbacks();
}
viz::CompositorFrame frame;
frame.metadata.begin_frame_ack =
viz::BeginFrameAck::CreateManualAckWithDamage();
frame.metadata.frame_token = GenerateNextFrameToken();
frame.render_pass_list.push_back(viz::CompositorRenderPass::Create());
const std::unique_ptr<viz::CompositorRenderPass>& render_pass =
frame.render_pass_list.back();
const viz::CompositorRenderPassId kRenderPassId{1};
// Compute a temporally stable (across frames) size for the render pass output
// rectangle that is consistent with the window size. It is used to set the
// size of the output surface. Note that computing the actual coverage while
// building up the render pass can lead to the size being one pixel too large,
// especially if the device scale factor has a floating point representation
// that requires many bits of precision in the mantissa, due to the coverage
// computing an "enclosing" pixel rectangle. This isn't a problem for the
// dirty rectangle, so it is updated as part of filling in the render pass.
// Additionally, we must use this size even if we are submitting an empty
// compositor frame, otherwise we may set the Surface created by Viz to be the
// wrong size. Then, trying to submit a regular compositor frame will fail
// because the size is different.
const float device_scale_factor = GetScaleFactor();
gfx::Size output_surface_size_in_pixels =
root_surface_->surface_hierarchy_content_bounds().size();
if (!client_submits_surfaces_in_pixel_coordinates_) {
// TODO(crbug.com/40150290): Should this be ceil? Why do we choose floor?
output_surface_size_in_pixels = gfx::ScaleToFlooredSize(
output_surface_size_in_pixels, device_scale_factor);
}
// Viz will crash if the frame size is empty. Ensure it's not empty.
// crbug.com/1041932.
if (output_surface_size_in_pixels.IsEmpty())
output_surface_size_in_pixels.SetSize(1, 1);
render_pass->SetNew(kRenderPassId, gfx::Rect(output_surface_size_in_pixels),
gfx::Rect(), gfx::Transform());
frame.metadata.device_scale_factor = device_scale_factor;
if (output_surface_size_in_pixels !=
layer_tree_frame_sink_holder_->LastSizeInPixels() ||
device_scale_factor !=
layer_tree_frame_sink_holder_->LastDeviceScaleFactor()) {
AllocateLocalSurfaceId();
}
layer_tree_frame_sink_holder_->SetLocalSurfaceId(GetCurrentLocalSurfaceId());
MaybeActivateSurface();
return frame;
}
void SurfaceTreeHost::HandleContextLost() {
// Stop observering the lost context.
context_provider_->RemoveObserver(this);
// Get new context and start observing it.
context_provider_ = aura::Env::GetInstance()
->context_factory()
->SharedMainThreadRasterContextProvider();
DCHECK(context_provider_);
context_provider_->AddObserver(this);
if (!GetSurfaceId().is_valid() || !root_surface_) {
return;
}
root_surface_->SurfaceHierarchyResourcesLost();
SubmitCompositorFrame();
}
void SurfaceTreeHost::HandleFrameSinkLost() {
if (!layer_tree_frame_sink_holder_->is_lost()) {
// If the frame_sink loss happens together with a context loss and
// HandleContextLost() is called first, `layer_tree_frame_sink_holder_` is
// already recreated with a compositor frame resubmitted. Skip to avoid an
// unnecessary compositor frame.
return;
}
if (!GetSurfaceId().is_valid() || !root_surface_) {
return;
}
// Resubmit compositor frame.
SubmitCompositorFrame();
}
float SurfaceTreeHost::GetScaleFactor() const {
return CalculateScaleFactor(scale_factor_);
}
float SurfaceTreeHost::GetPendingScaleFactor() const {
return CalculateScaleFactor(pending_scale_factor_);
}
bool SurfaceTreeHost::HasDoubleBufferedPendingScaleFactor() const {
return pending_scale_factor_.has_value();
}
void SurfaceTreeHost::CleanUpCallbacks() {
const base::TimeTicks now = base::TimeTicks::Now();
while (!frame_callbacks_.empty()) {
for (auto& callback : frame_callbacks_.front()) {
callback.Run(now);
}
frame_callbacks_.pop();
}
for (auto entry : active_presentation_callbacks_) {
while (!entry.second.empty()) {
entry.second.front().Run(gfx::PresentationFeedback());
entry.second.pop_front();
}
}
active_presentation_callbacks_.clear();
}
std::unique_ptr<LayerTreeFrameSinkHolder>
SurfaceTreeHost::CreateLayerTreeFrameSinkHolder() {
return std::make_unique<LayerTreeFrameSinkHolder>(this,
CreateLayerTreeFrameSink());
}
float SurfaceTreeHost::CalculateScaleFactor(
const std::optional<float>& scale_factor) const {
if (scale_factor) {
// TODO(crbug.com/40255259): Remove this once the scale factor precision
// issue is fixed for ARC.
if (std::abs(scale_factor.value() -
host_window_->layer()->device_scale_factor()) <
display::kDeviceScaleFactorErrorTolerance) {
return host_window_->layer()->device_scale_factor();
}
return scale_factor.value();
}
return host_window_->layer()->device_scale_factor();
}
void SurfaceTreeHost::ApplyRoundedCornersToSurfaceTree(
const gfx::RectF& bounds,
const gfx::RoundedCornersF& radii_in_dps) {
gfx::RoundedCornersF radii = radii_in_dps;
if (client_submits_surfaces_in_pixel_coordinates()) {
float scale_factor = GetScaleFactor();
radii = gfx::RoundedCornersF{
radii_in_dps.upper_left() * scale_factor,
radii_in_dps.upper_right() * scale_factor,
radii_in_dps.lower_right() * scale_factor,
radii_in_dps.lower_left() * scale_factor,
};
}
gfx::RRectF rounded_corners_bounds(bounds, radii);
ApplyAndPropagateRoundedCornersToSurfaceTree(root_surface(),
rounded_corners_bounds);
}
scoped_refptr<viz::RasterContextProvider>
SurfaceTreeHost::SetRasterContextProviderForTesting(
scoped_refptr<viz::RasterContextProvider> context_provider_test) {
auto old_provider = context_provider_;
context_provider_ = context_provider_test;
return old_provider;
}
void SurfaceTreeHost::ApplyAndPropagateRoundedCornersToSurfaceTree(
Surface* surface,
const gfx::RRectF& rounded_corners_bounds) {
surface->SetRoundedCorners(rounded_corners_bounds,
/*commit_override=*/true);
for (auto& sub_surface_entry : surface->sub_surfaces()) {
// Convert the rounded corners bounds to sub_surface local coordinates by
// subtracting the sub_surface origin. The origin is the offset of the
// subsurface from its parent's surface.
const gfx::RRectF rounded_bounds_in_sub_surface_coords =
rounded_corners_bounds - sub_surface_entry.second.OffsetFromOrigin();
ApplyAndPropagateRoundedCornersToSurfaceTree(
/*surface=*/sub_surface_entry.first,
rounded_bounds_in_sub_surface_coords);
}
}
void SurfaceTreeHost::SetScaleFactorTransform(float scale_factor) {
DCHECK(client_submits_surfaces_in_pixel_coordinates_);
gfx::Transform tr;
tr.Scale(1.0f / scale_factor, 1.0f / scale_factor);
if (root_surface()->window()->transform() != tr) {
root_surface()->window()->SetTransform(tr);
}
}
} // namespace exo