blob: 133360acd21acd802e6a8a966a877f856bc66269 [file] [log] [blame]
// Copyright 2013 The Flutter 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 "fake_session.h"
#include <zircon/types.h>
#include <algorithm> // For remove_if
#include <iterator> // For make_move_iterator
#include <memory>
#include "flutter/fml/logging.h"
#include "flutter/shell/platform/fuchsia/flutter/tests/fakes/scenic/fake_resources.h"
#include "fuchsia/images/cpp/fidl.h"
namespace flutter_runner::testing {
namespace {
template <typename T>
constexpr bool is_node_v = std::is_same_v<T, FakeEntityNodeState> ||
std::is_same_v<T, FakeOpacityNodeState> ||
std::is_same_v<T, FakeShapeNodeState> ||
std::is_same_v<T, FakeViewHolderState>;
template <typename T>
bool ResourceIs(const FakeResourceState& resource) {
return std::holds_alternative<T>(resource.state);
}
bool ResourceIsNode(const FakeResourceState& resource) {
return ResourceIs<FakeEntityNodeState>(resource) ||
ResourceIs<FakeOpacityNodeState>(resource) ||
ResourceIs<FakeShapeNodeState>(resource) ||
ResourceIs<FakeViewHolderState>(resource);
}
zx_koid_t GetKoid(zx_handle_t handle) {
if (handle == ZX_HANDLE_INVALID) {
return ZX_KOID_INVALID;
}
zx_info_handle_basic_t info;
zx_status_t status = zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info,
sizeof(info), nullptr, nullptr);
return status == ZX_OK ? info.koid : ZX_KOID_INVALID;
}
} // namespace
FakeSession::FakeSession() : binding_(this) {}
FakeSession::SessionAndListenerClientPair FakeSession::Bind(
async_dispatcher_t* dispatcher) {
FML_CHECK(!listener_.is_bound());
FML_CHECK(!binding_.is_bound());
fidl::InterfaceHandle<fuchsia::ui::scenic::Session> session;
auto listener_request = listener_.NewRequest(dispatcher);
binding_.Bind(session.NewRequest(), dispatcher);
return std::make_pair(std::move(session), std::move(listener_request));
}
void FakeSession::SetPresentHandler(PresentHandler present_handler) {
present_handler_ = std::move(present_handler);
}
void FakeSession::SetPresent2Handler(Present2Handler present2_handler) {
present2_handler_ = std::move(present2_handler);
}
void FakeSession::SetRequestPresentationTimesHandler(
RequestPresentationTimesHandler request_presentation_times_handler) {
request_presentation_times_handler_ =
std::move(request_presentation_times_handler);
}
void FakeSession::FireOnFramePresentedEvent(
fuchsia::scenic::scheduling::FramePresentedInfo frame_presented_info) {
FML_CHECK(is_bound());
binding_.events().OnFramePresented(std::move(frame_presented_info));
}
void FakeSession::DisconnectSession() {
// Unbind the channels and drop them on the floor, simulating Scenic behavior.
binding_.Unbind();
listener_.Unbind();
}
void FakeSession::NotImplemented_(const std::string& name) {
FML_LOG(FATAL) << "FakeSession does not implement " << name;
}
void FakeSession::Enqueue(std::vector<fuchsia::ui::scenic::Command> cmds) {
// Append `cmds` to the end of the command queue, preferring to move elements
// when possible.
command_queue_.insert(command_queue_.end(),
std::make_move_iterator(cmds.begin()),
std::make_move_iterator(cmds.end()));
}
void FakeSession::Present(uint64_t presentation_time,
std::vector<zx::event> acquire_fences,
std::vector<zx::event> release_fences,
PresentCallback callback) {
ApplyCommands();
PresentHandler present_handler =
present_handler_ ? present_handler_ : [](auto... args) -> auto {
return fuchsia::images::PresentationInfo{};
};
auto present_info = present_handler_(
presentation_time, std::move(acquire_fences), std::move(release_fences));
if (callback) {
callback(std::move(present_info));
}
}
void FakeSession::Present2(fuchsia::ui::scenic::Present2Args args,
Present2Callback callback) {
ApplyCommands();
Present2Handler present2_handler =
present2_handler_ ? present2_handler_ : [](auto args) -> auto {
return fuchsia::scenic::scheduling::FuturePresentationTimes{
.future_presentations = {},
.remaining_presents_in_flight_allowed = 1,
};
};
auto future_presentation_times = present2_handler(std::move(args));
if (callback) {
callback(std::move(future_presentation_times));
}
}
void FakeSession::RequestPresentationTimes(
int64_t requested_prediction_span,
RequestPresentationTimesCallback callback) {
RequestPresentationTimesHandler request_presentation_times_handler =
request_presentation_times_handler_ ? request_presentation_times_handler_
: [](auto args) -> auto {
return fuchsia::scenic::scheduling::FuturePresentationTimes{
.future_presentations = {},
.remaining_presents_in_flight_allowed = 1,
};
};
auto future_presentation_times =
request_presentation_times_handler(requested_prediction_span);
if (callback) {
callback(std::move(future_presentation_times));
}
}
void FakeSession::RegisterBufferCollection(
uint32_t buffer_id,
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token) {
zx_koid_t token_koid = GetKoid(token.channel().get());
auto [_, buffer_success] =
scene_graph_.buffer_collection_map.emplace(std::make_pair(
buffer_id,
StateT::HandleT<
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>>{
std::move(token), token_koid}));
FML_CHECK(buffer_success);
}
void FakeSession::DeregisterBufferCollection(uint32_t buffer_id) {
size_t erased = scene_graph_.buffer_collection_map.erase(buffer_id);
FML_CHECK(erased == 1);
}
void FakeSession::SetDebugName(std::string debug_name) {
debug_name_ = std::move(debug_name);
}
std::shared_ptr<FakeResourceState> FakeSession::GetResource(FakeResourceId id) {
FML_CHECK(id != kInvalidFakeResourceId);
auto resource_it = scene_graph_.resource_map.find(id);
FML_CHECK(resource_it != scene_graph_.resource_map.end());
auto resource_ptr = resource_it->second;
FML_CHECK(resource_ptr);
return resource_ptr;
}
void FakeSession::AddResource(FakeResourceState&& resource) {
const FakeResourceId resource_id = resource.id;
FML_CHECK(resource_id != kInvalidFakeResourceId);
// Track the view id if the resource is a view.
if (ResourceIs<FakeViewState>(resource)) {
// If there was already a View in the scene graph, scenic prints a warning
// here but doesn't update the "root view" and allows the Session to
// continue. See also: fxbug.dev/24450
if (scene_graph_.root_view_id == kInvalidFakeResourceId) {
scene_graph_.root_view_id = resource_id;
}
}
// Add to initial spot in parents map.
auto resource_ptr = std::make_shared<FakeResourceState>(
std::forward<FakeResourceState>(resource));
if (ResourceIsNode(*resource_ptr)) {
auto [_, parents_success] = parents_map_.emplace(std::make_pair(
resource_ptr.get(),
std::make_pair(std::weak_ptr<FakeResourceState>(resource_ptr),
std::weak_ptr<FakeResourceState>())));
FML_CHECK(parents_success);
}
// Add to initial spot in labels map.
auto empty_label_it = scene_graph_.label_map.find("");
if (empty_label_it == scene_graph_.label_map.end()) {
auto [emplace_it, empty_label_success] = scene_graph_.label_map.emplace(
std::make_pair("", std::vector<std::weak_ptr<FakeResourceState>>()));
FML_CHECK(empty_label_success);
empty_label_it = emplace_it;
}
empty_label_it->second.emplace_back(resource_ptr);
// Add to resource map.
auto [__, resource_success] = scene_graph_.resource_map.emplace(
std::make_pair(resource_id, std::move(resource_ptr)));
FML_CHECK(resource_success);
}
void FakeSession::DetachResourceFromParent(
std::shared_ptr<FakeResourceState> resource_ptr,
std::shared_ptr<FakeResourceState> new_parent_ptr) {
FML_CHECK(resource_ptr);
// Remove reference from the parent's `children` array.
auto parent_it = parents_map_.find(resource_ptr.get());
FML_CHECK(parent_it != parents_map_.end());
if (auto parent_ptr = parent_it->second.second.lock()) {
std::visit(
[&resource_ptr](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (is_node_v<T>) {
auto erase_it =
std::remove_if(state.node_state.children.begin(),
state.node_state.children.end(),
[&resource_ptr](const auto& resource) {
return resource == resource_ptr;
});
FML_CHECK(erase_it != state.node_state.children.end());
state.node_state.children.erase(erase_it);
} else if constexpr (std::is_same_v<T, FakeViewState>) {
auto erase_it =
std::remove_if(state.children.begin(), state.children.end(),
[&resource_ptr](const auto& resource) {
return resource == resource_ptr;
});
FML_CHECK(erase_it != state.children.end());
state.children.erase(erase_it);
} else {
FML_CHECK(false);
}
},
parent_ptr->state);
}
// Fix up the parent ptr.
if (new_parent_ptr) {
parent_it->second.second = new_parent_ptr;
} else {
parent_it->second.second = std::weak_ptr<FakeResourceState>();
}
}
void FakeSession::PruneDeletedResourceRefs() {
// Remove expired resources from the parents map.
for (auto parent_it = parents_map_.begin(), parent_end = parents_map_.end();
parent_it != parent_end;) {
if (parent_it->second.first.expired()) {
parent_it = parents_map_.erase(parent_it);
} else {
++parent_it;
}
}
// Remove expired resurces from the labels map.
for (auto scene_it = scene_graph_.label_map.begin(),
scene_end = scene_graph_.label_map.end();
scene_it != scene_end;) {
auto erase_it = std::remove_if(
scene_it->second.begin(), scene_it->second.end(),
[](const auto& weak_resource) { return weak_resource.expired(); });
if (erase_it != scene_it->second.end()) {
scene_it->second.erase(erase_it);
}
if (scene_it->second.empty()) {
scene_it = scene_graph_.label_map.erase(scene_it);
} else {
++scene_it;
}
}
}
void FakeSession::ApplyCommands() {
while (!command_queue_.empty()) {
auto scenic_command = std::move(command_queue_.front());
command_queue_.pop_front();
if (!scenic_command.is_gfx()) {
FML_LOG(FATAL) << "FakeSession: Unexpected non-gfx command (type "
<< scenic_command.Which() << ")";
continue;
}
auto& command = scenic_command.gfx();
switch (command.Which()) {
case fuchsia::ui::gfx::Command::Tag::kCreateResource:
ApplyCreateResourceCmd(std::move(command.create_resource()));
break;
case fuchsia::ui::gfx::Command::Tag::kReleaseResource:
ApplyReleaseResourceCmd(std::move(command.release_resource()));
break;
case fuchsia::ui::gfx::Command::Tag::kAddChild:
ApplyAddChildCmd(std::move(command.add_child()));
break;
case fuchsia::ui::gfx::Command::Tag::kDetach:
ApplyDetachCmd(std::move(command.detach()));
break;
case fuchsia::ui::gfx::Command::Tag::kDetachChildren:
ApplyDetachChildrenCmd(std::move(command.detach_children()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetTranslation:
ApplySetTranslationCmd(std::move(command.set_translation()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetScale:
ApplySetScaleCmd(std::move(command.set_scale()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetRotation:
ApplySetRotationCmd(std::move(command.set_rotation()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetAnchor:
ApplySetAnchorCmd(std::move(command.set_anchor()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetOpacity:
ApplySetOpacityCmd(command.set_opacity());
break;
case fuchsia::ui::gfx::Command::Tag::kSetShape:
ApplySetShapeCmd(std::move(command.set_shape()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetMaterial:
ApplySetMaterialCmd(std::move(command.set_material()));
break;
case ::fuchsia::ui::gfx::Command::Tag::kSetClipPlanes:
ApplySetClipPlanesCmd(std::move(command.set_clip_planes()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior:
ApplySetHitTestBehaviorCmd(std::move(command.set_hit_test_behavior()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetSemanticVisibility:
ApplySetSemanticVisibilityCmd(
std::move(command.set_semantic_visibility()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetViewProperties:
ApplySetViewPropertiesCmd(std::move(command.set_view_properties()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetTexture:
ApplySetTextureCmd(std::move(command.set_texture()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetColor:
ApplySetColorCmd(std::move(command.set_color()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetEventMask:
ApplySetEventMaskCmd(std::move(command.set_event_mask()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetLabel:
ApplySetLabelCmd(std::move(command.set_label()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetEnableViewDebugBounds:
ApplySetEnableViewDebugBoundsCmd(
std::move(command.set_enable_view_debug_bounds()));
break;
case fuchsia::ui::gfx::Command::Tag::kSetViewHolderBoundsColor:
ApplySetViewHolderBoundsColorCmd(
std::move(command.set_view_holder_bounds_color()));
break;
case fuchsia::ui::gfx::Command::Tag::kExportResource:
NotImplemented_("ExportResourceCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kImportResource:
NotImplemented_("ImportResourceCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetTag:
NotImplemented_("SetTagCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetSize:
NotImplemented_("SetSizeCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSendSizeChangeHintHack:
NotImplemented_("SendSizeChangedHintHackCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kAddPart:
NotImplemented_("AddPartCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetClip:
NotImplemented_("SetClipCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetCamera:
NotImplemented_("SetCameraCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetCameraTransform:
NotImplemented_("SetCameraTransformCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetCameraProjection:
NotImplemented_("SetCameraProjectionCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetStereoCameraProjection:
NotImplemented_("SetStereoCameraProjectionCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetCameraClipSpaceTransform:
NotImplemented_("SetCameraClipSpaceTransformCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetCameraPoseBuffer:
NotImplemented_("SetCameraPoseBufferCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetLightColor:
NotImplemented_("SetLightColorCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetLightDirection:
NotImplemented_("SetLightDirectionCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetPointLightPosition:
NotImplemented_("SetPointLightPositionCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetPointLightFalloff:
NotImplemented_("SetPointLightFalloffCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kAddLight:
NotImplemented_("AddLightCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kScene_AddAmbientLight:
NotImplemented_("Scene_AddAmbientLightCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kScene_AddDirectionalLight:
NotImplemented_("Scene_AddDirectionalLightCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kScene_AddPointLight:
NotImplemented_("Scene_AddPointLightCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kDetachLight:
NotImplemented_("DetachLightCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kDetachLights:
NotImplemented_("DetachLightsCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kBindMeshBuffers:
NotImplemented_("BindMeshBuffersCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kAddLayer:
NotImplemented_("AddLayerCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kRemoveLayer:
NotImplemented_("RemoveLayerCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kRemoveAllLayers:
NotImplemented_("RemoveAllLayersCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetLayerStack:
NotImplemented_("SetLayerStackCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetRenderer:
NotImplemented_("SetRendererCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetRendererParam:
NotImplemented_("SetRendererParamCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetDisableClipping:
NotImplemented_("SetDisableClippingCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetImportFocus:
NotImplemented_("SetImportFocusCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kTakeSnapshotCmd:
NotImplemented_("TakeSnapshotCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetDisplayColorConversion:
NotImplemented_("SetDisplayColorConversionCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetDisplayRotation:
NotImplemented_("SetDisplayRotationCmd");
break;
case fuchsia::ui::gfx::Command::Tag::kSetDisplayMinimumRgb:
NotImplemented_("SetDisplayMinimumRgbCmd");
break;
case fuchsia::ui::gfx::Command::Tag::Invalid:
FML_LOG(FATAL) << "FakeSession found Invalid gfx command";
break;
}
}
// Clean up resource refs after processing commands.
PruneDeletedResourceRefs();
}
void FakeSession::ApplyCreateResourceCmd(
fuchsia::ui::gfx::CreateResourceCmd command) {
const FakeResourceId resource_id = command.id;
FML_CHECK(resource_id != 0);
switch (command.resource.Which()) {
case fuchsia::ui::gfx::ResourceArgs::Tag::kMemory:
ApplyCreateMemory(resource_id, std::move(command.resource.memory()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kImage:
ApplyCreateImage(resource_id, std::move(command.resource.image()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kImage2:
ApplyCreateImage2(resource_id, std::move(command.resource.image2()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kImage3:
ApplyCreateImage3(resource_id, std::move(command.resource.image3()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kImagePipe2:
ApplyCreateImagePipe2(resource_id,
std::move(command.resource.image_pipe2()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kRectangle:
ApplyCreateRectangle(resource_id,
std::move(command.resource.rectangle()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kRoundedRectangle:
ApplyCreateRoundedRectangle(
resource_id, std::move(command.resource.rounded_rectangle()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kCircle:
ApplyCreateCircle(resource_id, std::move(command.resource.circle()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kMaterial:
ApplyCreateMaterial(resource_id, std::move(command.resource.material()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kView:
ApplyCreateView(resource_id, std::move(command.resource.view()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kView3:
ApplyCreateView(resource_id, std::move(command.resource.view3()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder:
ApplyCreateViewHolder(resource_id,
std::move(command.resource.view_holder()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kOpacityNode:
ApplyCreateOpacityNode(resource_id, command.resource.opacity_node());
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kEntityNode:
ApplyCreateEntityNode(resource_id,
std::move(command.resource.entity_node()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kShapeNode:
ApplyCreateShapeNode(resource_id,
std::move(command.resource.shape_node()));
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kBuffer:
NotImplemented_("CreateBufferResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kScene:
NotImplemented_("CreateSceneResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kCamera:
NotImplemented_("CreateCameraResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kStereoCamera:
NotImplemented_("CreateStereoCameraResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kRenderer:
NotImplemented_("CreateRendererResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kAmbientLight:
NotImplemented_("CreateAmbientLightResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kDirectionalLight:
NotImplemented_("CreateDirectionalLightResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kPointLight:
NotImplemented_("CreatePointLightResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kMesh:
NotImplemented_("CreateMeshResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kClipNode:
NotImplemented_("CreateClipNodeResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kCompositor:
NotImplemented_("CreateCompositorResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kDisplayCompositor:
NotImplemented_("CreateDisplayCompositorResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kLayerStack:
NotImplemented_("CreateLayerStackResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kLayer:
NotImplemented_("CreateLayerResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::kVariable:
NotImplemented_("CreateVariableResource");
break;
case fuchsia::ui::gfx::ResourceArgs::Tag::Invalid:
FML_LOG(FATAL) << "FakeSession found Invalid CreateResource command";
break;
default:
FML_UNREACHABLE();
}
}
void FakeSession::ApplyReleaseResourceCmd(
fuchsia::ui::gfx::ReleaseResourceCmd command) {
auto resource_ptr = GetResource(command.id);
if (ResourceIs<FakeViewState>(*resource_ptr)) {
FML_CHECK(scene_graph_.root_view_id == resource_ptr->id);
scene_graph_.root_view_id = kInvalidFakeResourceId;
}
scene_graph_.resource_map.erase(command.id);
}
void FakeSession::ApplyAddChildCmd(fuchsia::ui::gfx::AddChildCmd command) {
auto parent_node_ptr = GetResource(command.node_id);
FML_CHECK(ResourceIsNode(*parent_node_ptr) ||
ResourceIs<FakeViewState>(*parent_node_ptr));
auto child_node_ptr = GetResource(command.child_id);
FML_CHECK(ResourceIsNode(*child_node_ptr));
// Add the Node as a child of the new parent.
std::visit(
[&child_node_ptr](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (is_node_v<T>) {
state.node_state.children.emplace_back(child_node_ptr);
} else if constexpr (std::is_same_v<T, FakeViewState>) {
state.children.emplace_back(child_node_ptr);
} else {
FML_CHECK(false);
}
},
parent_node_ptr->state);
// Remove the Node as a child of the old parent and fix up the parent ptr.
DetachResourceFromParent(child_node_ptr, parent_node_ptr);
}
void FakeSession::ApplyDetachCmd(fuchsia::ui::gfx::DetachCmd command) {
auto resource_ptr = GetResource(command.id);
FML_CHECK(ResourceIsNode(*resource_ptr));
DetachResourceFromParent(std::move(resource_ptr));
}
void FakeSession::ApplyDetachChildrenCmd(
fuchsia::ui::gfx::DetachChildrenCmd command) {
auto resource_ptr = GetResource(command.node_id);
FML_CHECK(ResourceIsNode(*resource_ptr));
std::visit(
[this](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (is_node_v<T>) {
for (auto& child : state.node_state.children) {
DetachResourceFromParent(child);
}
state.node_state.children.clear();
} else {
FML_CHECK(false);
}
},
resource_ptr->state);
}
void FakeSession::ApplySetTranslationCmd(
fuchsia::ui::gfx::SetTranslationCmd command) {
auto resource_ptr = GetResource(command.id);
FML_CHECK(ResourceIsNode(*resource_ptr));
const std::array<float, 3> translation = {
command.value.value.x, command.value.value.y, command.value.value.z};
std::visit(
[&translation](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (is_node_v<T>) {
state.node_state.translation_vector = translation;
} else {
FML_CHECK(false);
}
},
resource_ptr->state);
}
void FakeSession::ApplySetScaleCmd(fuchsia::ui::gfx::SetScaleCmd command) {
auto resource_ptr = GetResource(command.id);
FML_CHECK(ResourceIsNode(*resource_ptr));
const std::array<float, 3> scale = {
command.value.value.x, command.value.value.y, command.value.value.z};
std::visit(
[&scale](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (is_node_v<T>) {
state.node_state.scale_vector = scale;
} else {
FML_CHECK(false);
}
},
resource_ptr->state);
}
void FakeSession::ApplySetRotationCmd(
fuchsia::ui::gfx::SetRotationCmd command) {
auto resource_ptr = GetResource(command.id);
FML_CHECK(ResourceIsNode(*resource_ptr));
const std::array<float, 4> rotation = {
command.value.value.x, command.value.value.y, command.value.value.z,
command.value.value.w};
std::visit(
[&rotation](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (is_node_v<T>) {
state.node_state.rotation_quaternion = rotation;
} else {
FML_CHECK(false);
}
},
resource_ptr->state);
}
void FakeSession::ApplySetAnchorCmd(fuchsia::ui::gfx::SetAnchorCmd command) {
auto resource_ptr = GetResource(command.id);
FML_CHECK(ResourceIsNode(*resource_ptr));
const std::array<float, 3> anchor = {
command.value.value.x, command.value.value.y, command.value.value.z};
std::visit(
[&anchor](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (is_node_v<T>) {
state.node_state.anchor_vector = anchor;
} else {
FML_CHECK(false);
}
},
resource_ptr->state);
}
void FakeSession::ApplySetOpacityCmd(fuchsia::ui::gfx::SetOpacityCmd command) {
auto resource_ptr = GetResource(command.node_id);
FML_CHECK(ResourceIs<FakeOpacityNodeState>(*resource_ptr));
const bool opacity = command.opacity;
std::visit(
[opacity](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (std::is_same_v<T, FakeOpacityNodeState>) {
state.opacity = opacity;
} else {
FML_CHECK(false);
}
},
resource_ptr->state);
}
void FakeSession::ApplySetShapeCmd(fuchsia::ui::gfx::SetShapeCmd command) {
auto shape_node_ptr = GetResource(command.node_id);
FML_CHECK(ResourceIs<FakeShapeNodeState>(*shape_node_ptr));
auto* shape_node_state =
std::get_if<FakeShapeNodeState>(&shape_node_ptr->state);
FML_CHECK(shape_node_state != nullptr);
auto shape_ptr = GetResource(command.shape_id);
FML_CHECK(ResourceIs<FakeShapeState>(*shape_ptr));
shape_node_state->shape = shape_ptr;
}
void FakeSession::ApplySetMaterialCmd(
fuchsia::ui::gfx::SetMaterialCmd command) {
auto shape_node_ptr = GetResource(command.node_id);
FML_CHECK(ResourceIs<FakeShapeNodeState>(*shape_node_ptr));
auto* shape_node_state =
std::get_if<FakeShapeNodeState>(&shape_node_ptr->state);
FML_CHECK(shape_node_state != nullptr);
auto material_ptr = GetResource(command.material_id);
FML_CHECK(ResourceIs<FakeMaterialState>(*material_ptr));
shape_node_state->material = material_ptr;
}
void FakeSession::ApplySetClipPlanesCmd(
fuchsia::ui::gfx::SetClipPlanesCmd command) {
auto node_ptr = GetResource(command.node_id);
FML_CHECK(ResourceIs<FakeEntityNodeState>(*node_ptr));
std::vector<FakeEntityNodeState::ClipPlane> clip_planes;
for (auto& clip_plane : command.clip_planes) {
clip_planes.emplace_back(FakeEntityNodeState::ClipPlane{
.dir = {clip_plane.dir.x, clip_plane.dir.y, clip_plane.dir.z},
.dist = clip_plane.dist,
});
}
std::visit(
[clip_planes = std::move(clip_planes)](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (std::is_same_v<T, FakeEntityNodeState>) {
state.clip_planes = std::move(clip_planes);
} else {
FML_CHECK(false);
}
},
node_ptr->state);
}
void FakeSession::ApplySetViewPropertiesCmd(
fuchsia::ui::gfx::SetViewPropertiesCmd command) {
auto view_holder_ptr = GetResource(command.view_holder_id);
FML_CHECK(ResourceIs<FakeViewHolderState>(*view_holder_ptr));
auto* view_holder_state =
std::get_if<FakeViewHolderState>(&view_holder_ptr->state);
FML_CHECK(view_holder_state != nullptr);
view_holder_state->properties = command.properties;
}
void FakeSession::ApplySetHitTestBehaviorCmd(
fuchsia::ui::gfx::SetHitTestBehaviorCmd command) {
auto node_ptr = GetResource(command.node_id);
FML_CHECK(ResourceIsNode(*node_ptr));
const bool hit_testable =
command.hit_test_behavior == fuchsia::ui::gfx::HitTestBehavior::kDefault;
std::visit(
[hit_testable](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (is_node_v<T>) {
state.node_state.hit_testable = hit_testable;
} else {
FML_CHECK(false);
}
},
node_ptr->state);
}
void FakeSession::ApplySetSemanticVisibilityCmd(
fuchsia::ui::gfx::SetSemanticVisibilityCmd command) {
auto node_ptr = GetResource(command.node_id);
FML_CHECK(ResourceIsNode(*node_ptr));
const bool semantic_visibility = command.visible;
std::visit(
[semantic_visibility](auto&& state) {
using T = std::decay_t<decltype(state)>;
if constexpr (is_node_v<T>) {
state.node_state.semantically_visible = semantic_visibility;
} else {
FML_CHECK(false);
}
},
node_ptr->state);
}
void FakeSession::ApplySetTextureCmd(fuchsia::ui::gfx::SetTextureCmd command) {
auto material_ptr = GetResource(command.material_id);
FML_CHECK(ResourceIs<FakeMaterialState>(*material_ptr));
auto* material_state = std::get_if<FakeMaterialState>(&material_ptr->state);
FML_CHECK(material_state != nullptr);
auto image_ptr = GetResource(command.texture_id);
FML_CHECK(ResourceIs<FakeImageState>(*image_ptr));
material_state->image = image_ptr;
}
void FakeSession::ApplySetColorCmd(fuchsia::ui::gfx::SetColorCmd command) {
auto material_ptr = GetResource(command.material_id);
FML_CHECK(ResourceIs<FakeMaterialState>(*material_ptr));
auto* material_state = std::get_if<FakeMaterialState>(&material_ptr->state);
FML_CHECK(material_state != nullptr);
material_state->color = {(command.color.value.red * 1.f) / 255.f,
(command.color.value.green * 1.f) / 255.f,
(command.color.value.blue * 1.f) / 255.f,
(command.color.value.alpha * 1.f) / 255.f};
}
void FakeSession::ApplySetEventMaskCmd(
fuchsia::ui::gfx::SetEventMaskCmd command) {
auto resource_ptr = GetResource(command.id);
resource_ptr->event_mask = command.event_mask;
}
void FakeSession::ApplySetLabelCmd(fuchsia::ui::gfx::SetLabelCmd command) {
auto resource_ptr = GetResource(command.id);
// Erase from old spot in the labels map.
auto current_label_it = scene_graph_.label_map.find(resource_ptr->label);
FML_CHECK(current_label_it != scene_graph_.label_map.end());
auto current_erase_it = std::remove_if(
current_label_it->second.begin(), current_label_it->second.end(),
[&resource_ptr](const auto& weak_resource) {
return resource_ptr == weak_resource.lock();
});
FML_CHECK(current_erase_it != current_label_it->second.end());
current_label_it->second.erase(current_erase_it);
// Add to new spot in labels map.
auto new_label_it = scene_graph_.label_map.find(command.label);
if (new_label_it == scene_graph_.label_map.end()) {
auto [emplace_it, current_label_success] =
scene_graph_.label_map.emplace(std::make_pair(
command.label, std::vector<std::weak_ptr<FakeResourceState>>()));
FML_CHECK(current_label_success);
new_label_it = emplace_it;
}
new_label_it->second.emplace_back(resource_ptr);
resource_ptr->label = std::move(command.label);
}
void FakeSession::ApplySetEnableViewDebugBoundsCmd(
fuchsia::ui::gfx::SetEnableDebugViewBoundsCmd command) {
auto view_ptr = GetResource(command.view_id);
FML_CHECK(ResourceIs<FakeViewState>(*view_ptr));
auto* view_state = std::get_if<FakeViewState>(&view_ptr->state);
FML_CHECK(view_state != nullptr);
view_state->enable_debug_bounds = command.enable;
}
void FakeSession::ApplySetViewHolderBoundsColorCmd(
fuchsia::ui::gfx::SetViewHolderBoundsColorCmd command) {
auto view_holder_ptr = GetResource(command.view_holder_id);
FML_CHECK(ResourceIs<FakeViewHolderState>(*view_holder_ptr));
auto* view_holder_state =
std::get_if<FakeViewHolderState>(&view_holder_ptr->state);
FML_CHECK(view_holder_state != nullptr);
view_holder_state->bounds_color = {command.color.value.red,
command.color.value.green,
command.color.value.blue, 1.f};
}
void FakeSession::ApplyCreateMemory(FakeResourceId id,
fuchsia::ui::gfx::MemoryArgs args) {
zx_koid_t vmo_koid = GetKoid(args.vmo.get());
AddResource(
{.id = id,
.state = FakeMemoryState{
.vmo = {std::move(args.vmo), vmo_koid},
.allocation_size = args.allocation_size,
.is_device_memory = (args.memory_type ==
fuchsia::images::MemoryType::VK_DEVICE_MEMORY),
}});
}
void FakeSession::ApplyCreateImage(FakeResourceId id,
fuchsia::ui::gfx::ImageArgs args) {
AddResource({.id = id,
.state = FakeImageState{
.image_def =
FakeImageState::ImageDef{
.info = std::move(args.info),
.memory_offset = args.memory_offset,
},
}});
// Hook up the memory resource to the image
auto image_ptr = GetResource(id);
FML_CHECK(ResourceIs<FakeImageState>(*image_ptr));
auto* image_state = std::get_if<FakeImageState>(&image_ptr->state);
FML_CHECK(image_state != nullptr);
auto memory_ptr = GetResource(args.memory_id);
FML_CHECK(ResourceIs<FakeMemoryState>(*memory_ptr));
image_state->memory = memory_ptr;
}
void FakeSession::ApplyCreateImage2(FakeResourceId id,
fuchsia::ui::gfx::ImageArgs2 args) {
AddResource(
{.id = id,
.state = FakeImageState{
.image_def =
FakeImageState::Image2Def{
.buffer_collection_id = args.buffer_collection_id,
.buffer_collection_index = args.buffer_collection_index,
.width = args.width,
.height = args.height,
},
}});
}
void FakeSession::ApplyCreateImage3(FakeResourceId id,
fuchsia::ui::gfx::ImageArgs3 args) {
zx_koid_t import_token_koid = GetKoid(args.import_token.value.get());
AddResource(
{.id = id,
.state = FakeImageState{
.image_def =
FakeImageState::Image3Def{
.import_token = {std::move(args.import_token),
import_token_koid},
.buffer_collection_index = args.buffer_collection_index,
.width = args.width,
.height = args.height,
},
}});
}
void FakeSession::ApplyCreateImagePipe2(FakeResourceId id,
fuchsia::ui::gfx::ImagePipe2Args args) {
zx_koid_t image_pipe_request_koid =
GetKoid(args.image_pipe_request.channel().get());
AddResource(
{.id = id,
.state = FakeImageState{
.image_def =
FakeImageState::ImagePipe2Def{
.image_pipe_request = {std::move(args.image_pipe_request),
image_pipe_request_koid},
},
}});
}
void FakeSession::ApplyCreateRectangle(FakeResourceId id,
fuchsia::ui::gfx::RectangleArgs args) {
FML_CHECK(args.width.is_vector1());
FML_CHECK(args.height.is_vector1());
AddResource({.id = id,
.state = FakeShapeState{
.shape_def =
FakeShapeState::RectangleDef{
.width = args.width.vector1(),
.height = args.height.vector1(),
},
}});
}
void FakeSession::ApplyCreateRoundedRectangle(
FakeResourceId id,
fuchsia::ui::gfx::RoundedRectangleArgs args) {
FML_CHECK(args.width.is_vector1());
FML_CHECK(args.height.is_vector1());
FML_CHECK(args.top_left_radius.is_vector1());
FML_CHECK(args.top_right_radius.is_vector1());
FML_CHECK(args.bottom_right_radius.is_vector1());
FML_CHECK(args.bottom_left_radius.is_vector1());
AddResource(
{.id = id,
.state = FakeShapeState{
.shape_def =
FakeShapeState::RoundedRectangleDef{
.width = args.width.vector1(),
.height = args.height.vector1(),
.top_left_radius = args.top_left_radius.vector1(),
.top_right_radius = args.top_right_radius.vector1(),
.bottom_right_radius = args.bottom_right_radius.vector1(),
.bottom_left_radius = args.bottom_left_radius.vector1(),
},
}});
}
void FakeSession::ApplyCreateCircle(FakeResourceId id,
fuchsia::ui::gfx::CircleArgs args) {
FML_CHECK(args.radius.is_vector1());
AddResource({.id = id,
.state = FakeShapeState{
.shape_def =
FakeShapeState::CircleDef{
.radius = args.radius.vector1(),
},
}});
}
void FakeSession::ApplyCreateMaterial(FakeResourceId id,
fuchsia::ui::gfx::MaterialArgs args) {
AddResource({
.id = id,
.state = FakeMaterialState{},
});
}
void FakeSession::ApplyCreateView(FakeResourceId id,
fuchsia::ui::gfx::ViewArgs args) {
FML_CHECK(scene_graph_.root_view_id == kInvalidFakeResourceId);
zx_koid_t token_koid = GetKoid(args.token.value.get());
AddResource({.id = id,
.state = FakeViewState{
.token = {std::move(args.token), token_koid},
.debug_name = std::string(args.debug_name->c_str(),
args.debug_name->length()),
}});
}
void FakeSession::ApplyCreateView(FakeResourceId id,
fuchsia::ui::gfx::ViewArgs3 args) {
zx_koid_t token_koid = GetKoid(args.token.value.get());
zx_koid_t control_ref_koid = GetKoid(args.control_ref.reference.get());
zx_koid_t view_ref_koid = GetKoid(args.view_ref.reference.get());
AddResource(
{.id = id,
.state = FakeViewState{
.token = {std::move(args.token), token_koid},
.control_ref = {std::move(args.control_ref), control_ref_koid},
.view_ref = {std::move(args.view_ref), view_ref_koid},
.debug_name =
std::string(args.debug_name->c_str(), args.debug_name->length()),
}});
}
void FakeSession::ApplyCreateViewHolder(FakeResourceId id,
fuchsia::ui::gfx::ViewHolderArgs args) {
zx_koid_t token_koid = GetKoid(args.token.value.get());
AddResource({
.id = id,
.state =
FakeViewHolderState{
.token = {std::move(args.token), token_koid},
.debug_name = std::string(args.debug_name->c_str(),
args.debug_name->length()),
},
});
}
void FakeSession::ApplyCreateEntityNode(FakeResourceId id,
fuchsia::ui::gfx::EntityNodeArgs args) {
AddResource({
.id = id,
.state = FakeEntityNodeState{},
});
}
void FakeSession::ApplyCreateOpacityNode(
FakeResourceId id,
fuchsia::ui::gfx::OpacityNodeArgsHACK args) {
AddResource({
.id = id,
.state = FakeOpacityNodeState{},
});
}
void FakeSession::ApplyCreateShapeNode(FakeResourceId id,
fuchsia::ui::gfx::ShapeNodeArgs args) {
AddResource({
.id = id,
.state = FakeShapeNodeState{},
});
}
} // namespace flutter_runner::testing