blob: d5b26e685a075d204d4ea45dcad072b77b1d6ed9 [file] [log] [blame]
// Copyright 2017 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 "garnet/lib/ui/gfx/engine/session.h"
#include <memory>
#include <utility>
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <trace/event.h>
#include "garnet/lib/ui/gfx/engine/hit_tester.h"
#include "garnet/lib/ui/gfx/engine/session_handler.h"
#include "garnet/lib/ui/gfx/resources/buffer.h"
#include "garnet/lib/ui/gfx/resources/camera.h"
#include "garnet/lib/ui/gfx/resources/compositor/display_compositor.h"
#include "garnet/lib/ui/gfx/resources/compositor/layer.h"
#include "garnet/lib/ui/gfx/resources/compositor/layer_stack.h"
#include "garnet/lib/ui/gfx/resources/image.h"
#include "garnet/lib/ui/gfx/resources/image_pipe.h"
#include "garnet/lib/ui/gfx/resources/image_pipe_handler.h"
#include "garnet/lib/ui/gfx/resources/lights/ambient_light.h"
#include "garnet/lib/ui/gfx/resources/lights/directional_light.h"
#include "garnet/lib/ui/gfx/resources/memory.h"
#include "garnet/lib/ui/gfx/resources/nodes/entity_node.h"
#include "garnet/lib/ui/gfx/resources/nodes/node.h"
#include "garnet/lib/ui/gfx/resources/nodes/opacity_node.h"
#include "garnet/lib/ui/gfx/resources/nodes/scene.h"
#include "garnet/lib/ui/gfx/resources/nodes/shape_node.h"
#include "garnet/lib/ui/gfx/resources/renderers/renderer.h"
#include "garnet/lib/ui/gfx/resources/shapes/circle_shape.h"
#include "garnet/lib/ui/gfx/resources/shapes/mesh_shape.h"
#include "garnet/lib/ui/gfx/resources/shapes/rectangle_shape.h"
#include "garnet/lib/ui/gfx/resources/shapes/rounded_rectangle_shape.h"
#include "garnet/lib/ui/gfx/resources/snapshot/snapshotter.h"
#include "garnet/lib/ui/gfx/resources/stereo_camera.h"
#include "garnet/lib/ui/gfx/resources/variable.h"
#include "garnet/lib/ui/gfx/resources/view.h"
#include "garnet/lib/ui/gfx/resources/view_holder.h"
#include "garnet/lib/ui/gfx/util/time.h"
#include "garnet/lib/ui/gfx/util/unwrap.h"
#include "garnet/lib/ui/gfx/util/wrap.h"
#include "garnet/lib/ui/scenic/util/print_command.h"
#include "lib/escher/hmd/pose_buffer.h"
#include "lib/escher/renderer/batch_gpu_uploader.h"
#include "lib/escher/shape/mesh.h"
#include "lib/escher/shape/rounded_rect_factory.h"
#include "lib/escher/util/type_utils.h"
namespace scenic_impl {
namespace gfx {
namespace {
// Makes it convenient to check that a value is constant and of a specific type,
// or a variable.
// TODO: There should also be a convenient way of type-checking a variable;
// this will necessarily involve looking up the value in the ResourceMap.
constexpr std::array<::fuchsia::ui::gfx::Value::Tag, 2> kFloatValueTypes{
{::fuchsia::ui::gfx::Value::Tag::kVector1,
::fuchsia::ui::gfx::Value::Tag::kVariableId}};
// Converts the provided vector of Hits into a fidl array of HitPtrs.
fidl::VectorPtr<::fuchsia::ui::gfx::Hit> WrapHits(
const std::vector<Hit>& hits) {
fidl::VectorPtr<::fuchsia::ui::gfx::Hit> wrapped_hits;
wrapped_hits.resize(hits.size());
for (size_t i = 0; i < hits.size(); ++i) {
const Hit& hit = hits[i];
::fuchsia::ui::gfx::Hit wrapped_hit;
wrapped_hit.tag_value = hit.tag_value;
wrapped_hit.ray_origin = Wrap(hit.ray.origin);
wrapped_hit.ray_direction = Wrap(hit.ray.direction);
wrapped_hit.inverse_transform = Wrap(hit.inverse_transform);
wrapped_hit.distance = hit.distance;
wrapped_hits->at(i) = std::move(wrapped_hit);
}
return wrapped_hits;
}
} // anonymous namespace
Session::Session(SessionId id, Engine* engine, EventReporter* event_reporter,
ErrorReporter* error_reporter)
: id_(id),
engine_(engine),
error_reporter_(error_reporter),
event_reporter_(event_reporter),
resources_(error_reporter),
weak_factory_(this) {
FXL_DCHECK(engine);
FXL_DCHECK(error_reporter);
}
Session::~Session() { FXL_DCHECK(!is_valid_); }
bool Session::ApplyCommand(::fuchsia::ui::gfx::Command command) {
TRACE_DURATION("gfx", "Session::ApplyCommand");
switch (command.Which()) {
case ::fuchsia::ui::gfx::Command::Tag::kCreateResource:
return ApplyCreateResourceCmd(std::move(command.create_resource()));
case ::fuchsia::ui::gfx::Command::Tag::kReleaseResource:
return ApplyReleaseResourceCmd(std::move(command.release_resource()));
case ::fuchsia::ui::gfx::Command::Tag::kExportResource:
return ApplyExportResourceCmd(std::move(command.export_resource()));
case ::fuchsia::ui::gfx::Command::Tag::kImportResource:
return ApplyImportResourceCmd(std::move(command.import_resource()));
case ::fuchsia::ui::gfx::Command::Tag::kSetImportFocus: {
// TODO(SCN-1026): Remove this.
if (auto import = resources_.FindResource<Import>(
command.set_import_focus().id,
ResourceMap::ErrorBehavior::kDontReportErrors)) {
import->set_focusable(command.set_import_focus().focusable);
return true;
}
return false;
}
case ::fuchsia::ui::gfx::Command::Tag::kAddChild:
return ApplyAddChildCmd(std::move(command.add_child()));
case ::fuchsia::ui::gfx::Command::Tag::kAddPart:
return ApplyAddPartCmd(std::move(command.add_part()));
case ::fuchsia::ui::gfx::Command::Tag::kDetach:
return ApplyDetachCmd(std::move(command.detach()));
case ::fuchsia::ui::gfx::Command::Tag::kDetachChildren:
return ApplyDetachChildrenCmd(std::move(command.detach_children()));
case ::fuchsia::ui::gfx::Command::Tag::kSetTag:
return ApplySetTagCmd(std::move(command.set_tag()));
case ::fuchsia::ui::gfx::Command::Tag::kSetTranslation:
return ApplySetTranslationCmd(std::move(command.set_translation()));
case ::fuchsia::ui::gfx::Command::Tag::kSetScale:
return ApplySetScaleCmd(std::move(command.set_scale()));
case ::fuchsia::ui::gfx::Command::Tag::kSetRotation:
return ApplySetRotationCmd(std::move(command.set_rotation()));
case ::fuchsia::ui::gfx::Command::Tag::kSetAnchor:
return ApplySetAnchorCmd(std::move(command.set_anchor()));
case ::fuchsia::ui::gfx::Command::Tag::kSetSize:
return ApplySetSizeCmd(std::move(command.set_size()));
case ::fuchsia::ui::gfx::Command::Tag::kSetOpacity:
return ApplySetOpacityCmd(command.set_opacity());
case ::fuchsia::ui::gfx::Command::Tag::kSendSizeChangeHintHack:
return ApplySendSizeChangeHintCmd(command.send_size_change_hint_hack());
case ::fuchsia::ui::gfx::Command::Tag::kSetShape:
return ApplySetShapeCmd(std::move(command.set_shape()));
case ::fuchsia::ui::gfx::Command::Tag::kSetMaterial:
return ApplySetMaterialCmd(std::move(command.set_material()));
case ::fuchsia::ui::gfx::Command::Tag::kSetClip:
return ApplySetClipCmd(std::move(command.set_clip()));
case ::fuchsia::ui::gfx::Command::Tag::kSetHitTestBehavior:
return ApplySetHitTestBehaviorCmd(
std::move(command.set_hit_test_behavior()));
case ::fuchsia::ui::gfx::Command::Tag::kSetViewProperties:
return ApplySetViewPropertiesCmd(
std::move(command.set_view_properties()));
case ::fuchsia::ui::gfx::Command::Tag::kSetCamera:
return ApplySetCameraCmd(std::move(command.set_camera()));
case ::fuchsia::ui::gfx::Command::Tag::kSetCameraTransform:
return ApplySetCameraTransformCmd(
std::move(command.set_camera_transform()));
case ::fuchsia::ui::gfx::Command::Tag::kSetCameraProjection:
return ApplySetCameraProjectionCmd(
std::move(command.set_camera_projection()));
case ::fuchsia::ui::gfx::Command::Tag::kSetStereoCameraProjection:
return ApplySetStereoCameraProjectionCmd(
std::move(command.set_stereo_camera_projection()));
case ::fuchsia::ui::gfx::Command::Tag::kSetCameraPoseBuffer:
return ApplySetCameraPoseBufferCmd(
std::move(command.set_camera_pose_buffer()));
case ::fuchsia::ui::gfx::Command::Tag::kSetLightColor:
return ApplySetLightColorCmd(std::move(command.set_light_color()));
case ::fuchsia::ui::gfx::Command::Tag::kSetLightDirection:
return ApplySetLightDirectionCmd(
std::move(command.set_light_direction()));
case ::fuchsia::ui::gfx::Command::Tag::kAddLight:
return ApplyAddLightCmd(std::move(command.add_light()));
case ::fuchsia::ui::gfx::Command::Tag::kDetachLight:
return ApplyDetachLightCmd(std::move(command.detach_light()));
case ::fuchsia::ui::gfx::Command::Tag::kDetachLights:
return ApplyDetachLightsCmd(std::move(command.detach_lights()));
case ::fuchsia::ui::gfx::Command::Tag::kSetTexture:
return ApplySetTextureCmd(std::move(command.set_texture()));
case ::fuchsia::ui::gfx::Command::Tag::kSetColor:
return ApplySetColorCmd(std::move(command.set_color()));
case ::fuchsia::ui::gfx::Command::Tag::kBindMeshBuffers:
return ApplyBindMeshBuffersCmd(std::move(command.bind_mesh_buffers()));
case ::fuchsia::ui::gfx::Command::Tag::kAddLayer:
return ApplyAddLayerCmd(std::move(command.add_layer()));
case ::fuchsia::ui::gfx::Command::Tag::kRemoveLayer:
return ApplyRemoveLayerCmd(std::move(command.remove_layer()));
case ::fuchsia::ui::gfx::Command::Tag::kRemoveAllLayers:
return ApplyRemoveAllLayersCmd(std::move(command.remove_all_layers()));
case ::fuchsia::ui::gfx::Command::Tag::kSetLayerStack:
return ApplySetLayerStackCmd(std::move(command.set_layer_stack()));
case ::fuchsia::ui::gfx::Command::Tag::kSetRenderer:
return ApplySetRendererCmd(std::move(command.set_renderer()));
case ::fuchsia::ui::gfx::Command::Tag::kSetRendererParam:
return ApplySetRendererParamCmd(std::move(command.set_renderer_param()));
case ::fuchsia::ui::gfx::Command::Tag::kSetEventMask:
return ApplySetEventMaskCmd(std::move(command.set_event_mask()));
case ::fuchsia::ui::gfx::Command::Tag::kSetLabel:
return ApplySetLabelCmd(std::move(command.set_label()));
case ::fuchsia::ui::gfx::Command::Tag::kSetDisableClipping:
return ApplySetDisableClippingCmd(
std::move(command.set_disable_clipping()));
case ::fuchsia::ui::gfx::Command::Tag::kTakeSnapshotCmd:
return ApplyTakeSnapshotCmdHACK(std::move(command.take_snapshot_cmd()));
case ::fuchsia::ui::gfx::Command::Tag::Invalid:
// FIDL validation should make this impossible.
FXL_CHECK(false);
return false;
}
}
bool Session::ApplyCreateResourceCmd(
::fuchsia::ui::gfx::CreateResourceCmd command) {
const ResourceId id = command.id;
if (id == 0) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplyCreateResourceCmd(): invalid ID: "
<< command;
return false;
}
switch (command.resource.Which()) {
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kMemory:
return ApplyCreateMemory(id, std::move(command.resource.memory()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kImage:
return ApplyCreateImage(id, std::move(command.resource.image()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kImagePipe:
return ApplyCreateImagePipe(id, std::move(command.resource.image_pipe()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kBuffer:
return ApplyCreateBuffer(id, std::move(command.resource.buffer()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kScene:
return ApplyCreateScene(id, std::move(command.resource.scene()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kCamera:
return ApplyCreateCamera(id, std::move(command.resource.camera()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kStereoCamera:
return ApplyCreateStereoCamera(
id, std::move(command.resource.stereo_camera()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kRenderer:
return ApplyCreateRenderer(id, std::move(command.resource.renderer()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kAmbientLight:
return ApplyCreateAmbientLight(
id, std::move(command.resource.ambient_light()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kDirectionalLight:
return ApplyCreateDirectionalLight(
id, std::move(command.resource.directional_light()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kRectangle:
return ApplyCreateRectangle(id, std::move(command.resource.rectangle()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kRoundedRectangle:
return ApplyCreateRoundedRectangle(
id, std::move(command.resource.rounded_rectangle()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kCircle:
return ApplyCreateCircle(id, std::move(command.resource.circle()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kMesh:
return ApplyCreateMesh(id, std::move(command.resource.mesh()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kMaterial:
return ApplyCreateMaterial(id, std::move(command.resource.material()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kView:
return ApplyCreateView(id, std::move(command.resource.view()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kViewHolder:
return ApplyCreateViewHolder(id,
std::move(command.resource.view_holder()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kClipNode:
return ApplyCreateClipNode(id, std::move(command.resource.clip_node()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kOpacityNode:
return ApplyCreateOpacityNode(id, command.resource.opacity_node());
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kEntityNode:
return ApplyCreateEntityNode(id,
std::move(command.resource.entity_node()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kShapeNode:
return ApplyCreateShapeNode(id, std::move(command.resource.shape_node()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kCompositor:
return ApplyCreateCompositor(id,
std::move(command.resource.compositor()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kDisplayCompositor:
return ApplyCreateDisplayCompositor(
id, std::move(command.resource.display_compositor()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kImagePipeCompositor:
return ApplyCreateImagePipeCompositor(
id, std::move(command.resource.image_pipe_compositor()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kLayerStack:
return ApplyCreateLayerStack(id,
std::move(command.resource.layer_stack()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kLayer:
return ApplyCreateLayer(id, std::move(command.resource.layer()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::kVariable:
return ApplyCreateVariable(id, std::move(command.resource.variable()));
case ::fuchsia::ui::gfx::ResourceArgs::Tag::Invalid:
// FIDL validation should make this impossible.
FXL_CHECK(false);
return false;
}
}
bool Session::ApplyReleaseResourceCmd(
::fuchsia::ui::gfx::ReleaseResourceCmd command) {
return resources_.RemoveResource(command.id);
}
bool Session::ApplyExportResourceCmd(
::fuchsia::ui::gfx::ExportResourceCmd command) {
if (!command.token) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplyExportResourceCmd(): "
"no token provided.";
return false;
}
if (auto resource = resources_.FindResource<Resource>(command.id)) {
return engine_->resource_linker()->ExportResource(resource.get(),
std::move(command.token));
}
return false;
}
bool Session::ApplyImportResourceCmd(
::fuchsia::ui::gfx::ImportResourceCmd command) {
if (!command.token) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplyImportResourceCmd(): "
"no token provided.";
return false;
}
ImportPtr import =
fxl::MakeRefCounted<Import>(this, command.id, command.spec);
return engine_->resource_linker()->ImportResource(import.get(), command.spec,
std::move(command.token)) &&
resources_.AddResource(command.id, std::move(import));
}
bool Session::ApplyAddChildCmd(::fuchsia::ui::gfx::AddChildCmd command) {
// Find the parent and child nodes. We can add:
// - Nodes to Nodes
// - ViewHolders to Nodes
// - Nodes to Views
// TODO(SCN-795): Split these out into separate commands?
if (auto parent = resources_.FindResource<Node>(
command.node_id, ResourceMap::ErrorBehavior::kDontReportErrors)) {
if (auto child = resources_.FindResource<Node>(
command.child_id, ResourceMap::ErrorBehavior::kDontReportErrors)) {
return parent->AddChild(std::move(child));
} else if (auto child =
resources_.FindResource<ViewHolder>(command.child_id)) {
return parent->AddViewHolder(std::move(child));
}
} else if (auto parent = resources_.FindResource<View>(command.node_id)) {
if (auto child = resources_.FindResource<Node>(command.child_id)) {
return parent->AddChild(std::move(child));
}
}
return false;
}
bool Session::ApplyAddPartCmd(::fuchsia::ui::gfx::AddPartCmd command) {
// Find the parent and part nodes.
if (auto parent_node = resources_.FindResource<Node>(command.node_id)) {
if (auto part_node = resources_.FindResource<Node>(command.part_id)) {
return parent_node->AddPart(std::move(part_node));
}
}
return false;
}
bool Session::ApplyTakeSnapshotCmdHACK(
::fuchsia::ui::gfx::TakeSnapshotCmdHACK command) {
async::PostTask(
async_get_default_dispatcher(), [weak = weak_factory_.GetWeakPtr(),
command = std::move(command)]() mutable {
if (!weak) {
if (auto callback = command.callback.Bind()) {
// TODO(SCN-978): Return an error to the caller for invalid data.
callback->OnData(fuchsia::mem::Buffer{});
}
return;
}
auto engine = weak->engine_;
Resource* resource = nullptr;
if (auto node = weak->resources_.FindResource<Node>(command.node_id)) {
resource = node.get();
} else if (command.node_id == 0) {
resource = engine->GetFirstCompositor();
} else {
if (auto callback = command.callback.Bind()) {
callback->OnData(fuchsia::mem::Buffer{});
}
return;
}
auto gpu_uploader =
escher::BatchGpuUploader::New(engine->GetEscherWeakPtr());
Snapshotter snapshotter(gpu_uploader);
// Take a snapshot and return the data in callback. The closure does
// not need the snapshotter instance and is invoked after the instance
// is destroyed.
snapshotter.TakeSnapshot(resource, [callback = command.callback.Bind()](
fuchsia::mem::Buffer snapshot) {
callback->OnData(std::move(snapshot));
});
});
return true;
}
bool Session::ApplyDetachCmd(::fuchsia::ui::gfx::DetachCmd command) {
if (auto resource = resources_.FindResource<Resource>(command.id)) {
return resource->Detach();
}
return false;
}
bool Session::ApplyDetachChildrenCmd(
::fuchsia::ui::gfx::DetachChildrenCmd command) {
if (auto node = resources_.FindResource<Node>(command.node_id)) {
return node->DetachChildren();
}
return false;
}
bool Session::ApplySetTagCmd(::fuchsia::ui::gfx::SetTagCmd command) {
if (auto node = resources_.FindResource<Node>(command.node_id)) {
return node->SetTagValue(command.tag_value);
}
return false;
}
bool Session::ApplySetTranslationCmd(
::fuchsia::ui::gfx::SetTranslationCmd command) {
if (auto node = resources_.FindResource<Node>(command.id)) {
if (IsVariable(command.value)) {
if (auto variable = resources_.FindVariableResource<Vector3Variable>(
command.value.variable_id)) {
return node->SetTranslation(variable);
}
} else {
return node->SetTranslation(UnwrapVector3(command.value));
}
}
return false;
}
bool Session::ApplySetScaleCmd(::fuchsia::ui::gfx::SetScaleCmd command) {
if (auto node = resources_.FindResource<Node>(command.id)) {
if (IsVariable(command.value)) {
if (auto variable = resources_.FindVariableResource<Vector3Variable>(
command.value.variable_id)) {
return node->SetScale(variable);
}
} else {
return node->SetScale(UnwrapVector3(command.value));
}
}
return false;
}
bool Session::ApplySetRotationCmd(::fuchsia::ui::gfx::SetRotationCmd command) {
if (auto node = resources_.FindResource<Node>(command.id)) {
if (IsVariable(command.value)) {
if (auto variable = resources_.FindVariableResource<QuaternionVariable>(
command.value.variable_id)) {
return node->SetRotation(variable);
}
} else {
return node->SetRotation(UnwrapQuaternion(command.value));
}
}
return false;
}
bool Session::ApplySetAnchorCmd(::fuchsia::ui::gfx::SetAnchorCmd command) {
if (auto node = resources_.FindResource<Node>(command.id)) {
if (IsVariable(command.value)) {
if (auto variable = resources_.FindVariableResource<Vector3Variable>(
command.value.variable_id)) {
return node->SetAnchor(variable);
}
}
return node->SetAnchor(UnwrapVector3(command.value));
}
return false;
}
bool Session::ApplySetSizeCmd(::fuchsia::ui::gfx::SetSizeCmd command) {
if (auto layer = resources_.FindResource<Layer>(command.id)) {
if (IsVariable(command.value)) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetSizeCmd(): "
"unimplemented for variable value.";
return false;
}
return layer->SetSize(UnwrapVector2(command.value));
}
return false;
}
bool Session::ApplySetOpacityCmd(::fuchsia::ui::gfx::SetOpacityCmd command) {
if (auto node = resources_.FindResource<OpacityNode>(command.node_id)) {
node->SetOpacity(command.opacity);
return true;
}
return false;
}
bool Session::ApplySendSizeChangeHintCmd(
::fuchsia::ui::gfx::SendSizeChangeHintCmdHACK command) {
if (auto node = resources_.FindResource<Node>(command.node_id)) {
return node->SendSizeChangeHint(command.width_change_factor,
command.height_change_factor);
}
return false;
}
bool Session::ApplySetShapeCmd(::fuchsia::ui::gfx::SetShapeCmd command) {
if (auto node = resources_.FindResource<ShapeNode>(command.node_id)) {
if (auto shape = resources_.FindResource<Shape>(command.shape_id)) {
node->SetShape(std::move(shape));
return true;
}
}
return false;
}
bool Session::ApplySetMaterialCmd(::fuchsia::ui::gfx::SetMaterialCmd command) {
if (auto node = resources_.FindResource<ShapeNode>(command.node_id)) {
if (auto material =
resources_.FindResource<Material>(command.material_id)) {
node->SetMaterial(std::move(material));
return true;
}
}
return false;
}
bool Session::ApplySetClipCmd(::fuchsia::ui::gfx::SetClipCmd command) {
if (command.clip_id != 0) {
// TODO(SCN-167): Support non-zero clip_id.
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetClipCmd(): only "
"clip_to_self is implemented.";
return false;
}
if (auto node = resources_.FindResource<Node>(command.node_id)) {
return node->SetClipToSelf(command.clip_to_self);
}
return false;
}
bool Session::ApplySetHitTestBehaviorCmd(
::fuchsia::ui::gfx::SetHitTestBehaviorCmd command) {
if (auto node = resources_.FindResource<Node>(command.node_id)) {
return node->SetHitTestBehavior(command.hit_test_behavior);
}
return false;
}
bool Session::ApplySetViewPropertiesCmd(
::fuchsia::ui::gfx::SetViewPropertiesCmd command) {
if (auto view_holder =
resources_.FindResource<ViewHolder>(command.view_holder_id)) {
view_holder->SetViewProperties(std::move(command.properties));
return true;
}
return false;
}
bool Session::ApplySetCameraCmd(::fuchsia::ui::gfx::SetCameraCmd command) {
if (auto renderer = resources_.FindResource<Renderer>(command.renderer_id)) {
if (command.camera_id == 0) {
renderer->SetCamera(nullptr);
return true;
} else if (auto camera =
resources_.FindResource<Camera>(command.camera_id)) {
renderer->SetCamera(std::move(camera));
return true;
}
}
return false;
}
bool Session::ApplySetTextureCmd(::fuchsia::ui::gfx::SetTextureCmd command) {
if (auto material = resources_.FindResource<Material>(command.material_id)) {
if (command.texture_id == 0) {
material->SetTexture(nullptr);
return true;
} else if (auto image =
resources_.FindResource<ImageBase>(command.texture_id)) {
material->SetTexture(std::move(image));
return true;
}
}
return false;
}
bool Session::ApplySetColorCmd(::fuchsia::ui::gfx::SetColorCmd command) {
if (auto material = resources_.FindResource<Material>(command.material_id)) {
if (IsVariable(command.color)) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetColorCmd(): "
"unimplemented for variable color.";
return false;
}
auto& color = command.color.value;
float red = static_cast<float>(color.red) / 255.f;
float green = static_cast<float>(color.green) / 255.f;
float blue = static_cast<float>(color.blue) / 255.f;
float alpha = static_cast<float>(color.alpha) / 255.f;
material->SetColor(red, green, blue, alpha);
return true;
}
return false;
}
bool Session::ApplyBindMeshBuffersCmd(
::fuchsia::ui::gfx::BindMeshBuffersCmd command) {
auto mesh = resources_.FindResource<MeshShape>(command.mesh_id);
auto index_buffer = resources_.FindResource<Buffer>(command.index_buffer_id);
auto vertex_buffer =
resources_.FindResource<Buffer>(command.vertex_buffer_id);
if (mesh && index_buffer && vertex_buffer) {
return mesh->BindBuffers(std::move(index_buffer), command.index_format,
command.index_offset, command.index_count,
std::move(vertex_buffer), command.vertex_format,
command.vertex_offset, command.vertex_count,
Unwrap(command.bounding_box));
}
return false;
}
bool Session::ApplyAddLayerCmd(::fuchsia::ui::gfx::AddLayerCmd command) {
auto layer_stack =
resources_.FindResource<LayerStack>(command.layer_stack_id);
auto layer = resources_.FindResource<Layer>(command.layer_id);
if (layer_stack && layer) {
return layer_stack->AddLayer(std::move(layer));
}
return false;
}
bool Session::ApplyRemoveLayerCmd(::fuchsia::ui::gfx::RemoveLayerCmd command) {
auto layer_stack =
resources_.FindResource<LayerStack>(command.layer_stack_id);
auto layer = resources_.FindResource<Layer>(command.layer_id);
if (layer_stack && layer) {
return layer_stack->RemoveLayer(std::move(layer));
}
return false;
}
bool Session::ApplyRemoveAllLayersCmd(
::fuchsia::ui::gfx::RemoveAllLayersCmd command) {
auto layer_stack =
resources_.FindResource<LayerStack>(command.layer_stack_id);
if (layer_stack) {
return layer_stack->RemoveAllLayers();
}
return false;
}
bool Session::ApplySetLayerStackCmd(
::fuchsia::ui::gfx::SetLayerStackCmd command) {
auto compositor = resources_.FindResource<Compositor>(command.compositor_id);
auto layer_stack =
resources_.FindResource<LayerStack>(command.layer_stack_id);
if (compositor && layer_stack) {
return compositor->SetLayerStack(std::move(layer_stack));
}
return false;
}
bool Session::ApplySetRendererCmd(::fuchsia::ui::gfx::SetRendererCmd command) {
auto layer = resources_.FindResource<Layer>(command.layer_id);
auto renderer = resources_.FindResource<Renderer>(command.renderer_id);
if (layer && renderer) {
return layer->SetRenderer(std::move(renderer));
}
return false;
}
bool Session::ApplySetRendererParamCmd(
::fuchsia::ui::gfx::SetRendererParamCmd command) {
auto renderer = resources_.FindResource<Renderer>(command.renderer_id);
if (renderer) {
switch (command.param.Which()) {
case ::fuchsia::ui::gfx::RendererParam::Tag::kShadowTechnique:
return renderer->SetShadowTechnique(command.param.shadow_technique());
case ::fuchsia::ui::gfx::RendererParam::Tag::kRenderFrequency:
renderer->SetRenderContinuously(
command.param.render_frequency() ==
::fuchsia::ui::gfx::RenderFrequency::CONTINUOUSLY);
return true;
case ::fuchsia::ui::gfx::RendererParam::Tag::Invalid:
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetRendererParamCmd(): "
"invalid param.";
}
}
return false;
}
bool Session::ApplySetEventMaskCmd(
::fuchsia::ui::gfx::SetEventMaskCmd command) {
if (auto r = resources_.FindResource<Resource>(command.id)) {
return r->SetEventMask(command.event_mask);
}
return false;
}
bool Session::ApplySetCameraTransformCmd(
::fuchsia::ui::gfx::SetCameraTransformCmd command) {
// TODO(SCN-123): support variables.
if (IsVariable(command.eye_position) || IsVariable(command.eye_look_at) ||
IsVariable(command.eye_up)) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetCameraTransformCmd(): "
"unimplemented: variable properties.";
return false;
} else if (auto camera = resources_.FindResource<Camera>(command.camera_id)) {
camera->SetTransform(UnwrapVector3(command.eye_position),
UnwrapVector3(command.eye_look_at),
UnwrapVector3(command.eye_up));
return true;
}
return false;
}
bool Session::ApplySetCameraProjectionCmd(
::fuchsia::ui::gfx::SetCameraProjectionCmd command) {
// TODO(SCN-123): support variables.
if (IsVariable(command.fovy)) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetCameraProjectionCmd(): "
"unimplemented: variable properties.";
return false;
} else if (auto camera = resources_.FindResource<Camera>(command.camera_id)) {
camera->SetProjection(UnwrapFloat(command.fovy));
return true;
}
return false;
}
bool Session::ApplySetStereoCameraProjectionCmd(
::fuchsia::ui::gfx::SetStereoCameraProjectionCmd command) {
if (IsVariable(command.left_projection) ||
IsVariable(command.right_projection)) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetStereoCameraProjectionOp(): "
"unimplemented: variable properties.";
return false;
} else if (auto stereo_camera =
resources_.FindResource<StereoCamera>(command.camera_id)) {
stereo_camera->SetStereoProjection(Unwrap(command.left_projection.value),
Unwrap(command.right_projection.value));
return true;
}
return false;
}
bool Session::ApplySetCameraPoseBufferCmd(
::fuchsia::ui::gfx::SetCameraPoseBufferCmd command) {
if (command.base_time > dispatcher_clock_now()) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetCameraPoseBufferCmd(): "
"base time not in the past";
return false;
}
auto buffer = resources_.FindResource<Buffer>(command.buffer_id);
if (!buffer) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetCameraPoseBufferCmd(): "
"invalid buffer ID";
return false;
}
if (command.num_entries < 1) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetCameraPoseBufferCmd(): "
"must have at least one entry in the pose buffer";
return false;
}
if (buffer->size() < command.num_entries * sizeof(escher::hmd::Pose)) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetCameraPoseBufferCmd(): "
"buffer is not large enough";
return false;
}
auto camera = resources_.FindResource<Camera>(command.camera_id);
if (!camera) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetCameraPoseBufferCmd(): "
"invalid camera ID";
return false;
}
camera->SetPoseBuffer(buffer, command.num_entries, command.base_time,
command.time_interval);
return true;
}
bool Session::ApplySetLightColorCmd(
::fuchsia::ui::gfx::SetLightColorCmd command) {
// TODO(SCN-123): support variables.
if (command.color.variable_id) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetLightColorCmd(): "
"unimplemented: variable color.";
return false;
} else if (auto light = resources_.FindResource<Light>(command.light_id)) {
return light->SetColor(Unwrap(command.color.value));
}
return false;
}
bool Session::ApplySetLightDirectionCmd(
::fuchsia::ui::gfx::SetLightDirectionCmd command) {
// TODO(SCN-123): support variables.
if (command.direction.variable_id) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplySetLightDirectionCmd(): "
"unimplemented: variable direction.";
return false;
} else if (auto light =
resources_.FindResource<DirectionalLight>(command.light_id)) {
return light->SetDirection(Unwrap(command.direction.value));
}
return false;
}
bool Session::ApplyAddLightCmd(::fuchsia::ui::gfx::AddLightCmd command) {
if (auto scene = resources_.FindResource<Scene>(command.scene_id)) {
if (auto light = resources_.FindResource<Light>(command.light_id)) {
return scene->AddLight(std::move(light));
}
}
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplyAddLightCmd(): unimplemented.";
return false;
}
bool Session::ApplyDetachLightCmd(::fuchsia::ui::gfx::DetachLightCmd command) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplyDetachLightCmd(): unimplemented.";
return false;
}
bool Session::ApplyDetachLightsCmd(
::fuchsia::ui::gfx::DetachLightsCmd command) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplyDetachLightsCmd(): unimplemented.";
return false;
}
bool Session::ApplySetLabelCmd(::fuchsia::ui::gfx::SetLabelCmd command) {
if (auto r = resources_.FindResource<Resource>(command.id)) {
return r->SetLabel(command.label.get());
}
return false;
}
bool Session::ApplySetDisableClippingCmd(
::fuchsia::ui::gfx::SetDisableClippingCmd command) {
if (auto r = resources_.FindResource<Renderer>(command.renderer_id)) {
r->DisableClipping(command.disable_clipping);
return true;
}
return false;
}
bool Session::ApplyCreateMemory(ResourceId id,
::fuchsia::ui::gfx::MemoryArgs args) {
auto memory = CreateMemory(id, std::move(args));
return memory ? resources_.AddResource(id, std::move(memory)) : false;
}
bool Session::ApplyCreateImage(ResourceId id,
::fuchsia::ui::gfx::ImageArgs args) {
if (auto memory = resources_.FindResource<Memory>(args.memory_id)) {
if (auto image = CreateImage(id, std::move(memory), args)) {
return resources_.AddResource(id, std::move(image));
}
}
return false;
}
bool Session::ApplyCreateImagePipe(ResourceId id,
::fuchsia::ui::gfx::ImagePipeArgs args) {
auto image_pipe = fxl::MakeRefCounted<ImagePipe>(
this, id, std::move(args.image_pipe_request));
return resources_.AddResource(id, image_pipe);
}
bool Session::ApplyCreateBuffer(ResourceId id,
::fuchsia::ui::gfx::BufferArgs args) {
if (auto memory = resources_.FindResource<Memory>(args.memory_id)) {
if (auto buffer = CreateBuffer(id, std::move(memory), args.memory_offset,
args.num_bytes)) {
return resources_.AddResource(id, std::move(buffer));
}
}
return false;
}
bool Session::ApplyCreateScene(ResourceId id,
::fuchsia::ui::gfx::SceneArgs args) {
auto scene = CreateScene(id, std::move(args));
return scene ? resources_.AddResource(id, std::move(scene)) : false;
}
bool Session::ApplyCreateCamera(ResourceId id,
::fuchsia::ui::gfx::CameraArgs args) {
auto camera = CreateCamera(id, std::move(args));
return camera ? resources_.AddResource(id, std::move(camera)) : false;
}
bool Session::ApplyCreateStereoCamera(
ResourceId id, ::fuchsia::ui::gfx::StereoCameraArgs args) {
auto camera = CreateStereoCamera(id, args);
return camera ? resources_.AddResource(id, std::move(camera)) : false;
}
bool Session::ApplyCreateRenderer(ResourceId id,
::fuchsia::ui::gfx::RendererArgs args) {
auto renderer = CreateRenderer(id, std::move(args));
return renderer ? resources_.AddResource(id, std::move(renderer)) : false;
}
bool Session::ApplyCreateAmbientLight(
ResourceId id, ::fuchsia::ui::gfx::AmbientLightArgs args) {
auto light = CreateAmbientLight(id);
return light ? resources_.AddResource(id, std::move(light)) : false;
}
bool Session::ApplyCreateDirectionalLight(
ResourceId id, ::fuchsia::ui::gfx::DirectionalLightArgs args) {
auto light = CreateDirectionalLight(id);
return light ? resources_.AddResource(id, std::move(light)) : false;
}
bool Session::ApplyCreateRectangle(ResourceId id,
::fuchsia::ui::gfx::RectangleArgs args) {
if (!AssertValueIsOfType(args.width, kFloatValueTypes) ||
!AssertValueIsOfType(args.height, kFloatValueTypes)) {
return false;
}
// TODO(SCN-123): support variables.
if (IsVariable(args.width) || IsVariable(args.height)) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplyCreateRectangle(): "
"unimplemented: variable width/height.";
return false;
}
auto rectangle =
CreateRectangle(id, args.width.vector1(), args.height.vector1());
return rectangle ? resources_.AddResource(id, std::move(rectangle)) : false;
}
bool Session::ApplyCreateRoundedRectangle(
ResourceId id, ::fuchsia::ui::gfx::RoundedRectangleArgs args) {
if (!AssertValueIsOfType(args.width, kFloatValueTypes) ||
!AssertValueIsOfType(args.height, kFloatValueTypes) ||
!AssertValueIsOfType(args.top_left_radius, kFloatValueTypes) ||
!AssertValueIsOfType(args.top_right_radius, kFloatValueTypes) ||
!AssertValueIsOfType(args.bottom_left_radius, kFloatValueTypes) ||
!AssertValueIsOfType(args.bottom_right_radius, kFloatValueTypes)) {
return false;
}
// TODO(SCN-123): support variables.
if (IsVariable(args.width) || IsVariable(args.height) ||
IsVariable(args.top_left_radius) || IsVariable(args.top_right_radius) ||
IsVariable(args.bottom_left_radius) ||
IsVariable(args.bottom_right_radius)) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplyCreateRoundedRectangle(): "
"unimplemented: variable width/height/radii.";
return false;
}
const float width = args.width.vector1();
const float height = args.height.vector1();
const float top_left_radius = args.top_left_radius.vector1();
const float top_right_radius = args.top_right_radius.vector1();
const float bottom_right_radius = args.bottom_right_radius.vector1();
const float bottom_left_radius = args.bottom_left_radius.vector1();
auto rectangle = CreateRoundedRectangle(id, width, height, top_left_radius,
top_right_radius, bottom_right_radius,
bottom_left_radius);
return rectangle ? resources_.AddResource(id, std::move(rectangle)) : false;
}
bool Session::ApplyCreateCircle(ResourceId id,
::fuchsia::ui::gfx::CircleArgs args) {
if (!AssertValueIsOfType(args.radius, kFloatValueTypes)) {
return false;
}
// TODO(SCN-123): support variables.
if (IsVariable(args.radius)) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplyCreateCircle(): "
"unimplemented: variable radius.";
return false;
}
auto circle = CreateCircle(id, args.radius.vector1());
return circle ? resources_.AddResource(id, std::move(circle)) : false;
}
bool Session::ApplyCreateMesh(ResourceId id,
::fuchsia::ui::gfx::MeshArgs args) {
auto mesh = CreateMesh(id);
return mesh ? resources_.AddResource(id, std::move(mesh)) : false;
}
bool Session::ApplyCreateMaterial(ResourceId id,
::fuchsia::ui::gfx::MaterialArgs args) {
auto material = CreateMaterial(id);
return material ? resources_.AddResource(id, std::move(material)) : false;
}
bool Session::ApplyCreateView(ResourceId id,
::fuchsia::ui::gfx::ViewArgs args) {
// Sanity check. We also rely on FIDL to enforce this for us, although it
// does not at the moment.
FXL_DCHECK(args.token)
<< "scenic_impl::gfx::Session::ApplyCreateView(): no token provided.";
if (auto view = CreateView(id, std::move(args))) {
view->As<View>()->Connect(); // Initiate the link.
resources_.AddResource(id, std::move(view));
return true;
}
return false;
}
bool Session::ApplyCreateViewHolder(ResourceId id,
::fuchsia::ui::gfx::ViewHolderArgs args) {
// Sanity check. We also rely on FIDL to enforce this for us, although it
// does not at the moment
FXL_DCHECK(args.token) << "scenic_impl::gfx::Session::ApplyCreateViewHolder()"
": no token provided.";
if (auto view_holder = CreateViewHolder(id, std::move(args))) {
view_holder->As<ViewHolder>()->Connect(); // Initiate the link.
resources_.AddResource(id, std::move(view_holder));
return true;
}
return false;
}
bool Session::ApplyCreateClipNode(ResourceId id,
::fuchsia::ui::gfx::ClipNodeArgs args) {
auto node = CreateClipNode(id, std::move(args));
return node ? resources_.AddResource(id, std::move(node)) : false;
}
bool Session::ApplyCreateEntityNode(ResourceId id,
::fuchsia::ui::gfx::EntityNodeArgs args) {
auto node = CreateEntityNode(id, std::move(args));
return node ? resources_.AddResource(id, std::move(node)) : false;
}
bool Session::ApplyCreateOpacityNode(ResourceId id,
::fuchsia::ui::gfx::OpacityNodeArgs args) {
auto node = CreateOpacityNode(id, args);
return node ? resources_.AddResource(id, std::move(node)) : false;
}
bool Session::ApplyCreateShapeNode(ResourceId id,
::fuchsia::ui::gfx::ShapeNodeArgs args) {
auto node = CreateShapeNode(id, std::move(args));
return node ? resources_.AddResource(id, std::move(node)) : false;
}
bool Session::ApplyCreateCompositor(ResourceId id,
::fuchsia::ui::gfx::CompositorArgs args) {
auto compositor = CreateCompositor(id, std::move(args));
return compositor ? resources_.AddResource(id, std::move(compositor)) : false;
}
bool Session::ApplyCreateDisplayCompositor(
ResourceId id, ::fuchsia::ui::gfx::DisplayCompositorArgs args) {
auto compositor = CreateDisplayCompositor(id, std::move(args));
return compositor ? resources_.AddResource(id, std::move(compositor)) : false;
}
bool Session::ApplyCreateImagePipeCompositor(
ResourceId id, ::fuchsia::ui::gfx::ImagePipeCompositorArgs args) {
auto compositor = CreateImagePipeCompositor(id, std::move(args));
return compositor ? resources_.AddResource(id, std::move(compositor)) : false;
}
bool Session::ApplyCreateLayerStack(ResourceId id,
::fuchsia::ui::gfx::LayerStackArgs args) {
auto layer_stack = CreateLayerStack(id, std::move(args));
return layer_stack ? resources_.AddResource(id, std::move(layer_stack))
: false;
}
bool Session::ApplyCreateLayer(ResourceId id,
::fuchsia::ui::gfx::LayerArgs args) {
auto layer = CreateLayer(id, std::move(args));
return layer ? resources_.AddResource(id, std::move(layer)) : false;
}
bool Session::ApplyCreateVariable(ResourceId id,
::fuchsia::ui::gfx::VariableArgs args) {
auto variable = CreateVariable(id, std::move(args));
return variable ? resources_.AddResource(id, std::move(variable)) : false;
}
ResourcePtr Session::CreateMemory(ResourceId id,
::fuchsia::ui::gfx::MemoryArgs args) {
return Memory::New(this, id, std::move(args), error_reporter_);
}
ResourcePtr Session::CreateImage(ResourceId id, MemoryPtr memory,
::fuchsia::ui::gfx::ImageArgs args) {
return Image::New(this, id, memory, args.info, args.memory_offset,
error_reporter_);
}
ResourcePtr Session::CreateBuffer(ResourceId id, MemoryPtr memory,
uint32_t memory_offset, uint32_t num_bytes) {
if (memory_offset + num_bytes > memory->size()) {
error_reporter_->ERROR() << "scenic_impl::gfx::Session::CreateBuffer(): "
"buffer does not fit within memory (buffer "
"offset: "
<< memory_offset << ", buffer size: " << num_bytes
<< ", memory size: " << memory->size() << ")";
return ResourcePtr();
}
// Make a pointer to a subregion of the memory, if necessary.
escher::GpuMemPtr gpu_mem =
(memory_offset > 0 || num_bytes < memory->size())
? memory->GetGpuMem()->Suballocate(num_bytes, memory_offset)
: memory->GetGpuMem();
return fxl::MakeRefCounted<Buffer>(this, id, std::move(gpu_mem),
std::move(memory));
}
ResourcePtr Session::CreateScene(ResourceId id,
::fuchsia::ui::gfx::SceneArgs args) {
return fxl::MakeRefCounted<Scene>(this, id);
}
ResourcePtr Session::CreateCamera(ResourceId id,
::fuchsia::ui::gfx::CameraArgs args) {
if (auto scene = resources_.FindResource<Scene>(args.scene_id)) {
return fxl::MakeRefCounted<Camera>(this, id, std::move(scene));
}
return ResourcePtr();
}
ResourcePtr Session::CreateStereoCamera(
ResourceId id, const ::fuchsia::ui::gfx::StereoCameraArgs args) {
if (auto scene = resources_.FindResource<Scene>(args.scene_id)) {
return fxl::MakeRefCounted<StereoCamera>(this, id, std::move(scene));
}
return ResourcePtr();
}
ResourcePtr Session::CreateRenderer(ResourceId id,
::fuchsia::ui::gfx::RendererArgs args) {
return fxl::MakeRefCounted<Renderer>(this, id);
}
ResourcePtr Session::CreateAmbientLight(ResourceId id) {
return fxl::MakeRefCounted<AmbientLight>(this, id);
}
ResourcePtr Session::CreateDirectionalLight(ResourceId id) {
return fxl::MakeRefCounted<DirectionalLight>(this, id);
}
ResourcePtr Session::CreateView(ResourceId id,
::fuchsia::ui::gfx::ViewArgs args) {
ViewLinker* view_linker = engine()->view_linker();
ViewLinker::ImportLink link =
view_linker->CreateImport(std::move(args.token), error_reporter());
// Create a View if the Link was successfully registered.
if (link.valid()) {
return fxl::MakeRefCounted<View>(this, id, std::move(link));
}
return nullptr;
}
ResourcePtr Session::CreateViewHolder(ResourceId id,
::fuchsia::ui::gfx::ViewHolderArgs args) {
ViewLinker* view_linker = engine()->view_linker();
ViewLinker::ExportLink link =
view_linker->CreateExport(std::move(args.token), error_reporter());
// Create a ViewHolder if the Link was successfully registered.
if (link.valid()) {
return fxl::MakeRefCounted<ViewHolder>(this, id, std::move(link));
}
return nullptr;
}
ResourcePtr Session::CreateClipNode(ResourceId id,
::fuchsia::ui::gfx::ClipNodeArgs args) {
error_reporter_->ERROR() << "scenic_impl::gfx::Session::CreateClipNode(): "
"unimplemented.";
return ResourcePtr();
}
ResourcePtr Session::CreateEntityNode(ResourceId id,
::fuchsia::ui::gfx::EntityNodeArgs args) {
return fxl::MakeRefCounted<EntityNode>(this, id);
}
ResourcePtr Session::CreateOpacityNode(
ResourceId id, ::fuchsia::ui::gfx::OpacityNodeArgs args) {
return fxl::MakeRefCounted<OpacityNode>(this, id);
}
ResourcePtr Session::CreateShapeNode(ResourceId id,
::fuchsia::ui::gfx::ShapeNodeArgs args) {
return fxl::MakeRefCounted<ShapeNode>(this, id);
}
ResourcePtr Session::CreateCompositor(ResourceId id,
::fuchsia::ui::gfx::CompositorArgs args) {
return Compositor::New(this, id);
}
ResourcePtr Session::CreateDisplayCompositor(
ResourceId id, ::fuchsia::ui::gfx::DisplayCompositorArgs args) {
Display* display = engine()->display_manager()->default_display();
if (!display) {
error_reporter_->ERROR() << "There is no default display available.";
return nullptr;
}
if (display->is_claimed()) {
error_reporter_->ERROR() << "The default display has already been claimed "
"by another compositor.";
return nullptr;
}
return fxl::MakeRefCounted<DisplayCompositor>(
this, id, display, engine()->CreateDisplaySwapchain(display));
}
ResourcePtr Session::CreateImagePipeCompositor(
ResourceId id, ::fuchsia::ui::gfx::ImagePipeCompositorArgs args) {
// TODO(SCN-179)
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::ApplyCreateImagePipeCompositor() "
"is unimplemented (SCN-179)";
return ResourcePtr();
}
ResourcePtr Session::CreateLayerStack(ResourceId id,
::fuchsia::ui::gfx::LayerStackArgs args) {
return fxl::MakeRefCounted<LayerStack>(this, id);
}
ResourcePtr Session::CreateVariable(ResourceId id,
::fuchsia::ui::gfx::VariableArgs args) {
fxl::RefPtr<Variable> variable;
switch (args.type) {
case ::fuchsia::ui::gfx::ValueType::kVector1:
variable = fxl::MakeRefCounted<FloatVariable>(this, id);
break;
case ::fuchsia::ui::gfx::ValueType::kVector2:
variable = fxl::MakeRefCounted<Vector2Variable>(this, id);
break;
case ::fuchsia::ui::gfx::ValueType::kVector3:
variable = fxl::MakeRefCounted<Vector3Variable>(this, id);
break;
case ::fuchsia::ui::gfx::ValueType::kVector4:
variable = fxl::MakeRefCounted<Vector4Variable>(this, id);
break;
case ::fuchsia::ui::gfx::ValueType::kMatrix4:
variable = fxl::MakeRefCounted<Matrix4x4Variable>(this, id);
break;
case ::fuchsia::ui::gfx::ValueType::kColorRgb:
// not yet supported
variable = nullptr;
break;
case ::fuchsia::ui::gfx::ValueType::kColorRgba:
// not yet supported
variable = nullptr;
break;
case ::fuchsia::ui::gfx::ValueType::kQuaternion:
variable = fxl::MakeRefCounted<QuaternionVariable>(this, id);
break;
case ::fuchsia::ui::gfx::ValueType::kFactoredTransform:
/* variable = fxl::MakeRefCounted<TransformVariable>(this, id); */
variable = nullptr;
break;
case ::fuchsia::ui::gfx::ValueType::kNone:
break;
}
if (variable && variable->SetValue(args.initial_value)) {
return variable;
}
return nullptr;
}
ResourcePtr Session::CreateLayer(ResourceId id,
::fuchsia::ui::gfx::LayerArgs args) {
return fxl::MakeRefCounted<Layer>(this, id);
}
ResourcePtr Session::CreateCircle(ResourceId id, float initial_radius) {
return fxl::MakeRefCounted<CircleShape>(this, id, initial_radius);
}
ResourcePtr Session::CreateRectangle(ResourceId id, float width, float height) {
return fxl::MakeRefCounted<RectangleShape>(this, id, width, height);
}
ResourcePtr Session::CreateRoundedRectangle(ResourceId id, float width,
float height, float top_left_radius,
float top_right_radius,
float bottom_right_radius,
float bottom_left_radius) {
auto factory = engine()->escher_rounded_rect_factory();
if (!factory) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session::CreateRoundedRectangle(): "
"no RoundedRectFactory available.";
return ResourcePtr();
}
// If radii sum exceeds width or height, scale them down.
float top_radii_sum = top_left_radius + top_right_radius;
float top_scale = std::min(width / top_radii_sum, 1.f);
float bottom_radii_sum = bottom_left_radius + bottom_right_radius;
float bottom_scale = std::min(width / bottom_radii_sum, 1.f);
float left_radii_sum = top_left_radius + bottom_left_radius;
float left_scale = std::min(height / left_radii_sum, 1.f);
float right_radii_sum = top_right_radius + bottom_right_radius;
float right_scale = std::min(height / right_radii_sum, 1.f);
top_left_radius *= std::min(top_scale, left_scale);
top_right_radius *= std::min(top_scale, right_scale);
bottom_left_radius *= std::min(bottom_scale, left_scale);
bottom_right_radius *= std::min(bottom_scale, right_scale);
escher::RoundedRectSpec rect_spec(width, height, top_left_radius,
top_right_radius, bottom_right_radius,
bottom_left_radius);
escher::MeshSpec mesh_spec{escher::MeshAttribute::kPosition2D |
escher::MeshAttribute::kUV};
return fxl::MakeRefCounted<RoundedRectangleShape>(
this, id, rect_spec,
factory->NewRoundedRect(
rect_spec, mesh_spec,
engine()->GetCommandContext()->batch_gpu_uploader()));
}
ResourcePtr Session::CreateMesh(ResourceId id) {
return fxl::MakeRefCounted<MeshShape>(this, id);
}
ResourcePtr Session::CreateMaterial(ResourceId id) {
return fxl::MakeRefCounted<Material>(this, id);
}
void Session::TearDown() {
if (!is_valid_) {
// TearDown already called.
return;
}
is_valid_ = false;
resources_.Clear();
scheduled_image_pipe_updates_ = {};
// We assume the channel for the associated ::fuchsia::ui::gfx::Session is
// closed because SessionHandler closes it before calling this method. The
// channel *must* be closed before we clear |scheduled_updates_|, since it
// contains pending callbacks to ::fuchsia::ui::gfx::Session::Present(); if it
// were not closed, we would have to invoke those callbacks before destroying
// them.
scheduled_updates_ = {};
fences_to_release_on_next_update_.reset();
if (resource_count_ != 0) {
auto exported_count =
engine()->resource_linker()->NumExportsForSession(this);
FXL_CHECK(resource_count_ == 0)
<< "Session::TearDown(): Not all resources have been collected. "
"Exported resources: "
<< exported_count
<< ", total outstanding resources: " << resource_count_;
}
error_reporter_ = nullptr;
}
ErrorReporter* Session::error_reporter() const {
return error_reporter_ ? error_reporter_ : ErrorReporter::Default();
}
EventReporter* Session::event_reporter() const { return event_reporter_; }
bool Session::AssertValueIsOfType(const ::fuchsia::ui::gfx::Value& value,
const ::fuchsia::ui::gfx::Value::Tag* tags,
size_t tag_count) {
using ::operator<<; // From print_commands.h
FXL_DCHECK(tag_count > 0);
for (size_t i = 0; i < tag_count; ++i) {
if (value.Which() == tags[i]) {
return true;
}
}
std::ostringstream str;
if (tag_count == 1) {
str << ", which is not the expected type: " << tags[0] << ".";
} else {
str << ", which is not one of the expected types (" << tags[0];
for (size_t i = 1; i < tag_count; ++i) {
str << ", " << tags[i];
}
str << ").";
}
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session: received value of type: " << value.Which()
<< str.str();
return false;
}
bool Session::ScheduleUpdate(
uint64_t requested_presentation_time,
std::vector<::fuchsia::ui::gfx::Command> commands,
::fidl::VectorPtr<zx::event> acquire_fences,
::fidl::VectorPtr<zx::event> release_events,
fuchsia::ui::scenic::Session::PresentCallback callback) {
if (is_valid()) {
uint64_t last_scheduled_presentation_time =
last_applied_update_presentation_time_;
if (!scheduled_updates_.empty()) {
last_scheduled_presentation_time =
std::max(last_scheduled_presentation_time,
scheduled_updates_.back().presentation_time);
}
if (requested_presentation_time < last_scheduled_presentation_time) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session: Present called with out-of-order "
"presentation time. "
<< "requested presentation time=" << requested_presentation_time
<< ", last scheduled presentation time="
<< last_scheduled_presentation_time << ".";
return false;
}
// If we're not running headless, warn if the requested presesentation time
// is not reasonable.
if (engine()->frame_scheduler() &&
engine()->display_manager()->default_display()) {
std::pair<zx_time_t, zx_time_t> target_times =
engine()->frame_scheduler()->ComputeTargetPresentationAndWakeupTimes(
requested_presentation_time);
uint64_t target_presentation_time = target_times.first;
zx_time_t vsync_interval =
engine()->display_manager()->default_display()->GetVsyncInterval();
// TODO(SCN-723): Re-enable warning when requested_presentation_time == 0
// after Flutter engine is fixed.
if (requested_presentation_time != 0 &&
target_presentation_time - vsync_interval >
requested_presentation_time) {
// Present called with too early of a presentation time.
TRACE_INSTANT("gfx", "Session requested too early presentation time",
TRACE_SCOPE_PROCESS, "session_id", id(),
"requested presentation time",
requested_presentation_time, "target presentation time",
target_presentation_time);
}
}
auto acquire_fence_set =
std::make_unique<escher::FenceSetListener>(std::move(acquire_fences));
// TODO: Consider calling ScheduleUpdateForSession immediately if
// acquire_fence_set is already ready (which is the case if there are
// zero acquire fences).
acquire_fence_set->WaitReadyAsync([weak = weak_factory_.GetWeakPtr(),
requested_presentation_time] {
if (weak)
weak->engine_->session_manager()->ScheduleUpdateForSession(
weak->engine_, requested_presentation_time, SessionPtr(weak.get()));
});
scheduled_updates_.push(
Update{requested_presentation_time, std::move(commands),
std::move(acquire_fence_set), std::move(release_events),
std::move(callback)});
}
return true;
}
void Session::ScheduleImagePipeUpdate(uint64_t presentation_time,
ImagePipePtr image_pipe) {
if (is_valid()) {
scheduled_image_pipe_updates_.push(
{presentation_time, std::move(image_pipe)});
engine_->session_manager()->ScheduleUpdateForSession(
engine_, presentation_time, SessionPtr(this));
}
}
bool Session::ApplyScheduledUpdates(uint64_t presentation_time,
uint64_t presentation_interval) {
TRACE_DURATION("gfx", "Session::ApplyScheduledUpdates", "session_id", id_,
"session_debug_name", debug_name_, "time", presentation_time,
"interval", presentation_interval);
if (presentation_time < last_presentation_time_) {
error_reporter_->ERROR()
<< "scenic_impl::gfx::Session: ApplyScheduledUpdates called with "
"presentation_time="
<< presentation_time << ", which is less than last_presentation_time_="
<< last_presentation_time_ << ".";
return false;
}
bool needs_render = false;
while (!scheduled_updates_.empty() &&
scheduled_updates_.front().presentation_time <= presentation_time) {
if (!scheduled_updates_.front().acquire_fences->ready()) {
TRACE_INSTANT("gfx", "Session missed frame", TRACE_SCOPE_PROCESS,
"session_id", id(), "session_debug_name", debug_name_,
"target presentation time (usecs)",
presentation_time / 1000,
"session target presentation time (usecs)",
scheduled_updates_.front().presentation_time / 1000);
break;
}
if (ApplyUpdate(std::move(scheduled_updates_.front().commands))) {
needs_render = true;
auto info = fuchsia::images::PresentationInfo();
info.presentation_time = presentation_time;
info.presentation_interval = presentation_interval;
scheduled_updates_.front().present_callback(std::move(info));
FXL_DCHECK(last_applied_update_presentation_time_ <=
scheduled_updates_.front().presentation_time);
last_applied_update_presentation_time_ =
scheduled_updates_.front().presentation_time;
for (size_t i = 0; i < fences_to_release_on_next_update_->size(); ++i) {
engine()->release_fence_signaller()->AddCPUReleaseFence(
std::move(fences_to_release_on_next_update_->at(i)));
}
fences_to_release_on_next_update_ =
std::move(scheduled_updates_.front().release_fences);
scheduled_updates_.pop();
// TODO: gather statistics about how close the actual
// presentation_time was to the requested time.
} else {
// An error was encountered while applying the update.
FXL_LOG(WARNING) << "scenic_impl::gfx::Session::ApplyScheduledUpdates(): "
"An error was encountered while applying the update. "
"Initiating teardown.";
scheduled_updates_ = {};
BeginTearDown();
// Tearing down a session will very probably result in changes to
// the global scene-graph.
return true;
}
}
// TODO: Unify with other session updates.
while (!scheduled_image_pipe_updates_.empty() &&
scheduled_image_pipe_updates_.top().presentation_time <=
presentation_time) {
// The bool returned from Update() is 0 or 1, and needs_render is 0 or 1, so
// bitwise |= is used which doesn't short-circuit.
needs_render |= scheduled_image_pipe_updates_.top().image_pipe->Update(
presentation_time, presentation_interval);
scheduled_image_pipe_updates_.pop();
}
return needs_render;
}
void Session::EnqueueEvent(::fuchsia::ui::gfx::Event event) {
if (!is_valid()) {
return;
}
event_reporter_->EnqueueEvent(std::move(event));
}
void Session::EnqueueEvent(::fuchsia::ui::input::InputEvent event) {
if (!is_valid()) {
return;
}
event_reporter_->EnqueueEvent(std::move(event));
}
bool Session::ApplyUpdate(std::vector<::fuchsia::ui::gfx::Command> commands) {
TRACE_DURATION("gfx", "Session::ApplyUpdate");
if (is_valid()) {
for (auto& command : commands) {
if (!ApplyCommand(std::move(command))) {
error_reporter_->ERROR() << "scenic_impl::gfx::Session::ApplyCommand() "
"failed to apply Command: "
<< command;
return false;
}
}
}
return true;
// TODO: acquire_fences and release_fences should be added to a list that is
// consumed by the FrameScheduler.
}
void Session::HitTest(uint32_t node_id, ::fuchsia::ui::gfx::vec3 ray_origin,
::fuchsia::ui::gfx::vec3 ray_direction,
fuchsia::ui::scenic::Session::HitTestCallback callback) {
if (auto node = resources_.FindResource<Node>(node_id)) {
SessionHitTester hit_tester(node->session());
std::vector<Hit> hits = hit_tester.HitTest(
node.get(), escher::ray4{escher::vec4(Unwrap(ray_origin), 1.f),
escher::vec4(Unwrap(ray_direction), 0.f)});
callback(WrapHits(hits));
} else {
// TODO(SCN-162): Currently the test fails if the node isn't presented yet.
// Perhaps we should given clients more control over which state of
// the scene graph will be consulted for hit testing purposes.
error_reporter_->WARN()
<< "Cannot perform hit test because node " << node_id
<< " does not exist in the currently presented content.";
callback(nullptr);
}
}
void Session::HitTestDeviceRay(
::fuchsia::ui::gfx::vec3 ray_origin, ::fuchsia::ui::gfx::vec3 ray_direction,
fuchsia::ui::scenic::Session::HitTestCallback callback) {
escher::ray4 ray =
escher::ray4{{Unwrap(ray_origin), 1.f}, {Unwrap(ray_direction), 0.f}};
// The layer stack expects the input to the hit test to be in unscaled device
// coordinates.
SessionHitTester hit_tester(this);
std::vector<Hit> layer_stack_hits =
engine_->GetFirstCompositor()->layer_stack()->HitTest(ray, &hit_tester);
callback(WrapHits(layer_stack_hits));
}
void Session::BeginTearDown() {
engine()->session_manager()->TearDownSession(id());
}
} // namespace gfx
} // namespace scenic_impl