blob: 5053abb977491f2f4431fae36771ceb03799e789 [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 "cc/trees/layer_tree_frame_sink.h"
#include "components/exo/layer_tree_frame_sink_holder.h"
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "gpu/command_buffer/client/gles2_interface.h"
#include "services/ws/public/mojom/window_tree_constants.mojom.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/display/display.h"
#include "ui/display/screen.h"
#include "ui/gfx/geometry/dip_util.h"
#include "ui/gfx/presentation_feedback.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->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:
SurfaceTreeHost* const surface_tree_host_;
DISALLOW_COPY_AND_ASSIGN(CustomWindowTargeter);
};
} // namespace
////////////////////////////////////////////////////////////////////////////////
// SurfaceTreeHost, public:
SurfaceTreeHost::SurfaceTreeHost(const std::string& window_name)
: host_window_(
std::make_unique<aura::Window>(nullptr,
aura::client::WINDOW_TYPE_CONTROL,
WMHelper::GetInstance()->env())) {
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(
ws::mojom::EventTargetingPolicy::DESCENDANTS_ONLY);
host_window_->SetEventTargeter(std::make_unique<CustomWindowTargeter>(this));
layer_tree_frame_sink_holder_ = std::make_unique<LayerTreeFrameSinkHolder>(
this, host_window_->CreateLayerTreeFrameSink());
WMHelper::GetInstance()->env()->context_factory()->AddObserver(this);
}
SurfaceTreeHost::~SurfaceTreeHost() {
WMHelper::GetInstance()->env()->context_factory()->RemoveObserver(this);
SetRootSurface(nullptr);
LayerTreeFrameSinkHolder::DeleteWhenLastResourceHasBeenReclaimed(
std::move(layer_tree_frame_sink_holder_));
}
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(
host_window_->env());
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;
// Call all frame callbacks with a null frame time to indicate that they
// have been cancelled.
while (!frame_callbacks_.empty()) {
frame_callbacks_.front().Run(base::TimeTicks());
frame_callbacks_.pop_front();
}
DCHECK(presentation_callbacks_.empty());
for (auto entry : active_presentation_callbacks_) {
while (!entry.second.empty()) {
entry.second.front().Run(gfx::PresentationFeedback());
entry.second.pop_front();
}
}
active_presentation_callbacks_.clear();
}
if (root_surface) {
root_surface_ = root_surface;
root_surface_->SetSurfaceDelegate(this);
host_window_->AddChild(root_surface_->window());
UpdateHostWindowBounds();
root_surface_->window()->Show();
}
}
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() {
while (!frame_callbacks_.empty()) {
frame_callbacks_.front().Run(base::TimeTicks::Now());
frame_callbacks_.pop_front();
}
}
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);
}
////////////////////////////////////////////////////////////////////////////////
// SurfaceDelegate overrides:
void SurfaceTreeHost::OnSurfaceCommit() {
DCHECK(presentation_callbacks_.empty());
root_surface_->CommitSurfaceHierarchy(false);
UpdateHostWindowBounds();
}
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;
}
////////////////////////////////////////////////////////////////////////////////
// ui::ContextFactoryObserver overrides:
void SurfaceTreeHost::OnLostSharedContext() {
if (!host_window_->GetSurfaceId().is_valid() || !root_surface_)
return;
root_surface_->SurfaceHierarchyResourcesLost();
SubmitCompositorFrame();
}
////////////////////////////////////////////////////////////////////////////////
// SurfaceTreeHost, protected:
void SurfaceTreeHost::SubmitCompositorFrame() {
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_ = std::make_unique<LayerTreeFrameSinkHolder>(
this, host_window_->CreateLayerTreeFrameSink());
}
viz::CompositorFrame frame;
frame.metadata.begin_frame_ack =
viz::BeginFrameAck::CreateManualAckWithDamage();
root_surface_->AppendSurfaceHierarchyCallbacks(&frame_callbacks_,
&presentation_callbacks_);
frame.metadata.frame_token = ++next_token_;
if (!presentation_callbacks_.empty()) {
DCHECK_EQ(active_presentation_callbacks_.count(*next_token_), 0u);
active_presentation_callbacks_[*next_token_] =
std::move(presentation_callbacks_);
} else {
active_presentation_callbacks_[*next_token_] = PresentationCallbacks();
}
frame.render_pass_list.push_back(viz::RenderPass::Create());
const std::unique_ptr<viz::RenderPass>& render_pass =
frame.render_pass_list.back();
const int kRenderPassId = 1;
// Compute a temporaly 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.
const float device_scale_factor =
host_window()->layer()->device_scale_factor();
const gfx::Size output_surface_size_in_pixels = gfx::ConvertSizeToPixel(
device_scale_factor, host_window_->bounds().size());
render_pass->SetNew(kRenderPassId, gfx::Rect(output_surface_size_in_pixels),
gfx::Rect(), gfx::Transform());
frame.metadata.device_scale_factor = device_scale_factor;
frame.metadata.local_surface_id_allocation_time =
host_window()->GetLocalSurfaceIdAllocation().allocation_time();
root_surface_->AppendSurfaceHierarchyContentsToFrame(
root_surface_origin_, device_scale_factor,
layer_tree_frame_sink_holder_->resource_manager(), &frame);
std::vector<GLbyte*> sync_tokens;
for (auto& resource : frame.resource_list)
sync_tokens.push_back(resource.mailbox_holder.sync_token.GetData());
ui::ContextFactory* context_factory =
WMHelper::GetInstance()->env()->context_factory();
gpu::gles2::GLES2Interface* gles2 =
context_factory->SharedMainThreadContextProvider()->ContextGL();
gles2->VerifySyncTokensCHROMIUM(sync_tokens.data(), sync_tokens.size());
layer_tree_frame_sink_holder_->SubmitCompositorFrame(std::move(frame));
}
////////////////////////////////////////////////////////////////////////////////
// SurfaceTreeHost, private:
void SurfaceTreeHost::UpdateHostWindowBounds() {
// 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(
host_window_->env());
gfx::Rect bounds = root_surface_->surface_hierarchy_content_bounds();
host_window_->SetBounds(
gfx::Rect(host_window_->bounds().origin(), bounds.size()));
const bool fills_bounds_opaquely =
bounds.size() == root_surface_->content_size() &&
root_surface_->FillsBoundsOpaquely();
host_window_->SetTransparent(!fills_bounds_opaquely);
root_surface_origin_ = gfx::Point() - bounds.OffsetFromOrigin();
root_surface_->window()->SetBounds(gfx::Rect(
root_surface_origin_, root_surface_->window()->bounds().size()));
}
} // namespace exo