// 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.
#ifndef COMPONENTS_EXO_SURFACE_TREE_HOST_H_
#define COMPONENTS_EXO_SURFACE_TREE_HOST_H_
#include <memory>
#include <set>
#include "base/containers/queue.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/scoped_observation.h"
#include "components/exo/surface.h"
#include "components/exo/surface_delegate.h"
#include "components/viz/common/gpu/context_lost_observer.h"
#include "components/viz/common/quads/compositor_frame.h"
#include "components/viz/common/surfaces/frame_sink_id.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/display_manager_observer.h"
#include "ui/display/types/display_constants.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/rounded_corners_f.h"
namespace aura {
class Window;
} // namespace aura
namespace viz {
class RasterContextProvider;
}
namespace exo {
class LayerTreeFrameSinkHolder;
// Notifies viz that exo doesn't want to throttle sending
// `DidReceiveCompositorFrameAck` and `ReclaimResources`. We don't want exo to
// merge those into OnBeginFrame as that makes clients of exo to throttle as
// well given frame callbacks as well as buffers are sent/released in a late
// manner.
BASE_DECLARE_FEATURE(kExoDisableBeginFrameAcks);
// This class provides functionality for hosting a surface tree. The surface
// tree is hosted in the |host_window_|.
class SurfaceTreeHost : public SurfaceDelegate,
public display::DisplayManagerObserver,
public ui::LayerOwner::Observer,
public viz::ContextLostObserver {
public:
explicit SurfaceTreeHost(const std::string& window_name);
SurfaceTreeHost(const std::string& window_name,
std::unique_ptr<aura::Window> host_window);
SurfaceTreeHost(const SurfaceTreeHost&) = delete;
SurfaceTreeHost& operator=(const SurfaceTreeHost&) = delete;
~SurfaceTreeHost() override;
// Sets a root surface of a surface tree. This surface tree will be hosted in
// the |host_window_|.
virtual void SetRootSurface(Surface* root_surface);
// Returns false if the hit test region is empty.
bool HasHitTestRegion() const;
// Sets |mask| to the path that delineates the hit test region of the hosted
// surface tree.
void GetHitTestMask(SkPath* mask) const;
// Call this to indicate that the previous CompositorFrame is processed and
// the surface is being scheduled for a draw.
virtual void DidReceiveCompositorFrameAck();
// Call this to indicate that the CompositorFrame with given
// |presentation_token| has been first time presented to user.
void DidPresentCompositorFrame(uint32_t presentation_token,
const gfx::PresentationFeedback& feedback);
// Sets the scale factor for all buffers associated with this surface. This
// affects all future commits.
void SetScaleFactor(float scale_factor);
aura::Window* host_window() { return host_window_.get(); }
const aura::Window* host_window() const { return host_window_.get(); }
Surface* root_surface() { return root_surface_; }
const Surface* root_surface() const { return root_surface_; }
const gfx::Point& root_surface_origin_pixel() const {
return root_surface_origin_pixel_;
}
LayerTreeFrameSinkHolder* layer_tree_frame_sink_holder() {
return layer_tree_frame_sink_holder_.get();
}
using PresentationCallbacks = std::list<Surface::PresentationCallback>;
base::queue<std::list<Surface::FrameCallback>>&
GetFrameCallbacksForTesting() {
return frame_callbacks_;
}
base::flat_map<uint32_t, PresentationCallbacks>&
GetActivePresentationCallbacksForTesting() {
return active_presentation_callbacks_;
}
uint32_t GenerateNextFrameToken() { return ++next_token_; }
// Returns the primary SurfaceId.
viz::SurfaceId GetSurfaceId() const;
// SurfaceDelegate:
void OnSurfaceCommit() override;
bool IsSurfaceSynchronized() const override;
bool IsInputEnabled(Surface* surface) const override;
void OnSetFrame(SurfaceFrameType type) override {}
void OnSetFrameColors(SkColor active_color, SkColor inactive_color) override {
}
void OnSetParent(Surface* parent, const gfx::Point& position) override {}
void OnSetStartupId(const char* startup_id) override {}
void OnSetApplicationId(const char* application_id) override {}
void SetUseImmersiveForFullscreen(bool value) override {}
void OnActivationRequested() override {}
void OnNewOutputAdded() override;
void OnSetServerStartResize() override {}
void ShowSnapPreviewToPrimary() override {}
void ShowSnapPreviewToSecondary() override {}
void HideSnapPreview() override {}
void SetSnapPrimary(float snap_ratio) override {}
void SetSnapSecondary(float snap_ratio) override {}
void UnsetSnap() override {}
void SetCanGoBack() override {}
void UnsetCanGoBack() override {}
void SetPip() override {}
void UnsetPip() override {}
void SetFloatToLocation(
chromeos::FloatStartLocation float_start_location) override {}
void SetAspectRatio(const gfx::SizeF& aspect_ratio) override {}
void MoveToDesk(int desk_index) override {}
void SetVisibleOnAllWorkspaces() override {}
void SetInitialWorkspace(const char* initial_workspace) override {}
void Pin(bool trusted) override {}
void Unpin() override {}
void SetSystemModal(bool system_modal) override {}
void SetTopInset(int height) override {}
SecurityDelegate* GetSecurityDelegate() override;
// display::DisplayManagerObserver:
void OnDidProcessDisplayChanges(
const DisplayConfigurationChange& configuration_change) override;
// viz::ContextLostObserver:
void OnContextLost() override;
void OnFrameSinkLost();
void set_client_submits_surfaces_in_pixel_coordinates(bool enabled) {
client_submits_surfaces_in_pixel_coordinates_ = enabled;
}
void SetSecurityDelegate(SecurityDelegate* security_delegate);
void SubmitCompositorFrameForTesting(viz::CompositorFrame frame);
using LayerTreeFrameSinkHolderFactory =
base::RepeatingCallback<std::unique_ptr<LayerTreeFrameSinkHolder>()>;
// It should only be used at initialization time before any frames are
// submitted.
void SetLayerTreeFrameSinkHolderFactoryForTesting(
LayerTreeFrameSinkHolderFactory frame_sink_holder_factory);
// Creates a LayerTreeFrameSink for the |host_window_|.
std::unique_ptr<cc::mojo_embedder::AsyncLayerTreeFrameSink>
CreateLayerTreeFrameSink();
// Overridden from ui::LayerOwner::Observer
void OnLayerRecreated(ui::Layer* old_layer) override;
// Applies rounded_corner_bounds (bounds + radii_in_dps) to the surface tree.
// `rounded_corner_bounds` should be in the coordinate space of the
// `root_surface`.
void ApplyRoundedCornersToSurfaceTree(
const gfx::RectF& bounds,
const gfx::RoundedCornersF& radii_in_dps);
scoped_refptr<viz::RasterContextProvider> SetRasterContextProviderForTesting(
scoped_refptr<viz::RasterContextProvider> context_provider_test);
protected:
void UpdateDisplayOnTree();
// Call this after a buffer has been committed but before a compositor frame
// has been submitted.
void WillCommit();
// Call this to submit a compositor frame.
void SubmitCompositorFrame();
// Call this to submit an empty compositor frame. This may be useful if
// the surface tree is becoming invisible but the resources (e.g. buffers)
// need to be released back to the client.
void SubmitEmptyCompositorFrame();
// Updates the host window's (or the closest representative surface layer's)
// size to cover exo surfaces that must be visible and not clipped.
// It also updates `root_surface_origin_` accordingly to the origin.
void UpdateSurfaceLayerSizeAndRootSurfaceOrigin();
// Updates the host layer's opacity. This has to be called after root
// surface's resource is updated.
void UpdateHostLayerOpacity();
void UpdateHostWindowOpaqueRegion();
bool client_submits_surfaces_in_pixel_coordinates() const {
return client_submits_surfaces_in_pixel_coordinates_;
}
bool bounds_is_dirty() const { return bounds_is_dirty_; }
void set_bounds_is_dirty(bool bounds_is_dirty) {
bounds_is_dirty_ = bounds_is_dirty;
}
// If the client has submitted a scale factor, we use that. Otherwise we use
// the host window's layer's scale factor.
virtual float GetScaleFactor() const;
virtual float GetPendingScaleFactor() const;
bool HasDoubleBufferedPendingScaleFactor() const;
// Sets the appropriate transform for the given scale factor.
// NOTE: This should only be done if the client submits in pixel coordinates.
void SetScaleFactorTransform(float scale_factor);
// Once a configure is acknowledged, accept the parent portion of the
// local_surface_id from the |host_window_|.
void UpdateLocalSurfaceIdFromParent(
const viz::LocalSurfaceId& parent_local_surface_id);
// Changes the local_surface_id as the viz::Surface property will change.
void AllocateLocalSurfaceId();
// If local_surface_id is newer than `GetCommitTargetLayer()`, update the
// surface ranges to produce different SurfaceDrawQuads.
virtual void MaybeActivateSurface();
// The local_surface_id that the `layer_tree_frame_sink_holder_` is submitting
// with.
const viz::LocalSurfaceId& GetCurrentLocalSurfaceId() const;
// Returns the ui::Layer that hosts client's surface commits, i.e.
// commit_target_layer. Its property can be controlled by the client.
// When an animation causes the host_window->layer to be cloned, before the
// client acks the config event for that request, the old_layer prior to the
// cloning should be the commit_target_layer.
//
// On SurfaceTreeHost implementations that don't have async config/ack flow,
// this returns host_window()->layer().
//
// Note: due to animation cancelling, the commit_target_layer can be
// destroyed with the cancelled animation, so this method may return nullptr.
virtual ui::Layer* GetCommitTargetLayer();
virtual const ui::Layer* GetCommitTargetLayer() const;
int64_t output_display_id() const { return output_display_id_; }
// The FrameSinkId associated with this.
viz::FrameSinkId frame_sink_id_;
private:
// Returns true if contents of `host_window_` fills the bounds opaquely.
bool ContentsFillsHostWindowOpaquely() const;
void InitHostWindow(const std::string& window_name);
viz::CompositorFrame PrepareToSubmitCompositorFrame();
void HandleContextLost();
void HandleFrameSinkLost();
void CleanUpCallbacks();
float CalculateScaleFactor(const std::optional<float>& scale_factor) const;
// Applies `rounded_corner_bounds` to the `surface` and propagates the bounds
// to its subsurfaces. `rounded_corner_bounds` should be in the local
// coordinates of the `surface`.
void ApplyAndPropagateRoundedCornersToSurfaceTree(
Surface* surface,
const gfx::RRectF& rounded_corners_bounds);
std::unique_ptr<LayerTreeFrameSinkHolder> CreateLayerTreeFrameSinkHolder();
std::unique_ptr<viz::ChildLocalSurfaceIdAllocator>
child_local_surface_id_allocator_;
raw_ptr<Surface> root_surface_ = nullptr;
// Position of root surface relative to topmost, leftmost sub-surface. The
// host window should be translated by the negation of this vector.
// The coordinates is Pixel.
gfx::Point root_surface_origin_pixel_;
// The coordinates is DP.
std::unique_ptr<aura::Window> host_window_;
std::unique_ptr<LayerTreeFrameSinkHolder> layer_tree_frame_sink_holder_;
LayerTreeFrameSinkHolderFactory frame_sink_holder_factory_;
// This queue contains lists the callbacks to notify the client when it is a
// good time to start producing a new frame. Each list corresponds to a
// compositor frame, in the order of submission to
// `layer_tree_frame_sink_holder_`.
//
// These callbacks move to |frame_callbacks_| when Commit() is called. They
// fire when the effect of the Commit() is scheduled to be drawn.
base::queue<std::list<Surface::FrameCallback>> frame_callbacks_;
// These lists contain the callbacks to notify the client when surface
// contents have been presented.
base::flat_map<uint32_t, PresentationCallbacks>
active_presentation_callbacks_;
// When a client calls set_scale_factor they're actually setting the scale
// factor for all future commits.
std::optional<float> pending_scale_factor_;
// This is the client-set scale factor that is being used for the current
// buffer.
std::optional<float> scale_factor_;
viz::FrameTokenGenerator next_token_;
scoped_refptr<viz::RasterContextProvider> context_provider_;
base::ScopedObservation<display::DisplayManager,
display::DisplayManagerObserver>
display_manager_observation_{this};
// The display id for the output the surface is entered onto.
int64_t output_display_id_ = display::kInvalidDisplayId;
bool client_submits_surfaces_in_pixel_coordinates_ = false;
raw_ptr<SecurityDelegate> security_delegate_ = nullptr;
std::set<gpu::SyncToken> prev_frame_verified_tokens_;
bool bounds_is_dirty_ = true;
base::WeakPtrFactory<SurfaceTreeHost> weak_ptr_factory_{this};
};
} // namespace exo
#endif // COMPONENTS_EXO_SURFACE_TREE_HOST_H_