blob: 74fe05b42d72de449ec066d8e38d9d77a917a3c8 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/media/playback/mediaplayer/test/fakes/fake_session.h"
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <iomanip>
#include <iostream>
#include "lib/fidl/cpp/optional.h"
#include "src/lib/fxl/logging.h"
#include "src/media/playback/mediaplayer/graph/formatting.h"
namespace media_player {
namespace test {
namespace {
constexpr uint64_t kPresentationRatePerSecond = 60;
constexpr zx::duration kPresentationInterval = zx::duration(ZX_SEC(1) / kPresentationRatePerSecond);
constexpr uint32_t kRootNodeId = 1;
} // namespace
FakeSession::FakeSession()
: dispatcher_(async_get_default_dispatcher()), binding_(this), weak_factory_(this) {
fuchsia::ui::gfx::ResourceArgs root_resource;
fuchsia::ui::gfx::ViewArgs view_args;
root_resource.set_view(std::move(view_args));
resources_by_id_.emplace(kRootNodeId, std::move(root_resource));
}
FakeSession::~FakeSession() {}
void FakeSession::Bind(fidl::InterfaceRequest<fuchsia::ui::scenic::Session> request,
fuchsia::ui::scenic::SessionListenerPtr listener) {
binding_.Bind(std::move(request));
listener_ = std::move(listener);
PresentScene();
}
void FakeSession::DumpExpectations(uint32_t display_height) {
if (image_pipe_) {
image_pipe_->DumpExpectations(display_height);
} else {
dump_expectations_ = true;
expected_display_height_ = display_height;
}
}
void FakeSession::SetExpectations(uint32_t black_image_id,
const fuchsia::images::ImageInfo& black_image_info,
const fuchsia::images::ImageInfo& info, uint32_t display_height,
const std::vector<PacketInfo>&& expected_packets_info) {
if (image_pipe_) {
image_pipe_->SetExpectations(black_image_id, black_image_info, info, display_height,
std::move(expected_packets_info));
} else {
expected_black_image_id_ = black_image_id;
expected_black_image_info_ = fidl::MakeOptional(black_image_info);
expected_image_info_ = fidl::MakeOptional(info);
expected_packets_info_ = std::move(expected_packets_info);
expected_display_height_ = display_height;
}
}
void FakeSession::Enqueue(std::vector<fuchsia::ui::scenic::Command> cmds) {
for (auto& command : cmds) {
switch (command.Which()) {
case fuchsia::ui::scenic::Command::Tag::kGfx:
switch (command.gfx().Which()) {
case fuchsia::ui::gfx::Command::Tag::kSetEventMask:
HandleSetEventMask(command.gfx().set_event_mask().id,
command.gfx().set_event_mask().event_mask);
break;
case fuchsia::ui::gfx::Command::Tag::kCreateResource:
HandleCreateResource(command.gfx().create_resource().id,
std::move(command.gfx().create_resource().resource));
break;
case fuchsia::ui::gfx::Command::Tag::kReleaseResource:
HandleReleaseResource(command.gfx().release_resource().id);
break;
case fuchsia::ui::gfx::Command::Tag::kAddChild:
HandleAddChild(command.gfx().add_child().node_id, command.gfx().add_child().child_id);
break;
case fuchsia::ui::gfx::Command::Tag::kAddPart:
break;
case fuchsia::ui::gfx::Command::Tag::kSetMaterial:
HandleSetMaterial(command.gfx().set_material().node_id,
command.gfx().set_material().material_id);
break;
case fuchsia::ui::gfx::Command::Tag::kSetTexture:
HandleSetTexture(command.gfx().set_texture().material_id,
command.gfx().set_texture().texture_id);
break;
case fuchsia::ui::gfx::Command::Tag::kSetShape:
HandleSetShape(command.gfx().set_shape().node_id, command.gfx().set_shape().shape_id);
break;
case fuchsia::ui::gfx::Command::Tag::kSetTranslation:
HandleSetTranslation(command.gfx().set_translation().id,
command.gfx().set_translation().value);
break;
case fuchsia::ui::gfx::Command::Tag::kSetScale:
HandleSetScale(command.gfx().set_scale().id, command.gfx().set_scale().value);
break;
case fuchsia::ui::gfx::Command::Tag::kSetClipPlanes:
HandleSetClipPlanes(command.gfx().set_clip_planes().node_id,
std::move(command.gfx().set_clip_planes().clip_planes));
break;
default:
break;
}
break;
case fuchsia::ui::scenic::Command::Tag::kViews:
FXL_LOG(INFO) << "Enqueue: views (not implemented), tag "
<< static_cast<fidl_union_tag_t>(command.views().Which());
break;
default:
FXL_LOG(INFO) << "Enqueue: (not implemented), tag "
<< static_cast<fidl_union_tag_t>(command.Which());
break;
}
}
}
void FakeSession::Present(uint64_t presentation_time, std::vector<zx::event> acquire_fences,
std::vector<zx::event> release_fences, PresentCallback callback) {
// The video renderer doesn't use these fences, so we don't support them in
// the fake.
FXL_CHECK(acquire_fences.empty()) << "Present: acquire_fences not supported.";
FXL_CHECK(release_fences.empty()) << "Present: release_fences not supported.";
async::PostTask(dispatcher_, [this, callback = std::move(callback), weak_this = GetWeakThis()]() {
if (!weak_this) {
return;
}
fuchsia::images::PresentationInfo presentation_info;
presentation_info.presentation_time = next_presentation_time_.get();
presentation_info.presentation_interval = kPresentationInterval.get();
callback(presentation_info);
});
}
FakeSession::Resource* FakeSession::FindResource(uint32_t id) {
auto iter = resources_by_id_.find(id);
return (iter == resources_by_id_.end()) ? nullptr : &iter->second;
}
void FakeSession::HandleSetEventMask(uint32_t resource_id, uint32_t event_mask) {
if (event_mask & fuchsia::ui::gfx::kMetricsEventMask) {
fuchsia::ui::gfx::Event gfx_event;
gfx_event.set_metrics({.node_id = resource_id, .metrics = {1.77344f, 1.77344f, 1.0f}});
SendGfxEvent(std::move(gfx_event));
}
}
void FakeSession::HandleCreateResource(uint32_t resource_id, fuchsia::ui::gfx::ResourceArgs args) {
if (args.is_image_pipe()) {
FXL_CHECK(!image_pipe_) << "fake supports only one image pipe.";
FXL_CHECK(args.image_pipe().image_pipe_request);
image_pipe_ = std::make_unique<FakeImagePipe>();
image_pipe_->Bind(std::move(args.image_pipe().image_pipe_request));
image_pipe_->OnPresentScene(zx::time(), next_presentation_time_, kPresentationInterval);
if (dump_expectations_) {
image_pipe_->DumpExpectations(expected_display_height_);
}
if (!expected_packets_info_.empty()) {
FXL_CHECK(expected_image_info_);
image_pipe_->SetExpectations(expected_black_image_id_, *expected_black_image_info_,
*expected_image_info_, expected_display_height_,
std::move(expected_packets_info_));
}
} else if (args.is_view()) {
fuchsia::ui::gfx::ViewProperties view_properties;
view_properties.bounding_box.min = {0.0f, 0.0f, -1000.0f};
view_properties.bounding_box.max = {1353.3f, 902.203f, 0.0f};
fuchsia::ui::gfx::Event gfx_event;
gfx_event.set_view_properties_changed({resource_id, std::move(view_properties)});
SendGfxEvent(std::move(gfx_event));
}
resources_by_id_.emplace(resource_id, std::move(args));
}
void FakeSession::HandleReleaseResource(uint32_t resource_id) {
if (resources_by_id_.erase(resource_id) != 1) {
FXL_LOG(ERROR) << "Asked to release unrecognized resource " << resource_id
<< ", closing connection.";
expected_ = false;
binding_.Unbind();
}
}
void FakeSession::HandleAddChild(uint32_t parent_id, uint32_t child_id) {
Resource* parent = FindResource(parent_id);
Resource* child = FindResource(child_id);
if (!parent) {
FXL_LOG(ERROR) << "Asked to add child, parent_id (" << parent_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!parent->can_have_children()) {
FXL_LOG(ERROR) << "Asked to add child, parent_id (" << parent_id
<< ") can't have children, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!child) {
FXL_LOG(ERROR) << "Asked to add child, child_id (" << child_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!child->can_have_parent()) {
FXL_LOG(ERROR) << "Asked to add child, child_id (" << child_id
<< ") can't have parent, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (child->parent_ != kNullResourceId) {
Resource* prev_parent = FindResource(child->parent_);
if (prev_parent != nullptr) {
prev_parent->children_.erase(child_id);
prev_parent->parts_.erase(child_id);
}
}
parent->children_.insert(child_id);
child->parent_ = parent_id;
}
void FakeSession::HandleSetMaterial(uint32_t node_id, uint32_t material_id) {
Resource* node = FindResource(node_id);
Resource* material = FindResource(material_id);
if (!node) {
FXL_LOG(ERROR) << "Asked to set material, node_id (" << node_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!node->can_have_material()) {
FXL_LOG(ERROR) << "Asked to set material, node_id (" << node_id
<< ") can't have material, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!material) {
FXL_LOG(ERROR) << "Asked to set material, material_id (" << material_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!material->is_material()) {
FXL_LOG(ERROR) << "Asked to set material, material_id (" << material_id
<< ") is not a material, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
// TODO(dalesat): Copy material info from |material| to |node|.
}
void FakeSession::HandleSetTexture(uint32_t material_id, uint32_t texture_id) {
Resource* material = FindResource(material_id);
Resource* texture = FindResource(texture_id);
if (!material) {
FXL_LOG(ERROR) << "Asked to set texture, material_id (" << material_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!material->is_material()) {
FXL_LOG(ERROR) << "Asked to set texture, material_id (" << material_id
<< ") is not a material, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!texture) {
FXL_LOG(ERROR) << "Asked to set texture, texture_id (" << texture_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!texture->is_texture()) {
FXL_LOG(ERROR) << "Asked to set texture, texture_id (" << texture_id
<< ") is not a texture, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
// TODO(dalesat): Copy texture info from |texture| to |material|.
}
void FakeSession::HandleSetShape(uint32_t node_id, uint32_t shape_id) {
Resource* node = FindResource(node_id);
Resource* shape = FindResource(shape_id);
if (!node) {
FXL_LOG(ERROR) << "Asked to set shape, node_id (" << node_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!node->can_have_shape()) {
FXL_LOG(ERROR) << "Asked to set shape, node_id (" << node_id
<< ") can't have a shape, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!shape) {
FXL_LOG(ERROR) << "Asked to set shape, shape_id (" << shape_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!shape->is_shape()) {
FXL_LOG(ERROR) << "Asked to set shape, shape_id (" << shape_id
<< ") is not a shape, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
node->shape_args_ = fidl::MakeOptional(fidl::Clone(shape->args_));
}
void FakeSession::HandleSetTranslation(uint32_t node_id,
const fuchsia::ui::gfx::Vector3Value& value) {
Resource* node = FindResource(node_id);
if (!node) {
FXL_LOG(ERROR) << "Asked to set translation, node_id (" << node_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!node->can_have_transform()) {
FXL_LOG(ERROR) << "Asked to set translation, node_id (" << node_id
<< ") can't have a transform, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
node->translation_ = fidl::MakeOptional(value);
}
void FakeSession::HandleSetScale(uint32_t node_id, const fuchsia::ui::gfx::Vector3Value& value) {
Resource* node = FindResource(node_id);
if (!node) {
FXL_LOG(ERROR) << "Asked to set scale, node_id (" << node_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!node->can_have_transform()) {
FXL_LOG(ERROR) << "Asked to set scale, node_id (" << node_id
<< ") can't have a transform, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
node->scale_ = fidl::MakeOptional(value);
}
void FakeSession::HandleSetClipPlanes(uint32_t node_id,
std::vector<fuchsia::ui::gfx::Plane3> value) {
Resource* node = FindResource(node_id);
if (!node) {
FXL_LOG(ERROR) << "Asked to set clip planes, node_id (" << node_id
<< ") not recognized, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
if (!node->can_have_clip_planes()) {
FXL_LOG(ERROR) << "Asked to set clip planes, node_id (" << node_id
<< ") can't have clip planes, closing connection.";
expected_ = false;
binding_.Unbind();
return;
}
std::swap(node->clip_planes_, value);
}
void FakeSession::PresentScene() {
auto now = zx::time(zx_clock_get_monotonic());
next_presentation_time_ = now + kPresentationInterval;
if (image_pipe_) {
image_pipe_->OnPresentScene(now, next_presentation_time_, kPresentationInterval);
}
async::PostTaskForTime(
dispatcher_,
[this, weak_this = GetWeakThis()]() {
if (!weak_this) {
return;
}
PresentScene();
},
next_presentation_time_);
}
void FakeSession::SendGfxEvent(fuchsia::ui::gfx::Event gfx_event) {
fuchsia::ui::scenic::Event event;
event.set_gfx(std::move(gfx_event));
std::vector<fuchsia::ui::scenic::Event> events;
events.push_back(std::move(event));
listener_->OnScenicEvent(std::move(events));
}
void FakeSession::DetectZFighting() {
std::vector<ShapeNode> shape_nodes;
FindShapeNodes(kRootNodeId, {0.0f, 0.0f, 0.0f}, {1.0f, 1.0f, 1.0f}, &shape_nodes);
if (shape_nodes.size() == 0) {
return;
}
for (size_t i = 0; i < shape_nodes.size() - 1; ++i) {
for (size_t j = i + 1; j < shape_nodes.size(); ++j) {
if (shape_nodes[i].Intersects(shape_nodes[j])) {
FXL_LOG(ERROR) << "Node " << shape_nodes[i].id_ << " z-fights with node "
<< shape_nodes[j].id_ << ".";
expected_ = false;
}
}
}
}
void FakeSession::FindShapeNodes(uint32_t node_id, fuchsia::ui::gfx::vec3 translation,
fuchsia::ui::gfx::vec3 scale,
std::vector<ShapeNode>* shape_nodes) {
FXL_CHECK(shape_nodes);
Resource* node = FindResource(node_id);
FXL_CHECK(node);
if (node->translation_) {
FXL_CHECK(node->translation_->variable_id == 0) << "Variables not supported.";
translation.x += node->translation_->value.x * scale.x;
translation.y += node->translation_->value.y * scale.y;
translation.z += node->translation_->value.z * scale.z;
}
if (node->scale_) {
FXL_CHECK(node->scale_->variable_id == 0) << "Variables not supported.";
scale.x *= node->scale_->value.x;
scale.y *= node->scale_->value.y;
scale.z *= node->scale_->value.z;
}
if (node->shape_args_) {
FXL_CHECK(node->shape_args_->is_rectangle()) << "Only rectangle shapes are supported";
fuchsia::ui::gfx::vec3 extent;
FXL_CHECK(node->shape_args_->rectangle().width.is_vector1())
<< "Only vector1 values are supported.";
FXL_CHECK(node->shape_args_->rectangle().height.is_vector1())
<< "Only vector1 values are supported.";
extent.x = scale.x * node->shape_args_->rectangle().width.vector1();
extent.y = scale.y * node->shape_args_->rectangle().height.vector1();
extent.z = scale.z * 0;
shape_nodes->emplace_back(
node_id,
fuchsia::ui::gfx::vec3{translation.x - extent.x / 2.0f, translation.y - extent.y / 2.0f,
translation.z - extent.z / 2.0f},
extent);
}
for (uint32_t child_id : node->children_) {
FindShapeNodes(child_id, translation, scale, shape_nodes);
}
for (uint32_t part_id : node->parts_) {
FindShapeNodes(part_id, translation, scale, shape_nodes);
}
}
} // namespace test
} // namespace media_player