#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/check_is_test.h"
#include "base/containers/flat_map.h"
#include "base/moving_window.h"
#include "base/types/expected.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/overlay_layer_id.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;
}
class DelegatedInkMetadata;
}
namespace gl {
struct CommitError {
enum class Reason {
kUnknown,
kIDCompositionDeviceCommit,
kPresentToSwapChain,
kSolidColorSurfacePoolCreateSurface,
kSolidColorSurfaceBeginDraw,
kSolidColorSurfaceEndDraw,
kSolidColorSurfaceCreateRenderTargetView,
};
Reason reason = Reason::kUnknown;
std::optional<HRESULT> hr;
};
class SwapChainPresenter;
struct VideoProcessorWrapper {
VideoProcessorWrapper();
~VideoProcessorWrapper();
VideoProcessorWrapper(VideoProcessorWrapper&& other) = delete;
VideoProcessorWrapper& operator=(VideoProcessorWrapper&& other) = delete;
VideoProcessorWrapper(const VideoProcessorWrapper&) = delete;
VideoProcessorWrapper& operator=(VideoProcessorWrapper& other) = delete;
class SizeSmoother {
public:
SizeSmoother();
~SizeSmoother();
SizeSmoother(SizeSmoother&& other) = delete;
SizeSmoother& operator=(SizeSmoother&& other) = delete;
SizeSmoother(const SizeSmoother& other) = delete;
SizeSmoother& operator=(SizeSmoother& other) = delete;
void PutSize(gfx::Size size);
gfx::Size GetSize() const;
private:
base::MovingMax<int> width_;
base::MovingMax<int> height_;
};
gfx::Size video_input_size;
gfx::Size video_output_size;
SizeSmoother input_size_smoother;
SizeSmoother output_size_smoother;
bool GetDriverSupportsVpAutoHdr() { return driver_supports_vp_auto_hdr; }
void SetDriverSupportsVpAutoHdr(bool value) {
driver_supports_vp_auto_hdr = value;
}
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;
private:
bool driver_supports_vp_auto_hdr = false;
};
class SolidColorSurface;
class SolidColorSurfacePool final {
public:
SolidColorSurfacePool(
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
Microsoft::WRL::ComPtr<IDCompositionDevice3> dcomp_device);
~SolidColorSurfacePool();
SolidColorSurfacePool(const SolidColorSurfacePool&) = delete;
SolidColorSurfacePool& operator=(const SolidColorSurfacePool&) = delete;
base::expected<IDCompositionSurface*, CommitError> GetSolidColorSurface(
const SkColor4f& color);
void TrimAfterCommit();
size_t GetNumSurfacesInPoolForTesting() const;
private:
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
Microsoft::WRL::ComPtr<IDCompositionDevice3> dcomp_device_;
std::vector<SolidColorSurface> tracked_surfaces_;
size_t num_used_this_frame_ = 0;
struct Stats {
int num_surfaces_requested = 0;
int num_surfaces_recolored = 0;
};
Stats stats_since_last_trim_;
};
class GL_EXPORT DCLayerTree {
public:
using DelegatedInkRenderer = DelegatedInkPointRendererGpu;
DCLayerTree(bool disable_nv12_dynamic_textures,
bool disable_vp_auto_hdr,
bool disable_vp_scaling,
bool disable_vp_super_resolution,
bool disable_dc_letterbox_video_optimization,
bool force_dcomp_triple_buffer_video_swap_chain,
bool no_downscaled_overlay_promotion);
DCLayerTree(const DCLayerTree&) = delete;
DCLayerTree& operator=(const DCLayerTree&) = delete;
~DCLayerTree();
void Initialize(HWND window,
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device);
base::expected<void, CommitError> CommitAndClearPendingOverlays(
std::vector<DCLayerOverlayParams> overlays);
VideoProcessorWrapper* InitializeVideoProcessor(
const gfx::Size& input_size,
const gfx::Size& output_size,
bool is_hdr_output,
bool& video_processor_recreated);
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 disable_dc_letterbox_video_optimization() const {
return disable_dc_letterbox_video_optimization_;
}
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_;
}
IDXGISwapChain1* GetLayerSwapChainForTesting(
const gfx::OverlayLayerId& layer_id) const;
void GetSwapChainVisualInfoForTesting(const gfx::OverlayLayerId& layer_id,
gfx::Transform* out_transform,
gfx::Point* out_offset,
gfx::Rect* out_clip_rect) const;
size_t GetSwapChainPresenterCountForTesting() const;
size_t GetDcompLayerCountForTesting() const;
IDCompositionVisual2* GetContentVisualForTesting(
const gfx::OverlayLayerId& layer_id) const;
IDCompositionSurface* GetBackgroundColorSurfaceForTesting(
const gfx::OverlayLayerId& layer_id) const;
size_t GetNumSurfacesInPoolForTesting() const;
#if DCHECK_IS_ON()
bool DcompVisualContentChangedFromPreviousFrameForTesting(
const gfx::OverlayLayerId& layer_id) const;
#endif
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 {
CHECK_IS_TEST();
return ink_renderer_.get();
}
class VisualTree {
public:
VisualTree(DCLayerTree* tree);
VisualTree(VisualTree&&) = delete;
VisualTree(const VisualTree&) = delete;
VisualTree& operator=(const VisualTree&) = delete;
~VisualTree();
base::expected<void, CommitError> BuildTree(
const std::vector<DCLayerOverlayParams>& overlays);
void GetSwapChainVisualInfoForTesting(const gfx::OverlayLayerId& layer_id,
gfx::Transform* out_transform,
gfx::Point* out_offset,
gfx::Rect* out_clip_rect) const;
size_t GetDcompLayerCountForTesting() const;
IDCompositionVisual2* GetContentVisualForTesting(
const gfx::OverlayLayerId& layer_id) const;
IDCompositionSurface* GetBackgroundColorSurfaceForTesting(
const gfx::OverlayLayerId& layer_id) const;
#if DCHECK_IS_ON()
bool DcompVisualContentChangedFromPreviousFrameForTesting(
const gfx::OverlayLayerId& layer_id) const;
#endif
using VisualSubtreeMap = base::flat_map<raw_ptr<IUnknown>, size_t>;
class VisualSubtree {
public:
VisualSubtree();
~VisualSubtree();
VisualSubtree(VisualSubtree&& other) = delete;
VisualSubtree& operator=(VisualSubtree&& other) = delete;
VisualSubtree(const VisualSubtree&) = delete;
VisualSubtree& operator=(VisualSubtree& other) = delete;
bool Update(
IDCompositionDevice3* dcomp_device,
Microsoft::WRL::ComPtr<IUnknown> dcomp_visual_content,
uint64_t dcomp_surface_serial,
const gfx::Size& image_size,
const gfx::RectF& content_rect,
Microsoft::WRL::ComPtr<IDCompositionSurface> background_color_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 std::optional<gfx::Rect>& clip_rect_in_root,
bool allow_antialiasing);
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();
}
IDCompositionSurface* background_color_surface_for_testing() const {
CHECK_IS_TEST();
return background_color_surface_.Get();
}
void GetSwapChainVisualInfoForTesting(gfx::Transform* out_transform,
gfx::Point* out_offset,
gfx::Rect* out_clip_rect) const;
#if DCHECK_IS_ON()
bool DcompVisualContentChangedFromPreviousFrameForTesting() const {
CHECK_IS_TEST();
return dcomp_visual_content_changed_from_previous_frame_;
}
#endif
int z_order() const { return z_order_; }
void set_z_order(int z_order) { z_order_ = z_order; }
gfx::Transform GetQuadToRootTransformForTesting() const {
return quad_to_root_transform_;
}
std::optional<gfx::Rect> GetClipRectInRootForTesting() const {
return clip_rect_in_root_;
}
private:
#if DCHECK_IS_ON()
friend class VisualTree;
#endif
Microsoft::WRL::ComPtr<IDCompositionVisual2> clip_visual_;
Microsoft::WRL::ComPtr<IDCompositionVisual2> rounded_corners_visual_;
Microsoft::WRL::ComPtr<IDCompositionVisual2> transform_visual_;
Microsoft::WRL::ComPtr<IDCompositionVisual2> background_color_visual_;
Microsoft::WRL::ComPtr<IDCompositionVisual2> content_visual_;
Microsoft::WRL::ComPtr<IUnknown> dcomp_visual_content_;
uint64_t dcomp_surface_serial_ = 0;
gfx::RectF content_rect_;
Microsoft::WRL::ComPtr<IDCompositionSurface> background_color_surface_;
SkColor4f background_color_;
gfx::Rect quad_rect_;
bool nearest_neighbor_filter_ = false;
gfx::Transform quad_to_root_transform_;
std::optional<gfx::Rect> clip_rect_in_root_;
gfx::RRectF rounded_corner_bounds_;
float opacity_ = 1.0;
gfx::Size image_size_;
bool allow_antialiasing_ = true;
int z_order_ = 0;
#if DCHECK_IS_ON()
bool dcomp_visual_content_changed_from_previous_frame_ = false;
#endif
};
VisualSubtree* GetFrontMostVisualSubtreeForTesting() const;
private:
VisualSubtreeMap BuildMapAndAssignMatchingSubtrees(
const std::vector<DCLayerOverlayParams>& overlays,
std::vector<std::unique_ptr<VisualSubtree>>& visual_subtrees,
std::vector<std::optional<size_t>>& overlay_index_to_reused_subtree,
std::vector<std::optional<size_t>>& subtree_index_to_overlay);
size_t ReuseUnmatchedSubtrees(
std::vector<std::unique_ptr<VisualSubtree>>& new_visual_subtrees,
std::vector<std::optional<size_t>>& overlay_index_to_reused_subtree,
std::vector<std::optional<size_t>>& subtree_index_to_overlay);
bool DetachUnusedSubtreesFromRoot(
size_t first_prev_frame_subtree_unused_index,
std::vector<bool>& prev_subtree_is_attached_to_root);
bool DetachReusedSubtreesThatNeedRepositioningFromRoot(
const std::vector<std::unique_ptr<VisualSubtree>>& new_visual_subtrees,
const std::vector<std::optional<size_t>>&
overlay_index_to_reused_subtree,
const std::vector<std::optional<size_t>>& subtree_index_to_overlay,
std::vector<bool>& prev_subtree_is_attached_to_root);
void DetachSubtreeFromRoot(VisualSubtree* subtree);
const raw_ptr<DCLayerTree> dc_layer_tree_ = nullptr;
std::vector<std::unique_ptr<VisualSubtree>> visual_subtrees_;
VisualSubtreeMap subtree_map_;
std::vector<gfx::OverlayLayerId> layer_ids_for_testing_;
const VisualSubtree* GetSubtreeFromLayerIdForTesting(
const gfx::OverlayLayerId& layer_id) const;
};
VisualTree::VisualSubtree* GetFrontMostVideoVisualSubtreeForTesting() const;
private:
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 disable_dc_letterbox_video_optimization_;
const bool force_dcomp_triple_buffer_video_swap_chain_;
const bool no_downscaled_overlay_promotion_;
const bool tint_video_layer_;
HWND window_;
Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
Microsoft::WRL::ComPtr<IDCompositionDevice3> dcomp_device_;
Microsoft::WRL::ComPtr<IDCompositionTarget> dcomp_target_;
std::unique_ptr<SolidColorSurfacePool> solid_color_surface_pool_;
VideoProcessorWrapper video_processor_wrapper_sdr_;
VideoProcessorWrapper video_processor_wrapper_hdr_;
gfx::ColorSpace video_input_color_space_;
gfx::ColorSpace video_output_color_space_;
Microsoft::WRL::ComPtr<IDCompositionVisual2> dcomp_root_visual_;
Microsoft::WRL::ComPtr<IDCompositionDynamicTexture> primary_plane_surface_;
uint64_t primary_plane_surface_serial_ = 0;
base::flat_map<gfx::OverlayLayerId, std::unique_ptr<SwapChainPresenter>>
video_swap_chains_;
std::unique_ptr<VisualTree> visual_tree_;
std::unique_ptr<HDRMetadataHelperWin> hdr_metadata_helper_;
std::unique_ptr<DelegatedInkRenderer> ink_renderer_;
std::unique_ptr<gfx::DelegatedInkMetadata> pending_delegated_ink_metadata_;
};
}
#endif