0

Refactored VisualSubtree's into VisualTree inner class.

This change is a preparation step to implement incremental tree
updates and getting rid of needs_rebuild_visual_tree_.

Changes made:
- Introduced VisulTree inner class of DCLayerTree. Now VisualTree
contains VisualSubtree's.
- Moved logic responsible for building dcomp tree from
 DCLayerTree::BuildVisualTreeHelper to VisualTree::UpdateTree.
- Removed DCLayerTree::root_surface_visual_. This reference is only
used to add DelegatedInkVisual to the tree. Now this visual is locally
cached within DCLayerTree::VisualTree::UpdateTree.
- Introduced kDCVisualTreeOptimization feature. In subsequent CLs
incremental tree updates will be implemented behind this feature.

Bug: 1380822
Change-Id: If546629ed09d98f44e9826c8beec815653da79cd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4051988
Reviewed-by: Sunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Commit-Queue: Olga Gerchikov <gerchiko@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#1111973}
This commit is contained in:
Olga Gerchikov
2023-03-02 01:46:06 +00:00
committed by Chromium LUCI CQ
parent 6d581494f2
commit 39e482689d
2 changed files with 252 additions and 163 deletions

@ -8,6 +8,7 @@
#include <utility>
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_functions.h"
#include "base/trace_event/trace_event.h"
@ -38,6 +39,10 @@ bool NeedSwapChainPresenter(const DCLayerOverlayParams* overlay) {
}
}
// TODO(http://crbug.com/1380822): Implement dcomp visual tree optimization.
BASE_FEATURE(kDCVisualTreeOptimization,
"DCVisualTreeOptimization",
base::FEATURE_DISABLED_BY_DEFAULT);
} // namespace
VideoProcessorWrapper::VideoProcessorWrapper() = default;
@ -191,24 +196,16 @@ void DCLayerTree::GetSwapChainVisualInfoForTesting(size_t index,
gfx::Transform* transform,
gfx::Point* offset,
gfx::Rect* clip_rect) const {
for (size_t i = 0, swapchain_i = 0; i < visual_subtrees_.size(); ++i) {
// Skip root layer.
if (visual_subtrees_[i]->z_order() == 0)
continue;
if (swapchain_i == index) {
visual_subtrees_[i]->GetSwapChainVisualInfoForTesting( // IN-TEST
transform, offset, clip_rect);
return;
}
swapchain_i++;
if (visual_tree_) {
visual_tree_->GetSwapChainVisualInfoForTesting(index, transform, // IN-TEST
offset, clip_rect);
}
}
DCLayerTree::VisualSubtree::VisualSubtree() = default;
DCLayerTree::VisualSubtree::~VisualSubtree() = default;
DCLayerTree::VisualTree::VisualSubtree::VisualSubtree() = default;
DCLayerTree::VisualTree::VisualSubtree::~VisualSubtree() = default;
bool DCLayerTree::VisualSubtree::Update(
bool DCLayerTree::VisualTree::VisualSubtree::Update(
IDCompositionDevice2* dcomp_device,
Microsoft::WRL::ComPtr<IUnknown> dcomp_visual_content,
uint64_t dcomp_surface_serial,
@ -306,7 +303,7 @@ bool DCLayerTree::VisualSubtree::Update(
return needs_commit;
}
void DCLayerTree::VisualSubtree::GetSwapChainVisualInfoForTesting(
void DCLayerTree::VisualTree::VisualSubtree::GetSwapChainVisualInfoForTesting(
gfx::Transform* transform,
gfx::Point* offset,
gfx::Rect* clip_rect) const {
@ -315,6 +312,114 @@ void DCLayerTree::VisualSubtree::GetSwapChainVisualInfoForTesting(
*clip_rect = clip_rect_.value_or(gfx::Rect());
}
DCLayerTree::VisualTree::VisualTree(DCLayerTree* dc_layer_tree)
: dc_layer_tree_(dc_layer_tree) {}
DCLayerTree::VisualTree::~VisualTree() = default;
bool DCLayerTree::VisualTree::UpdateTree(
const std::vector<std::unique_ptr<DCLayerOverlayParams>>& overlays,
bool needs_rebuild_visual_tree) {
// Grow or shrink list of visual subtrees to match pending overlays.
size_t old_visual_subtrees_size = visual_subtrees_.size();
if (old_visual_subtrees_size != overlays.size()) {
visual_subtrees_.resize(overlays.size());
needs_rebuild_visual_tree = true;
}
// Visual for root surface. Cache it to add DelegatedInk visual if needed.
Microsoft::WRL::ComPtr<IDCompositionVisual2> root_surface_visual;
bool needs_commit = false;
// Build or update visual subtree for each overlay.
for (size_t i = 0; i < overlays.size(); ++i) {
DCHECK(visual_subtrees_[i] || i >= old_visual_subtrees_size);
if (!visual_subtrees_[i]) {
visual_subtrees_[i] = std::make_unique<VisualSubtree>();
}
if (visual_subtrees_[i]->z_order() != overlays[i]->z_order) {
visual_subtrees_[i]->set_z_order(overlays[i]->z_order);
// Z-order is a property of the root visual's child list, not any property
// on the subtree's nodes. If it changes, we need to rebuild the tree.
needs_rebuild_visual_tree = true;
}
// We don't need to set |needs_rebuild_visual_tree| here since that is only
// needed when the root visual's children need to be reordered. |Update|
// only affects the subtree for each child, so only a commit is needed in
// this case.
needs_commit |= visual_subtrees_[i]->Update(
dc_layer_tree_->dcomp_device_.Get(),
overlays[i]->overlay_image->dcomp_visual_content(),
overlays[i]->overlay_image->dcomp_surface_serial(),
overlays[i]->quad_rect.OffsetFromOrigin(), overlays[i]->transform,
overlays[i]->clip_rect);
// Zero z_order represents root layer.
if (overlays[i]->z_order == 0) {
// Verify we have single root visual layer.
DCHECK(!root_surface_visual);
root_surface_visual = visual_subtrees_[i]->content_visual();
}
}
// Note: needs_rebuild_visual_tree might be set in this method,
// |DCLayerTree::CommitAndClearPendingOverlays|, and can also be set in
// |DCLayerTree::SetDelegatedInkTrailStartPoint| to add a delegated ink visual
// into the root surface's visual.
if (needs_rebuild_visual_tree) {
TRACE_EVENT0(
"gpu", "DCLayerTree::CommitAndClearPendingOverlays::ReBuildVisualTree");
// Rebuild root visual's child list.
dc_layer_tree_->dcomp_root_visual_->RemoveAllVisuals();
for (size_t i = 0; i < visual_subtrees_.size(); ++i) {
// We call AddVisual with insertAbove FALSE and referenceVisual nullptr
// which is equivalent to saying that the visual should be below no
// other visual, or in other words it should be above all other visuals.
dc_layer_tree_->dcomp_root_visual_->AddVisual(
visual_subtrees_[i]->container_visual(), FALSE, nullptr);
}
dc_layer_tree_->AddDelegatedInkVisualToTreeIfNeeded(
root_surface_visual.Get());
needs_commit = true;
}
if (needs_commit) {
TRACE_EVENT0("gpu", "DCLayerTree::CommitAndClearPendingOverlays::Commit");
HRESULT hr = dc_layer_tree_->dcomp_device_->Commit();
if (FAILED(hr)) {
DLOG(ERROR) << "Commit failed with error 0x" << std::hex << hr;
return false;
}
}
return true;
}
void DCLayerTree::VisualTree::GetSwapChainVisualInfoForTesting(
size_t index,
gfx::Transform* transform,
gfx::Point* offset,
gfx::Rect* clip_rect) const {
for (size_t i = 0, swapchain_i = 0; i < visual_subtrees_.size(); ++i) {
// Skip root layer.
if (visual_subtrees_[i]->z_order() == 0) {
continue;
}
if (swapchain_i == index) {
visual_subtrees_[i]->GetSwapChainVisualInfoForTesting( // IN-TEST
transform, offset, clip_rect);
return;
}
swapchain_i++;
}
}
bool DCLayerTree::CommitAndClearPendingOverlays(
DirectCompositionChildSurfaceWin* root_surface) {
TRACE_EVENT1("gpu", "DCLayerTree::CommitAndClearPendingOverlays",
@ -438,96 +543,33 @@ bool DCLayerTree::CommitAndClearPendingOverlays(
bool DCLayerTree::BuildVisualTreeHelper(
const std::vector<std::unique_ptr<DCLayerOverlayParams>>& overlays,
bool needs_rebuild_visual_tree) {
// Grow or shrink list of visual subtrees to match pending overlays.
size_t old_visual_subtrees_size = visual_subtrees_.size();
if (old_visual_subtrees_size != overlays.size()) {
visual_subtrees_.resize(overlays.size());
needs_rebuild_visual_tree = true;
// TODO(http://crbug.com/1380822): Enable optimization when delegated ink
// trails is active.
bool use_visual_tree_optimization =
base::FeatureList::IsEnabled(kDCVisualTreeOptimization) &&
!ink_renderer_->HasBeenInitialized();
// Optimized and not optimized trees are incompatible and cannot be reused
// for incremental updates. Rebuild visual tree if switching between optimized
// and not optimized trees or vice versa. It will be removed once delegated
// ink trails work with optimized DCOMP trees.
if (visual_tree_ &&
use_visual_tree_optimization != visual_tree_->tree_optimized()) {
visual_tree_ = nullptr;
}
#if DCHECK_IS_ON()
bool root_surface_visual_updated = false;
#endif
bool needs_commit = false;
// Build or update visual subtree for each overlay.
for (size_t i = 0; i < overlays.size(); ++i) {
DCHECK(visual_subtrees_[i] || i >= old_visual_subtrees_size);
if (!visual_subtrees_[i])
visual_subtrees_[i] = std::make_unique<VisualSubtree>();
if (visual_subtrees_[i]->z_order() != overlays[i]->z_order) {
visual_subtrees_[i]->set_z_order(overlays[i]->z_order);
// Z-order is a property of the root visual's child list, not any property
// on the subtree's nodes. If it changes, we need to rebuild the tree.
needs_rebuild_visual_tree = true;
}
// We don't need to set |needs_rebuild_visual_tree_| here since that is only
// needed when the root visual's children need to be reordered. |Update|
// only affects the subtree for each child, so only a commit is needed in
// this case.
needs_commit |= visual_subtrees_[i]->Update(
dcomp_device_.Get(), overlays[i]->overlay_image->dcomp_visual_content(),
overlays[i]->overlay_image->dcomp_surface_serial(),
overlays[i]->quad_rect.OffsetFromOrigin(), overlays[i]->transform,
overlays[i]->clip_rect);
// Zero z_order represents root layer.
if (overlays[i]->z_order == 0) {
DCHECK(root_surface_visual_.Get() ==
visual_subtrees_[i]->content_visual() ||
needs_rebuild_visual_tree);
#if DCHECK_IS_ON()
// Verify we have single root visual layer.
DCHECK(!root_surface_visual_updated);
root_surface_visual_updated = true;
#endif
root_surface_visual_ = visual_subtrees_[i]->content_visual();
}
// TODO(http://crbug.com/1380822): Implement tree optimization where the
// tree is built incrementally and does not require full rebuild.
if (use_visual_tree_optimization) {
NOTREACHED();
return false;
}
// Rebuild root visual's child list.
// Note: needs_rebuild_visual_tree might be set in the caller, this function
// and can also be set in DCLayerTree::SetDelegatedInkTrailStartPoint to add a
// delegated ink visual into the root surface's visual.
if (needs_rebuild_visual_tree) {
TRACE_EVENT0(
"gpu", "DCLayerTree::CommitAndClearPendingOverlays::ReBuildVisualTree");
dcomp_root_visual_->RemoveAllVisuals();
for (size_t i = 0; i < visual_subtrees_.size(); ++i) {
// We call AddVisual with insertAbove FALSE and referenceVisual nullptr
// which is equivalent to saying that the visual should be below no
// other visual, or in other words it should be above all other visuals.
dcomp_root_visual_->AddVisual(visual_subtrees_[i]->container_visual(),
FALSE, nullptr);
}
// Only add the ink visual to the tree if it has already been initialized.
// It will only have been initialized if delegated ink has been used, so
// this ensures the visual is only added when it is needed. The ink renderer
// must be updated so that if the root swap chain or dcomp device have
// changed the ink visual and delegated ink object can be updated
// accordingly.
if (ink_renderer_->HasBeenInitialized()) {
// Reinitialize the ink renderer in case the root swap chain or dcomp
// device changed since initialization.
if (InitializeInkRenderer())
AddDelegatedInkVisualToTree();
}
needs_commit = true;
if (!visual_tree_) {
visual_tree_ = std::make_unique<VisualTree>(this);
}
if (needs_commit) {
TRACE_EVENT0("gpu", "DCLayerTree::CommitAndClearPendingOverlays::Commit");
HRESULT hr = dcomp_device_->Commit();
if (FAILED(hr)) {
DLOG(ERROR) << "Commit failed with error 0x" << std::hex << hr;
return false;
}
}
return true;
return visual_tree_->UpdateTree(overlays, needs_rebuild_visual_tree);
}
bool DCLayerTree::ScheduleDCLayer(
@ -550,13 +592,26 @@ bool DCLayerTree::InitializeInkRenderer() {
return ink_renderer_->Initialize(dcomp_device_, root_swap_chain_);
}
void DCLayerTree::AddDelegatedInkVisualToTree() {
void DCLayerTree::AddDelegatedInkVisualToTreeIfNeeded(
IDCompositionVisual2* root_surface_visual) {
// Only add the ink visual to the tree if it has already been initialized.
// It will only have been initialized if delegated ink has been used, so
// this ensures the visual is only added when it is needed. The ink renderer
// must be updated so that if the root swap chain or dcomp device have
// changed the ink visual and delegated ink object can be updated
// accordingly.
if (!ink_renderer_->HasBeenInitialized()) {
return;
}
// Reinitialize the ink renderer in case the root swap chain or dcomp
// device changed since initialization.
if (!InitializeInkRenderer()) {
return;
}
DCHECK(SupportsDelegatedInk());
DCHECK(ink_renderer_->HasBeenInitialized());
root_surface_visual_->AddVisual(ink_renderer_->GetInkVisual(), FALSE,
nullptr);
root_surface_visual->AddVisual(ink_renderer_->GetInkVisual(), FALSE, nullptr);
// Adding the ink visual to a new visual tree invalidates all previously set
// properties. Therefore, force update.
ink_renderer_->SetNeedsDcompPropertiesUpdate();

@ -147,66 +147,101 @@ class DCLayerTree {
return ink_renderer_.get();
}
// 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 {
// Owns a list of |VisualSubtree|s that represent visual layers.
class VisualTree {
public:
VisualSubtree();
~VisualSubtree();
VisualSubtree(VisualSubtree&& other) = delete;
VisualSubtree& operator=(VisualSubtree&& other) = delete;
VisualSubtree(const VisualSubtree&) = delete;
VisualSubtree& operator=(VisualSubtree& other) = delete;
VisualTree(DCLayerTree* tree);
// Returns true if something was changed.
bool Update(IDCompositionDevice2* dcomp_device,
Microsoft::WRL::ComPtr<IUnknown> dcomp_visual_content,
uint64_t dcomp_surface_serial,
const gfx::Vector2d& quad_rect_offset,
const gfx::Transform& quad_to_root_transform,
const absl::optional<gfx::Rect>& clip_rect_in_root);
VisualTree(VisualTree&&) = delete;
VisualTree(const VisualTree&) = delete;
VisualTree& operator=(const VisualTree&) = delete;
IDCompositionVisual2* container_visual() const {
return clip_visual_.Get();
}
IDCompositionVisual2* content_visual() const {
return content_visual_.Get();
}
void GetSwapChainVisualInfoForTesting(gfx::Transform* transform,
~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;
// 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_; }
int z_order() const { return z_order_; }
void set_z_order(int z_order) { z_order_ = z_order; }
// 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(IDCompositionDevice2* dcomp_device,
Microsoft::WRL::ComPtr<IUnknown> dcomp_visual_content,
uint64_t dcomp_surface_serial,
const gfx::Vector2d& quad_rect_offset,
const gfx::Transform& quad_to_root_transform,
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();
}
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:
Microsoft::WRL::ComPtr<IDCompositionVisual2> clip_visual_;
Microsoft::WRL::ComPtr<IDCompositionVisual2> 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;
// Offset of the top left of the visual in quad space
gfx::Vector2d offset_;
// Transform from quad space to root space
gfx::Transform transform_;
// Clip rect in root space
absl::optional<gfx::Rect> clip_rect_;
// 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:
Microsoft::WRL::ComPtr<IDCompositionVisual2> clip_visual_;
Microsoft::WRL::ComPtr<IDCompositionVisual2> 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;
// Offset of the top left of the visual in quad space
gfx::Vector2d offset_;
// Transform from quad space to root space
gfx::Transform transform_;
// Clip rect in root space
absl::optional<gfx::Rect> clip_rect_;
// 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;
// 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:
@ -221,7 +256,8 @@ class DCLayerTree {
// 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 AddDelegatedInkVisualToTree();
void 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.
@ -257,9 +293,6 @@ class DCLayerTree {
// Set if root surface is using a direct composition surface currently.
Microsoft::WRL::ComPtr<IDCompositionSurface> root_dcomp_surface_;
// Direct composition visual for root surface.
Microsoft::WRL::ComPtr<IDCompositionVisual2> root_surface_visual_;
// Root direct composition visual for window dcomp target.
Microsoft::WRL::ComPtr<IDCompositionVisual2> dcomp_root_visual_;
@ -269,8 +302,9 @@ class DCLayerTree {
// List of swap chain presenters for previous frame.
std::vector<std::unique_ptr<SwapChainPresenter>> video_swap_chains_;
// List of DCOMP visual subtrees for previous frame.
std::vector<std::unique_ptr<VisualSubtree>> visual_subtrees_;
// 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;