| // 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(); |
| } |
| |
| void SurfaceTreeHost::OnLostVizProcess() {} |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // SurfaceTreeHost, protected: |
| |
| void SurfaceTreeHost::SubmitCompositorFrame() { |
| DCHECK(root_surface_); |
| 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 |