blob: bd2999231eef86de249693f722976d8f7129d293 [file] [log] [blame]
// Copyright 2021 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/flatland/engine/engine.h"
#include <fidl/fuchsia.hardware.display.types/cpp/fidl.h>
#include <lib/async/cpp/time.h>
#include <lib/syslog/cpp/macros.h>
#include <sstream>
#include <string>
#include "src/ui/scenic/lib/flatland/global_image_data.h"
#include "src/ui/scenic/lib/flatland/global_matrix_data.h"
#include "src/ui/scenic/lib/flatland/global_topology_data.h"
#include "src/ui/scenic/lib/flatland/scene_dumper.h"
#include "src/ui/scenic/lib/scheduling/frame_scheduler.h"
// Hardcoded double buffering.
// TODO(https://fxbug.dev/42156567): make this configurable. Even fancier: is it worth considering
// sharing a pool of framebuffers between multiple displays? (assuming that their dimensions are
// similar, etc.)
static constexpr uint32_t kNumDisplayFramebuffers = 2;
namespace flatland {
namespace {
void SignalAll(const std::vector<zx::event>& events) {
for (auto& e : events) {
e.signal(0u, ZX_EVENT_SIGNALED);
}
}
} // namespace
Engine::Engine(std::shared_ptr<DisplayCompositor> flatland_compositor,
std::shared_ptr<FlatlandPresenterImpl> flatland_presenter,
std::shared_ptr<UberStructSystem> uber_struct_system,
std::shared_ptr<LinkSystem> link_system, inspect::Node inspect_node,
GetRootTransformFunc get_root_transform)
: flatland_compositor_(std::move(flatland_compositor)),
flatland_presenter_(std::move(flatland_presenter)),
uber_struct_system_(std::move(uber_struct_system)),
link_system_(std::move(link_system)),
inspect_node_(std::move(inspect_node)),
get_root_transform_(std::move(get_root_transform)) {
FX_DCHECK(flatland_compositor_);
FX_DCHECK(flatland_presenter_);
FX_DCHECK(uber_struct_system_);
FX_DCHECK(link_system_);
InitializeInspectObjects();
}
constexpr char kSceneDump[] = "scene_dump";
void Engine::InitializeInspectObjects() {
inspect_scene_dump_ = inspect_node_.CreateLazyValues(kSceneDump, [this] {
inspect::Inspector inspector;
const auto root_transform = get_root_transform_();
if (!root_transform) {
inspector.GetRoot().CreateString(kSceneDump, "(No Root Transform)", &inspector);
return fpromise::make_ok_promise(std::move(inspector));
}
const SceneState scene_state(*this, *root_transform);
std::ostringstream output;
DumpScene(scene_state.snapshot, scene_state.topology_data, scene_state.images,
scene_state.image_indices, scene_state.image_rectangles, output);
inspector.GetRoot().CreateString(kSceneDump, output.str(), &inspector);
return fpromise::make_ok_promise(std::move(inspector));
});
}
void Engine::RenderScheduledFrame(uint64_t frame_number, zx::time presentation_time,
const FlatlandDisplay& display,
scheduling::FramePresentedCallback callback) {
// Emit a counter called "ScenicRender" for visualization in the Trace Viewer.
static bool render_edge_flag = false;
TRACE_COUNTER("gfx", "ScenicRender", 0, "", TA_UINT32(render_edge_flag = !render_edge_flag));
// NOTE: this name is important for benchmarking. Do not remove or modify it
// without also updating the "process_gfx_trace.go" script.
TRACE_DURATION("gfx", "RenderFrame", "frame_number", frame_number, "time",
presentation_time.get());
TRACE_FLOW_STEP("gfx", "scenic_frame", frame_number);
SceneState scene_state(*this, display.root_transform());
scenic_impl::display::Display* const hw_display = display.display();
#if defined(USE_FLATLAND_VERBOSE_LOGGING)
std::ostringstream str;
str << "Engine::RenderScheduledFrame()\n"
<< "Root transform of global topology: " << scene_state.topology_data.topology_vector[0]
<< "\nTopologically-sorted transforms and their corresponding parent transforms:";
for (size_t i = 1; i < scene_state.topology_data.topology_vector.size(); ++i) {
str << "\n " << scene_state.topology_data.topology_vector[i] << " -> "
<< scene_state.topology_data.topology_vector[scene_state.topology_data.parent_indices[i]];
}
str << "\nFrame display-list contains " << scene_state.image_rectangles.size()
<< " image-rectangles and " << scene_state.images.size() << " images.";
for (auto& r : scene_state.image_rectangles) {
str << "\n rect: " << r;
}
for (auto& i : scene_state.images) {
str << "\n image: " << i;
}
FLATLAND_VERBOSE_LOG << str.str();
#endif
link_system_->UpdateLinks(scene_state.topology_data.topology_vector,
scene_state.topology_data.live_handles, scene_state.global_matrices,
hw_display->device_pixel_ratio(), scene_state.snapshot);
// TODO(https://fxbug.dev/42156567): hack! need a better place to call AddDisplay().
if (hack_seen_display_id_values_.find(hw_display->display_id().value) ==
hack_seen_display_id_values_.end()) {
// This display hasn't been added to the DisplayCompositor yet.
hack_seen_display_id_values_.insert(hw_display->display_id().value);
DisplayInfo display_info{
.dimensions = glm::uvec2{hw_display->width_in_px(), hw_display->height_in_px()},
.formats = display.display()->pixel_formats()};
fuchsia::sysmem2::BufferCollectionInfo render_target_info;
flatland_compositor_->AddDisplay(hw_display, display_info,
/*num_vmos*/ kNumDisplayFramebuffers, &render_target_info);
}
CullRectangles(&scene_state.image_rectangles, &scene_state.images, hw_display->width_in_px(),
hw_display->height_in_px());
{
TRACE_DURATION("gfx", "flatland::Engine::RenderScheduledFrame[move topology_data]");
last_global_topology_data_ = std::move(scene_state.topology_data);
}
// Don't render any initial frames if there is no image that could actually be rendered. We do
// this to avoid triggering any changes in the display until we have content ready to render. We
// invoke |callback| to continue the render loop.
if (!first_frame_with_image_is_rendered_) {
if (scene_state.images.empty()) {
SkipRender(std::move(callback));
return;
}
first_frame_with_image_is_rendered_ = true;
}
flatland_compositor_->RenderFrame(frame_number, presentation_time,
{{.rectangles = std::move(scene_state.image_rectangles),
.images = std::move(scene_state.images),
.display_id = hw_display->display_id()}},
flatland_presenter_->TakeReleaseFences(), std::move(callback));
}
view_tree::SubtreeSnapshot Engine::GenerateViewTreeSnapshot(
const TransformHandle& root_transform) const {
TRACE_DURATION("gfx", "flatland::Engine::GenerateViewTreeSnapshot");
const auto& uber_struct_snapshot = uber_struct_system_->Snapshot();
const auto link_child_to_parent_transform_map = link_system_->GetLinkChildToParentTransformMap();
const auto& topology_data = last_global_topology_data_;
const auto matrix_vector = ComputeGlobalMatrices(
topology_data.topology_vector, topology_data.parent_indices, uber_struct_snapshot);
auto global_clip_regions =
ComputeGlobalTransformClipRegions(topology_data.topology_vector, topology_data.parent_indices,
matrix_vector, uber_struct_snapshot);
auto hit_regions =
ComputeGlobalHitRegions(topology_data.topology_vector, topology_data.parent_indices,
matrix_vector, uber_struct_snapshot);
return flatland::GlobalTopologyData::GenerateViewTreeSnapshot(
topology_data, std::move(hit_regions), std::move(global_clip_regions), matrix_vector,
link_child_to_parent_transform_map);
}
// TODO(https://fxbug.dev/42162342) If we put Screenshot on its own thread, we should make this call
// thread safe.
Renderables Engine::GetRenderables(const FlatlandDisplay& display) {
TransformHandle root = display.root_transform();
SceneState scene_state(*this, root);
const auto hw_display = display.display();
CullRectangles(&scene_state.image_rectangles, &scene_state.images, hw_display->width_in_px(),
hw_display->height_in_px());
return std::make_pair(std::move(scene_state.image_rectangles), std::move(scene_state.images));
}
Engine::SceneState::SceneState(Engine& engine, TransformHandle root_transform) {
TRACE_DURATION("gfx", "flatland::Engine::SceneState");
snapshot = engine.uber_struct_system_->Snapshot();
const auto links = engine.link_system_->GetResolvedTopologyLinks();
const auto link_system_id = engine.link_system_->GetInstanceId();
topology_data = GlobalTopologyData::ComputeGlobalTopologyData(snapshot, links, link_system_id,
root_transform);
global_matrices =
ComputeGlobalMatrices(topology_data.topology_vector, topology_data.parent_indices, snapshot);
auto [indices, im] =
ComputeGlobalImageData(topology_data.topology_vector, topology_data.parent_indices, snapshot);
this->image_indices = std::move(indices);
this->images = std::move(im);
const auto global_image_sample_regions = ComputeGlobalImageSampleRegions(
topology_data.topology_vector, topology_data.parent_indices, snapshot);
const auto global_clip_regions = ComputeGlobalTransformClipRegions(
topology_data.topology_vector, topology_data.parent_indices, global_matrices, snapshot);
image_rectangles =
ComputeGlobalRectangles(FilterByIndices(global_matrices, image_indices),
FilterByIndices(global_image_sample_regions, image_indices),
FilterByIndices(global_clip_regions, image_indices), images);
}
void Engine::SkipRender(scheduling::FramePresentedCallback callback) {
SignalAll(flatland_presenter_->TakeReleaseFences());
const auto now = async::Now(async_get_default_dispatcher());
callback({now, now});
}
} // namespace flatland