blob: 44c0c744a9bf069740c3b97b9b6abb9f3c476707 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/ui/scenic/lib/gfx/engine/view_tree_updater.h"
#include "src/ui/scenic/lib/gfx/resources/nodes/node.h"
#include "src/ui/scenic/lib/gfx/resources/nodes/scene.h"
#include "src/ui/scenic/lib/gfx/resources/view_holder.h"
namespace scenic_impl::gfx {
ViewTreeUpdater::ViewTreeUpdater(SessionId session_id)
: session_id_(session_id), weak_factory_(this) {}
void ViewTreeUpdater::AddUpdate(ViewTreeUpdate update) {
return view_tree_updates_.push_back(std::move(update));
}
void ViewTreeUpdater::TrackViewHolder(fxl::WeakPtr<ViewHolder> view_holder) {
FXL_DCHECK(view_holder) << "precondition"; // Called in ViewHolder constructor.
const zx_koid_t koid = view_holder->view_holder_koid();
view_tree_updates_.push_back(ViewTreeNewAttachNode{.koid = koid});
auto [iter, inserted] =
tracked_view_holders_.insert({koid, ViewHolderStatus{.view_holder = std::move(view_holder)}});
FXL_DCHECK(inserted);
}
void ViewTreeUpdater::UntrackViewHolder(zx_koid_t koid) {
// Disconnection in view tree handled by DeleteNode operation.
view_tree_updates_.push_back(ViewTreeDeleteNode{.koid = koid});
auto erased_count = tracked_view_holders_.erase(koid);
FXL_DCHECK(erased_count == 1);
}
void ViewTreeUpdater::UpdateViewHolderConnections() {
for (auto& kv : tracked_view_holders_) {
const zx_koid_t koid = kv.first;
ViewHolderStatus& status = kv.second;
const std::optional<bool> prev_connected = status.connected_to_session_root;
// Each ViewHolder may have an independent intra-Session "root" that connects it upwards.
// E.g., it's legal to have multiple Scene roots connecting to independent compositors.
zx_koid_t root = ZX_KOID_INVALID;
// Determine whether each ViewHolder is connected to some root.
bool now_connected = false;
FXL_DCHECK(status.view_holder) << "invariant";
Node* curr = status.view_holder ? status.view_holder->parent() : nullptr;
while (curr) {
if (curr->session_id() != session_id_) {
break; // Exited session boundary, quit upwards search.
}
if (curr->IsKindOf<ViewNode>() && curr->As<ViewNode>()->GetView()) {
root = curr->As<ViewNode>()->GetView()->view_ref_koid();
FXL_DCHECK(root != ZX_KOID_INVALID) << "invariant";
// TODO(SCN-1249): Enable following check when one-view-per-session is enforced.
// FXL_DCHECK(root_view_ && root_view_->view_ref_koid() == root)
// << "invariant: session's root-view-discovered and root-view-purported must match.";
now_connected = true;
break;
}
if (curr->IsKindOf<Scene>()) {
root = curr->As<Scene>()->view_ref_koid();
FXL_DCHECK(root != ZX_KOID_INVALID) << "invariant";
now_connected = true;
break;
}
curr = curr->parent();
}
// <prev> <now> <action>
// none true record connect, report connect (case 1)
// none false record disconnect (case 2)
// true true (nop)
// true false record disconnect, report disconnect (case 3)
// false true record connect, report connect (case 1)
// false false (nop)
if ((!prev_connected.has_value() && now_connected) ||
(prev_connected.has_value() && !prev_connected.value() && now_connected)) {
// Case 1
status.connected_to_session_root = std::make_optional<bool>(true);
view_tree_updates_.push_back(ViewTreeConnectToParent{.child = koid, .parent = root});
} else if (!prev_connected.has_value() && !now_connected) {
// Case 2
status.connected_to_session_root = std::make_optional<bool>(false);
} else if (prev_connected.has_value() && prev_connected.value() && !now_connected) {
// Case 3
status.connected_to_session_root = std::make_optional<bool>(false);
view_tree_updates_.push_back(ViewTreeDisconnectFromParent{.koid = koid});
}
}
}
void ViewTreeUpdater::StageViewTreeUpdates(SceneGraph* scene_graph) {
scene_graph->StageViewTreeUpdates(std::move(view_tree_updates_));
view_tree_updates_.clear();
}
} // namespace scenic_impl::gfx