0

[TreesInViz] Implement UIResource sync to LayerContextImpl

This change implements synchronization of UIResources to the Viz-side
LayerContextImpl. Creation & deletion are tracked for each UIResourceId,
and changes are wired during UpdateDisplayTree.

Usage of these resources in Viz is tested in an upcoming dependent CL.

Bug: 388877791
Change-Id: Ie9c02fd0914b349bce7a1ad627c55f957f3295a9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6532287
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Reviewed-by: Jonathan Ross <jonross@chromium.org>
Reviewed-by: Matthew Denton <mpdenton@chromium.org>
Commit-Queue: Victor Miura <vmiura@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1459446}
This commit is contained in:
Victor Miura
2025-05-13 08:08:32 -07:00
committed by Chromium LUCI CQ
parent ad4ae119b8
commit e9c3aa9b5f
10 changed files with 269 additions and 1 deletions

@ -471,6 +471,7 @@ cc_component("cc") {
public_deps = [
"//cc/base",
"//cc/mojom:layer_type",
"//cc/mojom:ui_resource_id",
"//cc/paint",
"//components/viz/common",
"//skia",

@ -396,6 +396,38 @@ viz::mojom::TransformTreeUpdatePtr ComputeTransformTreePropertiesUpdate(
return wire;
}
void SerializeUIResourceRequest(
cc::LayerTreeHostImpl& host_impl,
viz::RasterContextProvider& context_provider,
viz::mojom::LayerTreeUpdate& update,
cc::UIResourceId uid,
viz::mojom::TransferableUIResourceRequest::Type type) {
if (type == viz::mojom::TransferableUIResourceRequest::Type::kCreate) {
std::vector<viz::ResourceId> ids;
std::vector<viz::TransferableResource> resources;
viz::ResourceId resource_id = host_impl.ResourceIdForUIResource(uid);
bool opaque = host_impl.IsUIResourceOpaque(uid);
ids.push_back(resource_id);
host_impl.resource_provider()->PrepareSendToParent(ids, &resources,
&context_provider);
CHECK_EQ(resources.size(), ids.size());
auto& request = update.ui_resource_requests.emplace_back(
viz::mojom::TransferableUIResourceRequest::New());
request->type = type;
request->uid = uid;
request->transferable_resource = resources[0];
request->opaque = opaque;
} else {
CHECK_EQ(type, viz::mojom::TransferableUIResourceRequest::Type::kDelete);
auto& request = update.ui_resource_requests.emplace_back(
viz::mojom::TransferableUIResourceRequest::New());
request->type = type;
request->uid = uid;
}
}
viz::mojom::TileResourcePtr SerializeTileResource(
const Tile& tile,
viz::ClientResourceProvider& resource_provider,
@ -942,6 +974,23 @@ void VizLayerContext::UpdateDisplayTreeFrom(
update->viewport_damage_rect = viewport_damage_rect;
// Sync changes to UI resources
{
auto resource_changes = host_impl_->TakeUIResourceChanges(needs_full_sync_);
for (const auto& [uid, change] : resource_changes) {
if (change.resource_deleted) {
SerializeUIResourceRequest(
*host_impl_, context_provider, *update, uid,
viz::mojom::TransferableUIResourceRequest::Type::kDelete);
}
if (change.resource_created) {
SerializeUIResourceRequest(
*host_impl_, context_provider, *update, uid,
viz::mojom::TransferableUIResourceRequest::Type::kCreate);
}
}
}
// This flag will be set if and only if a new layer list was pushed to the
// active tree during activation, implying that at least one layer addition or
// removal happened since our last update. In this case only, we push the full

@ -119,6 +119,25 @@ mojom("layer_type") {
sources = [ "layer_type.mojom" ]
}
mojom("ui_resource_id") {
generate_java = true
sources = [ "ui_resource_id.mojom" ]
cpp_typemaps = [
{
types = [
{
mojom = "cc.mojom.UIResourceId"
cpp = "::cc::UIResourceId"
nullable_is_same_type = true
copyable_pass_by_value = true
},
]
traits_headers = [ "ui_resource_id_mojom_traits.h" ]
},
]
}
mojom("hit_test_opaqueness") {
generate_java = true
sources = [ "hit_test_opaqueness.mojom" ]

@ -0,0 +1,9 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
module cc.mojom;
struct UIResourceId {
int32 value;
};

@ -0,0 +1,35 @@
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CC_MOJOM_UI_RESOURCE_ID_MOJOM_TRAITS_H_
#define CC_MOJOM_UI_RESOURCE_ID_MOJOM_TRAITS_H_
#include "cc/mojom/ui_resource_id.mojom-shared.h"
#include "cc/resources/ui_resource_client.h"
namespace mojo {
template <>
struct StructTraits<cc::mojom::UIResourceIdDataView, cc::UIResourceId> {
static int32_t value(const cc::UIResourceId& id) {
// We cannot send resource ids that are uninitialized.
DCHECK_NE(id, cc::UIResourceClient::kUninitializedUIResourceId);
return static_cast<int32_t>(id);
}
static bool Read(cc::mojom::UIResourceIdDataView data,
cc::UIResourceId* out) {
cc::UIResourceId result(data.value());
// We cannot receive resource ids that are uninitialized.
if (result == cc::UIResourceClient::kUninitializedUIResourceId) {
return false;
}
*out = result;
return true;
}
};
} // namespace mojo
#endif // CC_MOJOM_UI_RESOURCE_ID_MOJOM_TRAITS_H_

@ -4342,6 +4342,25 @@ std::unique_ptr<MutatorEvents> LayerTreeHostImpl::TakeMutatorEvents() {
return events;
}
UIResourceChangeMap LayerTreeHostImpl::TakeUIResourceChanges(
bool require_full_sync) {
if (require_full_sync) {
ui_resource_changes_.clear();
if (settings_.TreesInVizInClientProcess()) {
// Mark all ui resources as created in this change set.
for (auto& pair : ui_resource_map_) {
UIResourceId uid = pair.first;
auto [change_it, success] =
ui_resource_changes_.try_emplace(uid, UIResourceChange());
change_it->second.resource_created = true;
}
}
}
return std::move(ui_resource_changes_);
}
void LayerTreeHostImpl::ClearHistory() {
client_->ClearHistory();
}
@ -5346,6 +5365,9 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid,
const UIResourceBitmap& bitmap) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"LayerTreeHostImpl::CreateUIResource");
// We expect only CreateUIResourceFromImportedResource to be used for
// trees_in_viz_in_viz_process mode.
DCHECK(!settings_.trees_in_viz_in_viz_process);
DCHECK_GT(uid, 0);
// Allow for multiple creation requests with the same UIResourceId. The
@ -5546,6 +5568,51 @@ void LayerTreeHostImpl::CreateUIResource(UIResourceId uid,
ui_resource_map_[uid] = std::move(data);
MarkUIResourceNotEvicted(uid);
if (settings_.TreesInVizInClientProcess()) {
auto [change_it, success] =
ui_resource_changes_.try_emplace(uid, UIResourceChange());
// Mark that a resource was created in this change set.
change_it->second.resource_created = true;
}
}
void LayerTreeHostImpl::CreateUIResourceFromImportedResource(
UIResourceId uid,
viz::ResourceId resource_id,
bool is_opaque) {
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"),
"LayerTreeHostImpl::CreateUIResourceFromResource");
// We expect only CreateUIResource to be used for
// non-trees_in_viz_in_viz_process mode.
DCHECK(settings_.trees_in_viz_in_viz_process);
DCHECK_GT(uid, 0);
// Allow for multiple creation requests with the same UIResourceId. The
// previous resource is simply deleted.
viz::ResourceId id = ResourceIdForUIResource(uid);
if (id) {
DeleteUIResource(uid);
}
if (!has_valid_layer_tree_frame_sink_) {
evicted_ui_resources_.insert(uid);
return;
}
UIResourceData data;
data.opaque = is_opaque;
data.resource_id_for_export = resource_id;
ui_resource_map_[uid] = std::move(data);
MarkUIResourceNotEvicted(uid);
if (settings_.TreesInVizInClientProcess()) {
auto [change_it, success] =
ui_resource_changes_.try_emplace(uid, UIResourceChange());
// Mark that a resource was created in this change set.
change_it->second.resource_created = true;
}
}
void LayerTreeHostImpl::DeleteUIResource(UIResourceId uid) {
@ -5562,6 +5629,19 @@ void LayerTreeHostImpl::DeleteUIResource(UIResourceId uid) {
ui_resource_map_.erase(it);
resource_provider_->RemoveImportedResource(id);
if (settings_.TreesInVizInClientProcess()) {
auto [change_it, success] =
ui_resource_changes_.try_emplace(uid, UIResourceChange());
// If a resource was marked as created in this change set, unmark it.
// Otherwise, the resource must have been created in a prior change set,
// so we mark it as requiring deletion.
if (change_it->second.resource_created) {
change_it->second.resource_created = false;
} else {
change_it->second.resource_deleted = true;
}
}
}
MarkUIResourceNotEvicted(uid);
}

@ -124,6 +124,13 @@ class TaskGraphRunner;
class UIResourceBitmap;
class Viewport;
struct UIResourceChange {
bool resource_created : 1 = false;
bool resource_deleted : 1 = false;
};
using UIResourceChangeMap = std::unordered_map<UIResourceId, UIResourceChange>;
// LayerTreeHostImpl owns the LayerImpl trees as well as associated rendering
// state.
class CC_EXPORT LayerTreeHostImpl : public TileManagerClient,
@ -731,6 +738,10 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient,
virtual void CreateUIResource(UIResourceId uid,
const UIResourceBitmap& bitmap);
virtual void CreateUIResourceFromImportedResource(UIResourceId uid,
viz::ResourceId resource_id,
bool is_opaque);
// Deletes a UI resource. May safely be called more than once.
virtual void DeleteUIResource(UIResourceId uid);
// Evict all UI resources. This differs from ClearUIResources in that this
@ -812,6 +823,7 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient,
std::vector<std::pair<int, bool>> TakeCompletedImageDecodeRequests();
// Returns mutator events to be handled by BeginMainFrame.
std::unique_ptr<MutatorEvents> TakeMutatorEvents();
UIResourceChangeMap TakeUIResourceChanges(bool require_full_sync);
void ClearHistory();
size_t CommitDurationSampleCountForTesting() const;
@ -1075,6 +1087,10 @@ class CC_EXPORT LayerTreeHostImpl : public TileManagerClient,
// associated with them anymore, as that is freed at the time of eviction.
std::set<UIResourceId> evicted_ui_resources_;
// When using a layer context for display, this tracks changes to UIResources
// that should be synchronized to the layer context.
UIResourceChangeMap ui_resource_changes_;
// These are valid when has_valid_layer_tree_frame_sink_ is true.
//
// A pointer used for communicating with and submitting output to the display

@ -1343,6 +1343,36 @@ base::expected<void, std::string> LayerContextImpl::DoUpdateDisplayTree(
host_impl_->SetViewportDamage(update->viewport_damage_rect);
for (auto& ui_resource_request : update->ui_resource_requests) {
if (ui_resource_request->type ==
mojom::TransferableUIResourceRequest::Type::kCreate) {
if (!ui_resource_request->transferable_resource ||
ui_resource_request->transferable_resource->is_empty()) {
return base::unexpected(
"Invalid transferable resource in UI resource creation");
}
ReleaseCallback release_callback = base::BindOnce(
[](cc::LayerTreeHostImpl* host_impl, ResourceId id,
const gpu::SyncToken& sync_token, bool is_lost) {
host_impl->ReturnResource({id, sync_token,
/*release_fence=*/gfx::GpuFenceHandle(),
/*count=*/1, is_lost});
},
host_impl_.get(), ui_resource_request->transferable_resource->id);
auto resource_id = host_impl_->resource_provider()->ImportResource(
ui_resource_request->transferable_resource.value(),
/*impl_release_callback=*/std::move(release_callback),
/*main_thread_release_callback=*/base::NullCallback(),
/*evicted_callback=*/base::NullCallback());
host_impl_->CreateUIResourceFromImportedResource(
ui_resource_request->uid, resource_id, ui_resource_request->opaque);
} else {
host_impl_->DeleteUIResource(ui_resource_request->uid);
}
}
property_trees.UpdateChangeTracking();
property_trees.transform_tree_mutable().set_needs_update(
transform_size_changed || transform_properties_changed ||

@ -70,6 +70,7 @@ mojom("mojom") {
"//cc/mojom:hit_test_opaqueness",
"//cc/mojom:layer_type",
"//cc/mojom:paint_flags",
"//cc/mojom:ui_resource_id",
"//gpu/ipc/common:interfaces",
"//media/mojo/mojom",
"//mojo/public/mojom/base",

@ -4,17 +4,42 @@
module viz.mojom;
import "cc/mojom/ui_resource_id.mojom";
import "services/viz/public/mojom/compositing/animation.mojom";
import "services/viz/public/mojom/compositing/begin_frame_args.mojom";
import "services/viz/public/mojom/compositing/layer.mojom";
import "services/viz/public/mojom/compositing/local_surface_id.mojom";
import "services/viz/public/mojom/compositing/tiling.mojom";
import "services/viz/public/mojom/compositing/resource_id.mojom";
import "services/viz/public/mojom/compositing/surface_range.mojom";
import "services/viz/public/mojom/compositing/tiling.mojom";
import "services/viz/public/mojom/compositing/transferable_resource.mojom";
import "services/viz/public/mojom/compositing/view_transition_request.mojom";
import "skia/public/mojom/skcolor4f.mojom";
import "ui/gfx/geometry/mojom/geometry.mojom";
import "ui/gfx/mojom/display_color_spaces.mojom";
// Describes a request to create or delete a UIResource in the service-side
// display tree.
struct TransferableUIResourceRequest {
// The type of request.
enum Type {
kCreate,
kDelete
};
Type type;
// The UIResourceId used to reference the UIResource for creation or
// deletion. cc::Layers reference resources by this Id.
cc.mojom.UIResourceId uid;
// The UI resource to be created should use the following
// TransferableResource contents.
TransferableResource? transferable_resource;
// The resource's pixel content should be treated as opaque for rendering.
bool opaque;
};
// Metadata and contents of a service-side display tree to be updated each time
// the corresponding client layer tree activates.
//
@ -79,6 +104,9 @@ struct LayerTreeUpdate {
// The tree's viewport damage rect.
gfx.mojom.Rect viewport_damage_rect;
// UIResource change requests accumulated since the last LayerTreeUpdate.
array<TransferableUIResourceRequest> ui_resource_requests;
// Properties of layers added or modified since the last update. Note that the
// ordering of this array is arbitrary and not relevant to the tree's layer
// list order. If any layers were added, removed, or otherwise reordered in