| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef UI_GL_DC_LAYER_TREE_H_ |
| #define UI_GL_DC_LAYER_TREE_H_ |
| |
| #include <windows.h> |
| #include <d3d11.h> |
| #include <dcomp.h> |
| #include <wrl/client.h> |
| |
| #include <memory> |
| |
| #include "base/containers/flat_map.h" |
| #include "mojo/public/cpp/bindings/pending_receiver.h" |
| #include "ui/gfx/color_space_win.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "ui/gl/dc_layer_overlay_params.h" |
| #include "ui/gl/delegated_ink_point_renderer_gpu.h" |
| #include "ui/gl/hdr_metadata_helper_win.h" |
| |
| namespace gfx { |
| namespace mojom { |
| class DelegatedInkPointRenderer; |
| } // namespace mojom |
| class DelegatedInkMetadata; |
| } // namespace gfx |
| |
| namespace gl { |
| |
| class DirectCompositionChildSurfaceWin; |
| class SwapChainPresenter; |
| |
| enum class VideoProcessorType { kSDR, kHDR }; |
| |
| // Cache video processor and its size. |
| struct VideoProcessorWrapper { |
| VideoProcessorWrapper(); |
| ~VideoProcessorWrapper(); |
| VideoProcessorWrapper(VideoProcessorWrapper&& other); |
| VideoProcessorWrapper& operator=(VideoProcessorWrapper&& other); |
| VideoProcessorWrapper(const VideoProcessorWrapper&) = delete; |
| VideoProcessorWrapper& operator=(VideoProcessorWrapper& other) = delete; |
| |
| // Input and output size of video processor . |
| gfx::Size video_input_size; |
| gfx::Size video_output_size; |
| |
| // The video processor is cached so SwapChains don't have to recreate it |
| // whenever they're created. |
| Microsoft::WRL::ComPtr<ID3D11VideoDevice> video_device; |
| Microsoft::WRL::ComPtr<ID3D11VideoContext> video_context; |
| Microsoft::WRL::ComPtr<ID3D11VideoProcessor> video_processor; |
| Microsoft::WRL::ComPtr<ID3D11VideoProcessorEnumerator> |
| video_processor_enumerator; |
| }; |
| |
| // DCLayerTree manages a tree of direct composition visuals, and associated |
| // swap chains for given overlay layers. It maintains a list of pending layers |
| // submitted using ScheduleDCLayer() that are presented and committed in |
| // CommitAndClearPendingOverlays(). |
| class DCLayerTree { |
| public: |
| using VideoProcessorMap = |
| base::flat_map<VideoProcessorType, VideoProcessorWrapper>; |
| using DelegatedInkRenderer = |
| DelegatedInkPointRendererGpu<IDCompositionInkTrailDevice, |
| IDCompositionDelegatedInkTrail, |
| DCompositionInkTrailPoint>; |
| |
| DCLayerTree(bool disable_nv12_dynamic_textures, |
| bool disable_vp_scaling, |
| bool disable_vp_super_resolution, |
| bool force_dcomp_triple_buffer_video_swap_chain, |
| bool no_downscaled_overlay_promotion); |
| |
| DCLayerTree(const DCLayerTree&) = delete; |
| DCLayerTree& operator=(const DCLayerTree&) = delete; |
| |
| ~DCLayerTree(); |
| |
| // Returns true on success. |
| bool Initialize(HWND window); |
| |
| // Present pending overlay layers, and perform a direct composition commit if |
| // necessary. Returns true if presentation and commit succeeded. |
| bool CommitAndClearPendingOverlays( |
| DirectCompositionChildSurfaceWin* root_surface); |
| |
| // Schedule an overlay layer for the next CommitAndClearPendingOverlays call. |
| bool ScheduleDCLayer(std::unique_ptr<DCLayerOverlayParams> params); |
| |
| // Called by SwapChainPresenter to initialize video processor that can handle |
| // at least given input and output size. The video processor is shared across |
| // layers so the same one can be reused if it's large enough. Returns true on |
| // success. |
| VideoProcessorWrapper* InitializeVideoProcessor(const gfx::Size& input_size, |
| const gfx::Size& output_size, |
| bool is_hdr_output); |
| |
| bool disable_nv12_dynamic_textures() const { |
| return disable_nv12_dynamic_textures_; |
| } |
| |
| bool disable_vp_scaling() const { return disable_vp_scaling_; } |
| |
| bool disable_vp_super_resolution() const { |
| return disable_vp_super_resolution_; |
| } |
| |
| bool force_dcomp_triple_buffer_video_swap_chain() const { |
| return force_dcomp_triple_buffer_video_swap_chain_; |
| } |
| |
| bool no_downscaled_overlay_promotion() const { |
| return no_downscaled_overlay_promotion_; |
| } |
| |
| VideoProcessorWrapper& GetOrCreateVideoProcessor(bool is_hdr); |
| |
| Microsoft::WRL::ComPtr<IDXGISwapChain1> GetLayerSwapChainForTesting( |
| size_t index) const; |
| |
| void GetSwapChainVisualInfoForTesting(size_t index, |
| gfx::Transform* transform, |
| gfx::Point* offset, |
| gfx::Rect* clip_rect) const; |
| |
| size_t GetSwapChainPresenterCountForTesting() const { |
| return video_swap_chains_.size(); |
| } |
| |
| size_t GetDcompLayerCountForTesting() const { |
| return visual_tree_ ? visual_tree_->GetDcompLayerCountForTesting() : 0; |
| } |
| IDCompositionVisual3* GetContentVisualForTesting(size_t index) const { |
| return visual_tree_ ? visual_tree_->GetContentVisualForTesting(index) |
| : nullptr; |
| } |
| |
| void SetFrameRate(float frame_rate); |
| |
| const std::unique_ptr<HDRMetadataHelperWin>& GetHDRMetadataHelper() { |
| return hdr_metadata_helper_; |
| } |
| |
| HWND window() const { return window_; } |
| |
| bool SupportsDelegatedInk(); |
| |
| void SetDelegatedInkTrailStartPoint( |
| std::unique_ptr<gfx::DelegatedInkMetadata>); |
| |
| void InitDelegatedInkPointRendererReceiver( |
| mojo::PendingReceiver<gfx::mojom::DelegatedInkPointRenderer> |
| pending_receiver); |
| |
| DelegatedInkRenderer* GetInkRendererForTesting() const { |
| return ink_renderer_.get(); |
| } |
| |
| // Owns a list of |VisualSubtree|s that represent visual layers. |
| class VisualTree { |
| public: |
| VisualTree(DCLayerTree* tree); |
| |
| VisualTree(VisualTree&&) = delete; |
| VisualTree(const VisualTree&) = delete; |
| VisualTree& operator=(const VisualTree&) = delete; |
| |
| ~VisualTree(); |
| // Given pending overlays, builds or updates this visual tree. |
| // Returns true if commit succeeded. |
| bool UpdateTree( |
| const std::vector<std::unique_ptr<DCLayerOverlayParams>>& overlays, |
| // True if the tree must rebuild. |
| bool needs_rebuild_visual_tree); |
| void GetSwapChainVisualInfoForTesting(size_t index, |
| gfx::Transform* transform, |
| gfx::Point* offset, |
| gfx::Rect* clip_rect) const; |
| size_t GetDcompLayerCountForTesting() const { |
| return visual_subtrees_.size(); |
| } |
| IDCompositionVisual3* GetContentVisualForTesting(size_t index) const { |
| return visual_subtrees_[index]->content_visual(); |
| } |
| // Returns true if the tree is optimized. |
| // TODO(http://crbug.com/1380822): Implement tree optimization where the |
| // tree is built incrementally and does not require full rebuild. |
| bool tree_optimized() const { return tree_optimized_; } |
| |
| // Owns a subtree of DComp visual that apply clip, offset, etc. and contains |
| // some content at its leaf. |
| // This class keeps track about what properties are currently set on the |
| // visuals. |
| class VisualSubtree { |
| public: |
| VisualSubtree(); |
| ~VisualSubtree(); |
| VisualSubtree(VisualSubtree&& other) = delete; |
| VisualSubtree& operator=(VisualSubtree&& other) = delete; |
| VisualSubtree(const VisualSubtree&) = delete; |
| VisualSubtree& operator=(VisualSubtree& other) = delete; |
| |
| // Returns true if something was changed. |
| bool Update(IDCompositionDevice3* dcomp_device, |
| Microsoft::WRL::ComPtr<IUnknown> dcomp_visual_content, |
| uint64_t dcomp_surface_serial, |
| const gfx::Size& image_size, |
| absl::optional<SkColor4f> content_tint_color, |
| const gfx::Rect& content_rect, |
| const gfx::Rect& quad_rect, |
| bool nearest_neighbor_filter, |
| const gfx::Transform& quad_to_root_transform, |
| const gfx::RRectF& rounded_corner_bounds, |
| float opacity, |
| const absl::optional<gfx::Rect>& clip_rect_in_root); |
| |
| IDCompositionVisual3* container_visual() const { |
| return clip_visual_.Get(); |
| } |
| IDCompositionVisual3* content_visual() const { |
| return content_visual_.Get(); |
| } |
| IUnknown* dcomp_visual_content() const { |
| return dcomp_visual_content_.Get(); |
| } |
| void GetSwapChainVisualInfoForTesting(gfx::Transform* transform, |
| gfx::Point* offset, |
| gfx::Rect* clip_rect) const; |
| |
| int z_order() const { return z_order_; } |
| void set_z_order(int z_order) { z_order_ = z_order; } |
| |
| private: |
| // The root of this subtree. In root space and contains the clip rect and |
| // controls subtree opacity. |
| Microsoft::WRL::ComPtr<IDCompositionVisual3> clip_visual_; |
| // In root space and contains the rounded rectangle clip. This is separate |
| // from |clip_visual_| since an overlay layer can have both a rectangular |
| // and a rounded rectangular clip rects. |
| Microsoft::WRL::ComPtr<IDCompositionVisual3> rounded_corners_visual_; |
| // The child of |clip_visual_|, transforms its children from quad to root |
| // space. This visual exists because |offset_| is in quad space, so it |
| // must be affected by |transform_|. They cannot be on the same visual |
| // since |IDCompositionVisual::SetTransform| and |
| // |IDCompositionVisual::SetOffset[XY]| are applied in the opposite order |
| // than we want. |
| Microsoft::WRL::ComPtr<IDCompositionVisual3> transform_visual_; |
| // The child of |transform_visual_|. In quad space, holds |
| // |dcomp_visual_content_|. |
| Microsoft::WRL::ComPtr<IDCompositionVisual3> content_visual_; |
| |
| // The content to be placed at the leaf of the visual subtree. Either an |
| // IDCompositionSurface or an IDXGISwapChain. |
| Microsoft::WRL::ComPtr<IUnknown> dcomp_visual_content_; |
| // |dcomp_surface_serial_| is associated with |dcomp_visual_content_| of |
| // IDCompositionSurface type. New value indicates that dcomp surface data |
| // is updated. |
| uint64_t dcomp_surface_serial_ = 0; |
| |
| // True if |content_visual_| has soft borders. This is required for |
| // anti-aliasing when |transform_| makes |quad_rect_| not have |
| // axis-aligned integer bounds in root space. |
| bool content_soft_borders_ = false; |
| |
| // The portion of |dcomp_visual_content_| to display. This area will be |
| // mapped to |quad_rect_|'s bounds. |
| gfx::Rect content_rect_; |
| |
| // The bounds which contain this overlay. When mapped by |transform_|, |
| // this is the bounds of the overlay in root space. |
| gfx::Rect quad_rect_; |
| |
| // Whether or not to use nearest-neighbor filtering to scale |
| // |dcomp_visual_content_|. This is applied to |transform_visual_| since |
| // both it and |content_visual_| can scale the content. |
| bool nearest_neighbor_filter_ = false; |
| |
| // Transform from quad space to root space. |
| gfx::Transform transform_; |
| |
| // Clip rect in root space. |
| absl::optional<gfx::Rect> clip_rect_; |
| |
| // Rounded corner clip in root space |
| gfx::RRectF rounded_corner_bounds_; |
| |
| // The opacity of the entire visual subtree |
| float opacity_ = 1.0; |
| |
| // A color that will tint this visual's content. Each channel of the color |
| // will be multiplied by the respective channel in this visual's output. |
| // A white tint is a no-op. |
| absl::optional<SkColor4f> content_tint_color_; |
| |
| // The size of overlay image in |dcomp_visual_content_| which is in |
| // pixels. |
| gfx::Size image_size_; |
| |
| // The order relative to the root surface. Positive values means the |
| // visual appears in front of the root surface (i.e. overlay) and negative |
| // values means the visual appears below the root surface (i.e. underlay). |
| int z_order_ = 0; |
| }; |
| |
| private: |
| // Tree that owns `this`. |
| const raw_ptr<DCLayerTree> dc_layer_tree_ = nullptr; |
| // List of DCOMP visual subtrees for previous frame. |
| std::vector<std::unique_ptr<VisualSubtree>> visual_subtrees_; |
| // TODO(http://crbug.com/1380822): Implement tree optimization where the |
| // tree is built incrementally and does not require full rebuild. |
| const bool tree_optimized_ = false; |
| }; |
| |
| private: |
| // Given pending overlays, builds or updates visual tree. |
| // Returns true if commit succeeded. |
| bool BuildVisualTreeHelper( |
| const std::vector<std::unique_ptr<DCLayerOverlayParams>>& overlays, |
| // True if the caller determined that rebuilding the tree is required. |
| bool needs_rebuild_visual_tree); |
| |
| // This will add an ink visual to the visual tree to enable delegated ink |
| // trails. This will initially always be called directly before an OS |
| // delegated ink API is used. After that, it can also be added anytime the |
| // visual tree is rebuilt. |
| void AddDelegatedInkVisualToTreeIfNeeded( |
| IDCompositionVisual3* root_surface_visual); |
| |
| // The ink renderer must be initialized before an OS API is used in order to |
| // set up the delegated ink visual and delegated ink trail object. |
| bool InitializeInkRenderer(); |
| |
| // Returns the size of the surface to |resource_size_in_pixels|. |
| raw_ptr<IDCompositionSurface> GetOrCreateSolidWhiteTexture( |
| gfx::Size& resource_size_in_pixels); |
| |
| const bool disable_nv12_dynamic_textures_; |
| const bool disable_vp_scaling_; |
| const bool disable_vp_super_resolution_; |
| const bool force_dcomp_triple_buffer_video_swap_chain_; |
| const bool no_downscaled_overlay_promotion_; |
| |
| HWND window_; |
| Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_; |
| Microsoft::WRL::ComPtr<IDCompositionDevice3> dcomp_device_; |
| Microsoft::WRL::ComPtr<IDCompositionTarget> dcomp_target_; |
| |
| // A IDCompositionSurface cleared to white, used for solid color overlays. |
| Microsoft::WRL::ComPtr<IDCompositionSurface> solid_color_texture_; |
| |
| // Store video processor for SDR/HDR mode separately, which could avoid |
| // problem in (http://crbug.com/1121061). |
| VideoProcessorMap video_processor_map_; |
| |
| // Current video processor input and output colorspace. |
| gfx::ColorSpace video_input_color_space_; |
| gfx::ColorSpace video_output_color_space_; |
| |
| // Set to true if a direct composition root visual needs rebuild. |
| // Each overlay is represented by a VisualSubtree, which is placed in the root |
| // visual's child list in draw order. Whenever the number of overlays or their |
| // draw order changes, the root visual needs to be rebuilt. |
| bool needs_rebuild_visual_tree_ = false; |
| |
| // Set if root surface is using a swap chain currently. |
| Microsoft::WRL::ComPtr<IDXGISwapChain1> root_swap_chain_; |
| |
| // Set if root surface is using a direct composition surface currently. |
| Microsoft::WRL::ComPtr<IDCompositionSurface> root_dcomp_surface_; |
| |
| // Root direct composition visual for window dcomp target. |
| Microsoft::WRL::ComPtr<IDCompositionVisual3> dcomp_root_visual_; |
| |
| // List of pending overlay layers from ScheduleDCLayer(). |
| std::vector<std::unique_ptr<DCLayerOverlayParams>> pending_overlays_; |
| |
| // List of swap chain presenters for previous frame. |
| std::vector<std::unique_ptr<SwapChainPresenter>> video_swap_chains_; |
| |
| // A tree that owns all DCOMP visuals for overlays along with attributes |
| // required to build DCOMP tree. It's updated for each frame. |
| std::unique_ptr<VisualTree> visual_tree_; |
| |
| // Number of frames per second. |
| float frame_rate_ = 0.f; |
| |
| // dealing with hdr metadata |
| std::unique_ptr<HDRMetadataHelperWin> hdr_metadata_helper_; |
| |
| // Renderer for drawing delegated ink trails using OS APIs. This is created |
| // when the DCLayerTree is created, but can only be queried to check if the |
| // platform supports delegated ink trails. It must be initialized via the |
| // Initialize() method in order to be used for drawing delegated ink trails. |
| std::unique_ptr<DelegatedInkRenderer> ink_renderer_; |
| }; |
| |
| } // namespace gl |
| |
| #endif // UI_GL_DC_LAYER_TREE_H_ |