blob: 60bf935c9d4fb611ef973534424029626f43c26e [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 "src/ui/scenic/lib/gfx/engine/session.h"
#include <lib/async/cpp/task.h>
#include <lib/async/default.h>
#include <lib/fdio/directory.h>
#include <lib/fostr/fidl/fuchsia/ui/gfx/formatting.h>
#include <lib/trace/event.h>
#include <memory>
#include <utility>
#include <fbl/auto_call.h>
#include "src/ui/lib/escher/hmd/pose_buffer.h"
#include "src/ui/lib/escher/renderer/batch_gpu_uploader.h"
#include "src/ui/lib/escher/shape/mesh.h"
#include "src/ui/lib/escher/util/type_utils.h"
#include "src/ui/scenic/lib/gfx/engine/gfx_command_applier.h"
#include "src/ui/scenic/lib/gfx/resources/compositor/layer_stack.h"
#include "src/ui/scenic/lib/gfx/swapchain/swapchain_factory.h"
#include "src/ui/scenic/lib/gfx/util/time.h"
#include "src/ui/scenic/lib/gfx/util/unwrap.h"
#include "src/ui/scenic/lib/gfx/util/wrap.h"
#include "src/ui/scenic/lib/scheduling/frame_scheduler.h"
using scheduling::Present2Info;
namespace scenic_impl {
namespace gfx {
Session::Session(SessionId id, SessionContext session_context,
std::shared_ptr<EventReporter> event_reporter,
std::shared_ptr<ErrorReporter> error_reporter, inspect::Node inspect_node)
: id_(id),
error_reporter_(std::move(error_reporter)),
event_reporter_(std::move(event_reporter)),
session_context_(std::move(session_context)),
resource_context_(
/* Sessions can be used in integration tests, with and without Vulkan.
When Vulkan is unavailable, the Escher pointer is null. These
ternaries protect against null-pointer dispatching for these
non-Vulkan tests. */
{session_context_.vk_device,
session_context_.escher != nullptr ? session_context_.escher->vk_physical_device()
: vk::PhysicalDevice(),
session_context_.escher != nullptr ? session_context_.escher->device()->dispatch_loader()
: vk::DispatchLoaderDynamic(),
session_context_.escher != nullptr ? session_context_.escher->device()->caps()
: escher::VulkanDeviceQueues::Caps(),
session_context_.escher_resource_recycler, session_context_.escher_image_factory,
session_context_.escher != nullptr ? session_context_.escher->sampler_cache()
: nullptr}),
resources_(error_reporter_),
view_tree_updater_(id),
inspect_node_(std::move(inspect_node)),
weak_factory_(this) {
FX_DCHECK(error_reporter_);
FX_DCHECK(event_reporter_);
inspect_resource_count_ = inspect_node_.CreateUint("resource_count", 0);
zx_status_t status = fdio_service_connect("/svc/fuchsia.sysmem.Allocator",
sysmem_allocator_.NewRequest().TakeChannel().release());
FX_DCHECK(status == ZX_OK);
}
Session::~Session() {
if (root_view_ && session_context_.scene_graph) {
session_context_.scene_graph->InvalidateAnnotationViewHolder(root_view_->view_ref_koid());
}
resources_.Clear();
scheduled_updates_ = {};
FX_CHECK(resource_count_ == 0) << "Session::~Session(): " << resource_count_
<< " resources have not yet been destroyed.";
}
void Session::DispatchCommand(fuchsia::ui::scenic::Command command,
scheduling::PresentId present_id) {
FX_DCHECK(command.Which() == fuchsia::ui::scenic::Command::Tag::kGfx);
FX_DCHECK(scheduled_updates_.empty() || scheduled_updates_.front().present_id <= present_id);
scheduled_updates_.emplace(present_id, std::move(command.gfx()));
}
EventReporter* Session::event_reporter() const { return event_reporter_.get(); }
void Session::UpdateAndStageViewTreeUpdates(SceneGraph* scene_graph) {
view_tree_updater_.UpdateViewHolderConnections();
view_tree_updater_.StageViewTreeUpdates(scene_graph);
}
bool Session::ApplyScheduledUpdates(CommandContext* command_context,
scheduling::PresentId present_id) {
// RAII object to ensure UpdateViewHolderConnections and StageViewTreeUpdates, on all exit paths.
fbl::AutoCall cleanup([this, command_context] {
UpdateAndStageViewTreeUpdates(command_context->scene_graph.get());
});
// Batch all updates up to |present_id|.
std::vector<::fuchsia::ui::gfx::Command> commands;
while (!scheduled_updates_.empty() && scheduled_updates_.front().present_id <= present_id) {
std::move(scheduled_updates_.front().commands.begin(),
scheduled_updates_.front().commands.end(), std::back_inserter(commands));
scheduled_updates_.pop();
}
if (!ApplyUpdate(command_context, std::move(commands))) {
// An error was encountered while applying the update.
FX_LOGS(WARNING) << "scenic_impl::gfx::Session::ApplyScheduledUpdates(): "
"An error was encountered while applying the update. "
"Initiating teardown.";
// Update failed. Do not handle any additional updates and clear any pending updates.
scheduled_updates_ = {};
return false;
}
// Updates have been applied - inspect latest session resource and tree stats.
inspect_resource_count_.Set(resource_count_);
auto buffer_collections_it = deregistered_buffer_collections_.begin();
while (buffer_collections_it != deregistered_buffer_collections_.end()) {
if (buffer_collections_it->ImageResourceIds().empty()) {
deregistered_buffer_collections_.erase(buffer_collections_it);
}
++buffer_collections_it;
}
return true;
}
void Session::EnqueueEvent(::fuchsia::ui::gfx::Event event) {
event_reporter_->EnqueueEvent(std::move(event));
}
void Session::EnqueueEvent(::fuchsia::ui::input::InputEvent event) {
event_reporter_->EnqueueEvent(std::move(event));
}
bool Session::SetRootView(fxl::WeakPtr<View> view) {
// Check that the root view ID is being set or being cleared. If there is
// already a root view, another cannot be set.
if (root_view_) {
return false;
}
root_view_ = view;
return true;
}
bool Session::ApplyUpdate(CommandContext* command_context,
std::vector<::fuchsia::ui::gfx::Command> commands) {
TRACE_DURATION("gfx", "Session::ApplyUpdate");
for (auto& command : commands) {
if (!ApplyCommand(command_context, std::move(command))) {
error_reporter_->ERROR() << "scenic_impl::gfx::Session::ApplyCommand() "
"failed to apply Command: "
<< command;
return false;
}
}
return true;
}
void Session::RegisterBufferCollection(
uint32_t buffer_collection_id,
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token) {
if (buffer_collection_id == 0) {
error_reporter_->ERROR() << "RegisterBufferCollection called with buffer_collection_id 0.";
return;
}
if (buffer_collections_.count(buffer_collection_id)) {
error_reporter_->ERROR()
<< "RegisterBufferCollection called with pre-existing buffer_collection_id "
<< buffer_collection_id << ".";
return;
}
auto result =
BufferCollectionInfo::New(sysmem_allocator_.get(), session_context_.escher, std::move(token));
if (result.is_error()) {
error_reporter_->ERROR() << "Unable to register collection.";
return;
}
buffer_collections_[buffer_collection_id] = std::move(result.value());
}
void Session::DeregisterBufferCollection(uint32_t buffer_collection_id) {
if (buffer_collection_id == 0) {
error_reporter_->ERROR() << "DeregisterBufferCollection called with buffer_collection_id 0.";
return;
}
auto buffer_collection = buffer_collections_.extract(buffer_collection_id);
if (buffer_collection.empty()) {
error_reporter_->ERROR() << "DeregisterBufferCollection failed, buffer_collection_id "
<< buffer_collection_id << " not found.";
return;
}
deregistered_buffer_collections_.push_back(std::move(buffer_collection.mapped()));
}
} // namespace gfx
} // namespace scenic_impl