blob: 14b6747a51123516f264b9401ed6ca57dc79460c [file] [log] [blame]
// Copyright 2017 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/exo/surface_tree_host.h"
#include <algorithm>
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "cc/output/compositor_frame.h"
#include "cc/output/layer_tree_frame_sink.h"
#include "components/exo/layer_tree_frame_sink_holder.h"
#include "components/exo/surface.h"
#include "services/ui/public/interfaces/window_tree_constants.mojom.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_targeter.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/cursor/cursor.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/path.h"
namespace exo {
namespace {
class CustomWindowTargeter : public aura::WindowTargeter {
public:
explicit CustomWindowTargeter(SurfaceTreeHost* surface_tree_host)
: surface_tree_host_(surface_tree_host) {}
~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 = event.location();
if (window->parent())
aura::Window::ConvertPointToTarget(window->parent(), window,
&local_point);
aura::Window::ConvertPointToTarget(window, surface->window(), &local_point);
return surface->HitTestRect(gfx::Rect(local_point, gfx::Size(1, 1)));
}
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:
SurfaceTreeHost* const surface_tree_host_;
DISALLOW_COPY_AND_ASSIGN(CustomWindowTargeter);
};
} // namespace
////////////////////////////////////////////////////////////////////////////////
// SurfaceTreeHost, public:
SurfaceTreeHost::SurfaceTreeHost(const std::string& window_name,
aura::WindowDelegate* window_delegate) {
host_window_ = base::MakeUnique<aura::Window>(window_delegate);
host_window_->SetType(aura::client::WINDOW_TYPE_CONTROL);
host_window_->SetName(window_name);
host_window_->Init(ui::LAYER_SOLID_COLOR);
host_window_->set_owned_by_parent(false);
// The host window is a container of surface tree. It doesn't handle pointer
// events.
host_window_->SetEventTargetingPolicy(
ui::mojom::EventTargetingPolicy::DESCENDANTS_ONLY);
host_window_->SetEventTargeter(base::MakeUnique<CustomWindowTargeter>(this));
layer_tree_frame_sink_holder_ = base::MakeUnique<LayerTreeFrameSinkHolder>(
this, host_window_->CreateLayerTreeFrameSink());
aura::Env::GetInstance()->context_factory()->AddObserver(this);
}
SurfaceTreeHost::~SurfaceTreeHost() {
aura::Env::GetInstance()->context_factory()->RemoveObserver(this);
SetRootSurface(nullptr);
if (host_window_->layer()->GetCompositor()) {
host_window_->layer()->GetCompositor()->vsync_manager()->RemoveObserver(
this);
}
}
void SurfaceTreeHost::SetRootSurface(Surface* root_surface) {
if (root_surface == root_surface_)
return;
if (root_surface_) {
root_surface_->window()->Hide();
host_window_->RemoveChild(root_surface_->window());
host_window_->SetBounds(
gfx::Rect(host_window_->bounds().origin(), gfx::Size()));
root_surface_->SetSurfaceDelegate(nullptr);
// Force recreating resources when the surface is added to a tree again.
root_surface_->SurfaceHierarchyResourcesLost();
root_surface_ = nullptr;
active_frame_callbacks_.splice(active_frame_callbacks_.end(),
frame_callbacks_);
// Call all frame callbacks with a null frame time to indicate that they
// have been cancelled.
while (!active_frame_callbacks_.empty()) {
active_frame_callbacks_.front().Run(base::TimeTicks());
active_frame_callbacks_.pop_front();
}
swapping_presentation_callbacks_.splice(
swapping_presentation_callbacks_.end(), presentation_callbacks_);
swapped_presentation_callbacks_.splice(
swapped_presentation_callbacks_.end(),
swapping_presentation_callbacks_);
// Call all presentation callbacks with a null presentation time to indicate
// that they have been cancelled.
while (!swapped_presentation_callbacks_.empty()) {
swapped_presentation_callbacks_.front().Run(base::TimeTicks(),
base::TimeDelta());
swapped_presentation_callbacks_.pop_front();
}
}
if (root_surface) {
root_surface_ = root_surface;
root_surface_->SetSurfaceDelegate(this);
host_window_->AddChild(root_surface_->window());
host_window_->SetBounds(gfx::Rect(host_window_->bounds().origin(),
root_surface_->content_size()));
root_surface_->window()->Show();
}
}
bool SurfaceTreeHost::HasHitTestMask() const {
return root_surface_ ? root_surface_->HasHitTestMask() : false;
}
void SurfaceTreeHost::GetHitTestMask(gfx::Path* mask) const {
if (root_surface_)
root_surface_->GetHitTestMask(mask);
}
gfx::Rect SurfaceTreeHost::GetHitTestBounds() const {
return root_surface_ ? root_surface_->GetHitTestBounds() : gfx::Rect();
}
gfx::NativeCursor SurfaceTreeHost::GetCursor(const gfx::Point& point) const {
return root_surface_ ? root_surface_->GetCursor() : ui::CursorType::kNull;
}
void SurfaceTreeHost::DidReceiveCompositorFrameAck() {
active_frame_callbacks_.splice(active_frame_callbacks_.end(),
frame_callbacks_);
swapping_presentation_callbacks_.splice(
swapping_presentation_callbacks_.end(), presentation_callbacks_);
UpdateNeedsBeginFrame();
}
void SurfaceTreeHost::SetBeginFrameSource(
viz::BeginFrameSource* begin_frame_source) {
if (needs_begin_frame_) {
DCHECK(begin_frame_source_);
begin_frame_source_->RemoveObserver(this);
needs_begin_frame_ = false;
}
begin_frame_source_ = begin_frame_source;
UpdateNeedsBeginFrame();
}
void SurfaceTreeHost::UpdateNeedsBeginFrame() {
if (!begin_frame_source_)
return;
bool needs_begin_frame = !active_frame_callbacks_.empty();
if (needs_begin_frame == needs_begin_frame_)
return;
needs_begin_frame_ = needs_begin_frame;
if (needs_begin_frame_)
begin_frame_source_->AddObserver(this);
else
begin_frame_source_->RemoveObserver(this);
}
////////////////////////////////////////////////////////////////////////////////
// SurfaceDelegate overrides:
void SurfaceTreeHost::OnSurfaceCommit() {
root_surface_->CommitSurfaceHierarchy(gfx::Point(), &frame_callbacks_,
&presentation_callbacks_);
SubmitCompositorFrame();
}
void SurfaceTreeHost::OnSurfaceContentSizeChanged() {
host_window_->SetBounds(gfx::Rect(host_window_->bounds().origin(),
root_surface_->content_size()));
}
bool SurfaceTreeHost::IsSurfaceSynchronized() const {
// To host a surface tree, the root surface has to be desynchronized.
DCHECK(root_surface_);
return false;
}
////////////////////////////////////////////////////////////////////////////////
// aura::WindowObserver overrides:
void SurfaceTreeHost::OnWindowAddedToRootWindow(aura::Window* window) {
DCHECK_EQ(window, host_window());
window->layer()->GetCompositor()->vsync_manager()->AddObserver(this);
}
void SurfaceTreeHost::OnWindowRemovingFromRootWindow(aura::Window* window,
aura::Window* new_root) {
DCHECK_EQ(window, host_window());
window->layer()->GetCompositor()->vsync_manager()->RemoveObserver(this);
}
void SurfaceTreeHost::OnWindowDestroying(aura::Window* window) {
DCHECK_EQ(window, host_window());
window->RemoveObserver(this);
}
////////////////////////////////////////////////////////////////////////////////
// cc::BeginFrameObserverBase overrides:
bool SurfaceTreeHost::OnBeginFrameDerivedImpl(const viz::BeginFrameArgs& args) {
current_begin_frame_ack_ =
viz::BeginFrameAck(args.source_id, args.sequence_number, false);
if (!frame_callbacks_.empty()) {
// In this case, the begin frame arrives just before
// |DidReceivedCompositorFrameAck()|, we need more begin frames to run
// |frame_callbacks_| which will be moved to |active_frame_callbacks_| by
// |DidReceivedCompositorFrameAck()| shortly.
layer_tree_frame_sink_holder_->frame_sink()->DidNotProduceFrame(
current_begin_frame_ack_);
current_begin_frame_ack_.sequence_number =
viz::BeginFrameArgs::kInvalidFrameNumber;
begin_frame_source_->DidFinishFrame(this);
}
while (!active_frame_callbacks_.empty()) {
active_frame_callbacks_.front().Run(args.frame_time);
active_frame_callbacks_.pop_front();
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// ui::CompositorVSyncManager::Observer overrides:
void SurfaceTreeHost::OnUpdateVSyncParameters(base::TimeTicks timebase,
base::TimeDelta interval) {
// Use current time if platform doesn't provide an accurate timebase.
if (timebase.is_null())
timebase = base::TimeTicks::Now();
while (!swapped_presentation_callbacks_.empty()) {
swapped_presentation_callbacks_.front().Run(timebase, interval);
swapped_presentation_callbacks_.pop_front();
}
// VSync parameters updates are generated at the start of a new swap. Move
// the swapping presentation callbacks to swapped callbacks so they fire
// at the next VSync parameters update as that will contain the presentation
// time for the previous frame.
swapped_presentation_callbacks_.splice(swapped_presentation_callbacks_.end(),
swapping_presentation_callbacks_);
}
////////////////////////////////////////////////////////////////////////////////
// ui::ContextFactoryObserver overrides:
void SurfaceTreeHost::OnLostResources() {
if (!host_window_->GetSurfaceId().is_valid() || !root_surface_)
return;
root_surface_->SurfaceHierarchyResourcesLost();
SubmitCompositorFrame();
}
void SurfaceTreeHost::SubmitCompositorFrame() {
DCHECK(root_surface_);
cc::CompositorFrame frame;
// If we commit while we don't have an active BeginFrame, we acknowledge a
// manual one.
if (current_begin_frame_ack_.sequence_number ==
viz::BeginFrameArgs::kInvalidFrameNumber) {
current_begin_frame_ack_ = viz::BeginFrameAck::CreateManualAckWithDamage();
} else {
current_begin_frame_ack_.has_damage = true;
}
frame.metadata.begin_frame_ack = current_begin_frame_ack_;
const int kRenderPassId = 1;
std::unique_ptr<viz::RenderPass> render_pass = viz::RenderPass::Create();
render_pass->SetNew(kRenderPassId, gfx::Rect(), gfx::Rect(),
gfx::Transform());
frame.render_pass_list.push_back(std::move(render_pass));
float device_scale_factor = host_window()->layer()->device_scale_factor();
root_surface_->AppendSurfaceHierarchyContentsToFrame(
gfx::Point(), device_scale_factor, layer_tree_frame_sink_holder_.get(),
&frame);
// Surface uses DIP, but the |output_rect| uses pixels, so we need
// scale it beased on the |device_scale_factor|.
frame.render_pass_list.back()->output_rect =
gfx::Rect(gfx::ConvertSizeToPixel(device_scale_factor,
root_surface_->content_size()));
host_window_->layer()->SetFillsBoundsOpaquely(
root_surface_->FillsBoundsOpaquely());
frame.metadata.device_scale_factor = device_scale_factor;
layer_tree_frame_sink_holder_->frame_sink()->SubmitCompositorFrame(
std::move(frame));
if (current_begin_frame_ack_.sequence_number !=
viz::BeginFrameArgs::kInvalidFrameNumber) {
if (!current_begin_frame_ack_.has_damage) {
layer_tree_frame_sink_holder_->frame_sink()->DidNotProduceFrame(
current_begin_frame_ack_);
}
current_begin_frame_ack_.sequence_number =
viz::BeginFrameArgs::kInvalidFrameNumber;
if (begin_frame_source_)
begin_frame_source_->DidFinishFrame(this);
}
}
} // namespace exo