blob: 2ecb718aaebaefcf2f41a353c62d76c11fdc3468 [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/resources/snapshot/snapshotter.h"
#include <lib/fit/function.h>
#include <lib/zx/vmo.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/import.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/lights/point_light.h"
#include "garnet/lib/ui/gfx/resources/material.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/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/resource_visitor.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/version.h"
#include "garnet/lib/ui/gfx/resources/view.h"
#include "garnet/lib/ui/gfx/resources/view_holder.h"
#include "lib/escher/util/trace_macros.h"
#include "lib/escher/vk/image.h"
#include "lib/fsl/vmo/sized_vmo.h"
#include "lib/fsl/vmo/vector.h"
#include "lib/fxl/logging.h"
namespace scenic_impl {
namespace gfx {
// Helper function to create a |SizedVmo| from bytes of certain size.
bool VmoFromBytes(const uint8_t* bytes, size_t num_bytes, uint32_t type,
uint32_t version, fsl::SizedVmo* sized_vmo_ptr) {
FXL_CHECK(sized_vmo_ptr);
zx::vmo vmo;
size_t total_size = num_bytes + sizeof(type) + sizeof(version);
zx_status_t status = zx::vmo::create(total_size, 0u, &vmo);
if (status < 0) {
FXL_LOG(WARNING) << "zx::vmo::create failed: " << status;
return false;
}
if (num_bytes > 0) {
status = vmo.write(&type, 0, sizeof(uint32_t));
if (status < 0) {
FXL_LOG(WARNING) << "zx::vmo::write snapshot type failed: " << status;
return false;
}
status = vmo.write(&version, sizeof(uint32_t), sizeof(uint32_t));
if (status < 0) {
FXL_LOG(WARNING) << "zx::vmo::write snapshot version failed: " << status;
return false;
}
status = vmo.write(bytes, 2 * sizeof(uint32_t), num_bytes);
if (status < 0) {
FXL_LOG(WARNING) << "zx::vmo::write bytes failed: " << status;
return false;
}
}
*sized_vmo_ptr = fsl::SizedVmo(std::move(vmo), total_size);
return true;
}
Snapshotter::Snapshotter(std::unique_ptr<escher::BatchGpuUploader> gpu_uploader)
: gpu_uploader_(std::move(gpu_uploader)) {}
void Snapshotter::TakeSnapshot(Resource* resource,
TakeSnapshotCallback callback) {
FXL_DCHECK(resource) << "Cannot snapshot null resource.";
// Visit the scene graph starting with |resource| and collecting images
// and buffers to read from the GPU.
resource->Accept(this);
// Submit all images/buffers to be read from GPU.
gpu_uploader_->Submit([node_serializer = current_node_serializer_,
callback = std::move(callback)]() {
TRACE_DURATION("gfx", "Snapshotter::Serialize");
auto builder = std::make_shared<flatbuffers::FlatBufferBuilder>();
builder->Finish(node_serializer->serialize(*builder));
fsl::SizedVmo sized_vmo;
if (!VmoFromBytes(builder->GetBufferPointer(), builder->GetSize(),
SnapshotData::SnapshotType::kFlatBuffer,
SnapshotData::SnapshotVersion::v1_0, &sized_vmo)) {
return callback(fuchsia::mem::Buffer{});
} else {
return callback(std::move(sized_vmo).ToTransport());
}
});
}
void Snapshotter::Visit(EntityNode* r) { VisitNode(r); }
void Snapshotter::Visit(OpacityNode* r) { VisitNode(r); }
void Snapshotter::Visit(ShapeNode* r) {
if (r->shape() && r->material()) {
VisitNode(r);
r->shape()->Accept(this);
r->material()->Accept(this);
}
}
void Snapshotter::Visit(Scene* r) {
// TODO(SCN-1221): Should handle Scene better, e.g. storing the lights.
VisitNode(r);
}
void Snapshotter::Visit(CircleShape* r) {
FXL_DCHECK(current_node_serializer_);
auto shape = std::make_shared<CircleSerializer>();
shape->radius = r->radius();
current_node_serializer_->shape = shape;
}
void Snapshotter::Visit(RectangleShape* r) {
FXL_DCHECK(current_node_serializer_);
auto shape = std::make_shared<RectangleSerializer>();
shape->width = r->width();
shape->height = r->height();
current_node_serializer_->shape = shape;
}
void Snapshotter::Visit(RoundedRectangleShape* r) {
FXL_DCHECK(current_node_serializer_);
auto shape = std::make_shared<RoundedRectangleSerializer>();
shape->width = r->width();
shape->height = r->height();
shape->top_left_radius = r->top_left_radius();
shape->top_right_radius = r->top_right_radius();
shape->bottom_right_radius = r->bottom_right_radius();
shape->bottom_left_radius = r->bottom_left_radius();
current_node_serializer_->shape = shape;
VisitMesh(r->escher_mesh());
}
void Snapshotter::Visit(MeshShape* r) {
FXL_DCHECK(current_node_serializer_);
current_node_serializer_->shape = std::make_shared<MeshSerializer>();
VisitMesh(r->escher_mesh());
}
void Snapshotter::Visit(Material* r) {
if (r->texture_image()) {
r->texture_image()->Accept(this);
} else {
auto color = std::make_shared<ColorSerializer>();
color->red = r->red();
color->green = r->green();
color->blue = r->blue();
color->alpha = r->alpha();
current_node_serializer_->material = color;
}
VisitResource(r);
}
void Snapshotter::Visit(Memory* r) { VisitResource(r); }
void Snapshotter::Visit(Image* r) {
VisitImage(r->GetEscherImage());
VisitResource(r);
}
void Snapshotter::Snapshotter::Visit(ImagePipe* r) {
VisitImage(r->GetEscherImage());
VisitResource(r);
}
void Snapshotter::Visit(Buffer* r) { VisitResource(r); }
void Snapshotter::Visit(View* r) { VisitResource(r); }
void Snapshotter::Visit(ViewHolder* r) { VisitResource(r); }
void Snapshotter::Visit(Compositor* r) { r->layer_stack()->Accept(this); }
void Snapshotter::Visit(DisplayCompositor* r) {
r->layer_stack()->Accept(this);
}
void Snapshotter::Visit(LayerStack* r) {
for (auto& layer : r->layers()) {
layer->Accept(this);
}
}
void Snapshotter::Visit(Layer* r) { r->renderer()->Accept(this); }
void Snapshotter::Visit(Camera* r) { r->scene()->Accept(this); }
void Snapshotter::Visit(Renderer* r) { r->camera()->Accept(this); }
void Snapshotter::Visit(Light* r) { VisitResource(r); }
void Snapshotter::Visit(AmbientLight* r) { VisitResource(r); }
void Snapshotter::Visit(DirectionalLight* r) { VisitResource(r); }
void Snapshotter::Visit(PointLight* r) { VisitResource(r); }
void Snapshotter::Visit(Import* r) { r->delegate()->Accept(this); }
void Snapshotter::VisitNode(Node* r) {
auto parent_serializer = current_node_serializer_;
auto node_serializer = std::make_shared<NodeSerializer>();
if (parent_serializer) {
parent_serializer->children.push_back(node_serializer);
}
// Name.
node_serializer->name = r->label();
// Transform.
if (!r->transform().IsIdentity()) {
auto transform = std::make_shared<TransformSerializer>();
node_serializer->transform = transform;
auto& translation = r->translation();
transform->translation =
snapshot::Vec3(translation.x, translation.y, translation.z);
auto& scale = r->scale();
transform->scale = snapshot::Vec3(scale.x, scale.y, scale.z);
auto& rotation = r->rotation();
transform->rotation =
snapshot::Quat(rotation.x, rotation.y, rotation.z, rotation.w);
auto& anchor = r->anchor();
transform->anchor = snapshot::Vec3(anchor.x, anchor.y, anchor.z);
}
// Children.
for (auto& part : r->parts()) {
// Set current node to this node during children traversal.
current_node_serializer_ = node_serializer;
part->Accept(this);
}
for (auto& child : r->children()) {
// Set current node to this node during children traversal.
current_node_serializer_ = node_serializer;
child->Accept(this);
}
// Set current node to this node during children traversal.
current_node_serializer_ = node_serializer;
VisitResource(r);
current_node_serializer_ = node_serializer;
}
void Snapshotter::VisitResource(Resource* r) {
auto node_serializer = current_node_serializer_;
for (auto& import : r->imports()) {
current_node_serializer_ = node_serializer;
import->Accept(this);
}
current_node_serializer_ = node_serializer;
}
void Snapshotter::VisitImage(escher::ImagePtr image) {
if (!image) {
return;
}
auto format = (int32_t)image->format();
auto width = image->width();
auto height = image->height();
ReadImage(image,
[format, width, height, node_serializer = current_node_serializer_](
escher::BufferPtr buffer) {
auto image = std::make_shared<ImageSerializer>();
image->buffer = buffer;
image->format = format;
image->width = width;
image->height = height;
node_serializer->material = image;
});
}
void Snapshotter::VisitMesh(escher::MeshPtr mesh) {
FXL_DCHECK(current_node_serializer_);
auto geometry = std::make_shared<GeometrySerializer>();
geometry->bbox_min =
snapshot::Vec3(mesh->bounding_box().min().x, mesh->bounding_box().min().y,
mesh->bounding_box().min().z);
geometry->bbox_max =
snapshot::Vec3(mesh->bounding_box().max().x, mesh->bounding_box().max().y,
mesh->bounding_box().max().z);
current_node_serializer_->mesh = geometry;
for (int i = -1; i < (int)mesh->attribute_buffers().size(); i++) {
// -1 implies index buffer, >=0 is attribute buffer.
bool is_index_buffer = i == -1;
auto src_buffer = is_index_buffer ? mesh->index_buffer()
: mesh->attribute_buffer(i).buffer;
// Attribute buffer other than primarily attribute buffers can be null.
if (!src_buffer) {
continue;
}
ReadBuffer(src_buffer, [geometry, is_index_buffer,
mesh](escher::BufferPtr buffer) {
if (is_index_buffer) {
auto indices = std::make_shared<IndexBufferSerializer>();
indices->buffer = buffer;
indices->index_count = mesh->num_indices();
geometry->indices = indices;
} else {
auto attribute = std::make_shared<AttributeBufferSerializer>();
attribute->buffer = buffer;
attribute->vertex_count = mesh->num_vertices();
attribute->stride = mesh->spec().stride(0);
geometry->attributes.push_back(attribute);
}
});
}
}
void Snapshotter::ReadImage(
escher::ImagePtr image,
fit::function<void(escher::BufferPtr buffer)> callback) {
vk::BufferImageCopy region;
region.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageExtent.width = image->width();
region.imageExtent.height = image->height();
region.imageExtent.depth = 1;
region.bufferOffset = 0;
auto reader = gpu_uploader_->AcquireReader(image->size());
reader->ReadImage(image, region);
gpu_uploader_->PostReader(std::move(reader), std::move(callback));
}
void Snapshotter::ReadBuffer(
escher::BufferPtr buffer,
fit::function<void(escher::BufferPtr buffer)> callback) {
auto reader = gpu_uploader_->AcquireReader(buffer->size());
reader->ReadBuffer(buffer, {0, 0, buffer->size()});
gpu_uploader_->PostReader(std::move(reader), std::move(callback));
}
} // namespace gfx
} // namespace scenic_impl