blob: 0a0773b48fdbd36ec778383c67fd7c729c1bba50 [file] [log] [blame]
// 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/base/moving_max.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/gl_export.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;
// 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 GL_EXPORT DCLayerTree {
public:
using DelegatedInkRenderer =
DelegatedInkPointRendererGpu<IDCompositionInkTrailDevice,
IDCompositionDelegatedInkTrail,
DCompositionInkTrailPoint>;
DCLayerTree(bool disable_nv12_dynamic_textures,
bool disable_vp_auto_hdr,
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,
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device);
// 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 disable_nv12_dynamic_textures() const {
return disable_nv12_dynamic_textures_;
}
bool disable_vp_auto_hdr() const { return disable_vp_auto_hdr_; }
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_;
}
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;
}
IDCompositionVisual2* GetContentVisualForTesting(size_t index) const {
return visual_tree_ ? visual_tree_->GetContentVisualForTesting(index)
: nullptr;
}
#if DCHECK_IS_ON()
bool GetAttachedToRootFromPreviousFrameForTesting(size_t index) const;
#endif // DCHECK_IS_ON()
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.
// This method is called to build visual tree when
// kDCompVisualTreeOptimization is disabled.
// Parameters:
// overlays - overlay inputs to build dcomp tree.
// needs_rebuild_visual_tree - true if the tree needs to be rebuilt.
// Returns true if commit succeeded.
bool BuildTreeDefault(
const std::vector<std::unique_ptr<DCLayerOverlayParams>>& overlays,
bool needs_rebuild_visual_tree);
// Given pending overlays, builds or updates this visual tree.
// This method is called to build visual tree when
// kDCompVisualTreeOptimization is enabled.
// Returns true if commit succeeded.
bool BuildTreeOptimized(
const std::vector<std::unique_ptr<DCLayerOverlayParams>>& overlays,
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();
}
IDCompositionVisual2* GetContentVisualForTesting(size_t index) const {
return visual_subtrees_[index]->content_visual();
}
#if DCHECK_IS_ON()
bool GetAttachedToRootFromPreviousFrameForTesting(size_t index) const {
return visual_subtrees_[index]
->GetAttachedToRootFromPreviousFrameForTesting();
}
#endif // DCHECK_IS_ON()
// Maps the visual content to its corresponding subtree index.
// This is used to find matching subtrees from the previous frame
// that can be reused in the current frame.
// It's safe to use raw pointers here since we have a ComPtr to the visual
// content in the visual subtrees list for the previous frame.
using VisualSubtreeMap = base::flat_map<raw_ptr<IUnknown>, size_t>;
// 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,
const gfx::Rect& content_rect,
Microsoft::WRL::ComPtr<IDCompositionSurface> solid_white_surface,
const SkColor4f& background_color,
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);
IDCompositionVisual2* container_visual() const {
return clip_visual_.Get();
}
IDCompositionVisual2* 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;
#if DCHECK_IS_ON()
bool GetAttachedToRootFromPreviousFrameForTesting() const {
return attached_to_root_from_previous_frame_;
}
#endif // DCHECK_IS_ON()
int z_order() const { return z_order_; }
void set_z_order(int z_order) { z_order_ = z_order; }
private:
#if DCHECK_IS_ON()
friend class VisualTree;
#endif // DCHECK_IS_ON()
// The root of this subtree. In root space and contains the clip rect and
// controls subtree opacity.
Microsoft::WRL::ComPtr<IDCompositionVisual2> 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<IDCompositionVisual2> 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<IDCompositionVisual2> transform_visual_;
// A child of |transform_visual_|. In quad space, holds
// |dcomp_visual_content_|. Visually, this is behind |content_visual_|.
Microsoft::WRL::ComPtr<IDCompositionVisual2> background_color_visual_;
// A child of |transform_visual_|. In quad space, holds
// |dcomp_visual_content_|.
Microsoft::WRL::ComPtr<IDCompositionVisual2> content_visual_;
// The content to be placed at a 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;
// The portion of |dcomp_visual_content_| to display. This area will be
// mapped to |quad_rect_|'s bounds.
gfx::Rect content_rect_;
// The surface containing solid white for the background color fill to be
// placed at a leaf of the visual subtree. Will be tinted by
// |background_color_|. Must be present if |background_color_| is
// non-transparent. Must be created from |GetOrCreateSolidWhiteTexture|.
Microsoft::WRL::ComPtr<IDCompositionSurface> solid_white_surface_;
// The color of the surface that will be placed on
// |background_color_visual_|.
SkColor4f background_color_;
// 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 quad_to_root_transform_;
// Clip rect in root space.
absl::optional<gfx::Rect> clip_rect_in_root_;
// Rounded corner clip in root space
gfx::RRectF rounded_corner_bounds_;
// The opacity of the entire visual subtree
float opacity_ = 1.0;
// 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;
#if DCHECK_IS_ON()
// True if the subtree is reused from the previous frame and keeps its
// attachment to the root from the previous frame. Used for testing.
bool attached_to_root_from_previous_frame_ = false;
#endif // DCHECK_IS_ON()
};
private:
// This function is called as part of |BuildTreeOptimized|.
// For each given overlay:
// 1. Populate visual subtree map with visual content.
// 2. Find the matching subtree from the previous frame. The subtree matches
// if it owns identical visual content. If the match is found:
// 2.1. Updates |overlay_index_to_reused_subtree| with the
// index to the matching subtree.
// 2.2. Updates |subtree_index_to_overlay| with the overlay index the
// previous frame subtree is matched to.
// Returns populated visual subtree map.
VisualSubtreeMap BuildMapAndAssignMatchingSubtrees(
const std::vector<std::unique_ptr<DCLayerOverlayParams>>& overlays,
std::vector<std::unique_ptr<VisualSubtree>>& visual_subtrees,
std::vector<absl::optional<size_t>>& overlay_index_to_reused_subtree,
std::vector<absl::optional<size_t>>& subtree_index_to_overlay);
// This function is called as part of |BuildTreeOptimized|.
// For each overlay that has no match attempts to find unused subtree of
// the previous frame to be reused in the current frame. If such a subtree
// is identified:
// 1. Updates |overlay_index_to_reused_subtree| with the
// index to the found subtree.
// 2. Updates |subtree_index_to_overlay| with the overlay index the
// found subtree is assigned to.
// Returns previous frame subtree first unused index.
size_t ReuseUnmatchedSubtrees(
std::vector<std::unique_ptr<VisualSubtree>>& new_visual_subtrees,
std::vector<absl::optional<size_t>>& overlay_index_to_reused_subtree,
std::vector<absl::optional<size_t>>& subtree_index_to_overlay);
// This function is called as part of |BuildTreeOptimized|.
// Detaches unused subtrees of the previous frame from root starting with
// |first_prev_frame_subtree_unused_index| returned from
// |ReuseUnmatchedSubtrees|.
// Updates |prev_subtree_is_attached_to_root| accordingly.
// Returns true if commit is needed.
bool DetachUnusedSubtreesFromRoot(
size_t first_prev_frame_subtree_unused_index,
std::vector<bool>& prev_subtree_is_attached_to_root);
// This function is called as part of |BuildTreeOptimized|.
// Removes reused subtrees of the previous frame from the root that need to
// be repositioned in the current frame.
// Updates |prev_subtree_is_attached_to_root| accordingly.
// Returns true if commit is needed.
bool DetachReusedSubtreesThatNeedRepositioningFromRoot(
const std::vector<std::unique_ptr<VisualSubtree>>& new_visual_subtrees,
const std::vector<absl::optional<size_t>>&
overlay_index_to_reused_subtree,
const std::vector<absl::optional<size_t>>& subtree_index_to_overlay,
std::vector<bool>& prev_subtree_is_attached_to_root);
// Detaches given subtree from the root.
void DetachSubtreeFromRoot(VisualSubtree* subtree);
// 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_;
VisualSubtreeMap subtree_map_;
};
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.
// Returns true if the commit is needed.
bool AddDelegatedInkVisualToTreeIfNeeded(
IDCompositionVisual2* 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 nullptr if the surface could not be created.
raw_ptr<IDCompositionSurface> GetOrCreateSolidWhiteTexture();
const bool disable_nv12_dynamic_textures_;
const bool disable_vp_auto_hdr_;
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 the largest video processor to avoid problems in
// (http://crbug.com/1121061) and (http://crbug.com/1472975).
VideoProcessorWrapper video_processor_wrapper_;
// To reduce resource usage, we keep track of the largest input/output
// dimensions for several last VideoProcessor usages. All 4 dimensions must be
// tracked separately.
ui::MovingMax max_video_processor_input_height_;
ui::MovingMax max_video_processor_input_width_;
ui::MovingMax max_video_processor_output_height_;
ui::MovingMax max_video_processor_output_width_;
// 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<IDCompositionVisual2> 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_