blob: 634d320b1f842c676bc5e245f81b73f8ef985fce [file] [log] [blame]
// Copyright 2022 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/examples/simplest_sysmem/view_provider.h"
#include <fuchsia/sysmem2/cpp/fidl.h>
#include <fuchsia/ui/composition/cpp/fidl.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/ui/scenic/cpp/view_creation_tokens.h>
#include <lib/ui/scenic/cpp/view_identity.h>
#include <zircon/rights.h>
#include <zircon/status.h>
#include <cstdint>
#include <fbl/algorithm.h>
#include "src/ui/examples/simplest_sysmem/png_helper.h"
#include "src/ui/examples/simplest_sysmem/sysmem_helper.h"
namespace sysmem_example {
ViewProviderImpl::ViewProviderImpl(sys::ComponentContext* component_context, RenderType type)
: context_(component_context), render_type_(type) {
context_->outgoing()->AddPublicService<fuchsia::ui::app::ViewProvider>(
bindings_.GetHandler(this));
}
ViewProviderImpl::~ViewProviderImpl() {
context_->outgoing()->RemovePublicService<fuchsia::ui::app::ViewProvider>();
}
void ViewProviderImpl::CreateView2(fuchsia::ui::app::CreateView2Args args) {
flatland_ = context_->svc()->Connect<fuchsia::ui::composition::Flatland>();
flatland_.set_error_handler([](zx_status_t status) {
FX_LOGS(ERROR) << "Lost connection to Scenic: " << zx_status_get_string(status);
});
parent_watcher_.set_error_handler([](zx_status_t status) {
FX_LOGS(ERROR) << "Error from fuchsia::ui::composition::ParentViewportWatcher: "
<< zx_status_get_string(status);
});
auto view_identity = scenic::NewViewIdentityOnCreation();
flatland_->CreateView2(std::move(*args.mutable_view_creation_token()), std::move(view_identity),
/* protocols = */ {}, parent_watcher_.NewRequest());
flatland_->CreateTransform(fuchsia::ui::composition::TransformId({.value = kRootTransformId}));
flatland_->SetRootTransform(fuchsia::ui::composition::TransformId({.value = kRootTransformId}));
// NOTE: These examples only respond to the first GetLayout() calls. Any future
// size change is ignored.
if (render_type_ == sysmem_example::RenderType::RECTANGLE) {
const fuchsia::ui::composition::ContentId kFilledRectId = {1};
flatland_->CreateFilledRect(kFilledRectId);
flatland_->SetSolidFill(kFilledRectId, {1, 0, 1, 1} /* The color fuchsia*/,
{200, 100} /* size */);
// Here we attach view content directly to `kRootTransformId`.
// See |WriteToSysmem| for a simple example of creating a child transform and attaching it to
// the root.
flatland_->SetTranslation(fuchsia::ui::composition::TransformId({.value = kRootTransformId}),
{0, 0});
flatland_->SetContent(fuchsia::ui::composition::TransformId({.value = kRootTransformId}),
kFilledRectId);
}
if (render_type_ == sysmem_example::RenderType::COLOR_BLOCK) {
uint32_t image_width = 256;
uint32_t image_height = 256;
uint32_t total_bytes = image_width * image_height * 4;
uint8_t* image_bytes = new uint8_t[total_bytes];
GenerateColorBlockImage(image_width, image_height, image_bytes);
WriteToSysmem(image_bytes, image_width, image_height, fuchsia::images2::PixelFormat::B8G8R8A8);
}
if (render_type_ == sysmem_example::RenderType::PNG) {
std::vector<uint8_t> write_values;
png_helper::PNGImageSize image_size = {};
uint8_t* image_bytes;
png_helper::LoadPngFromFile(&image_size, &image_bytes);
WriteToSysmem(image_bytes, image_size.width, image_size.height,
fuchsia::images2::PixelFormat::R8G8B8A8);
}
flatland_->Present(fuchsia::ui::composition::PresentArgs{});
}
void ViewProviderImpl::WriteToSysmem(uint8_t* write_values, uint32_t image_width,
uint32_t image_height,
fuchsia::images2::PixelFormat pixel_format) {
context_->svc()->Connect(sysmem_allocator_.NewRequest());
context_->svc()->Connect(flatland_allocator_.NewRequest());
sysmem_helper::BufferCollectionImportExportTokens ref_pair =
sysmem_helper::BufferCollectionImportExportTokens::New();
fuchsia::sysmem2::BufferCollectionTokenSyncPtr local_token;
fuchsia::sysmem2::BufferCollectionTokenSyncPtr dup_token;
fuchsia::sysmem2::AllocatorAllocateSharedCollectionRequest allocate_request;
allocate_request.set_token_request(local_token.NewRequest());
zx_status_t status = sysmem_allocator_->AllocateSharedCollection(std::move(allocate_request));
FX_CHECK(status == ZX_OK) << "Cannot allocate shared collection: "
<< zx_status_get_string(status);
fuchsia::sysmem2::BufferCollectionTokenDuplicateRequest dup_request;
dup_request.set_rights_attenuation_mask(ZX_RIGHT_SAME_RIGHTS);
dup_request.set_token_request(dup_token.NewRequest());
status = local_token->Duplicate(std::move(dup_request));
FX_CHECK(status == ZX_OK) << "Cannot duplicate token: " << zx_status_get_string(status);
fuchsia::sysmem2::Node_Sync_Result sync_result;
status = local_token->Sync(&sync_result);
FX_CHECK(sync_result.is_response());
fuchsia::ui::composition::RegisterBufferCollectionArgs args = {};
args.set_export_token(std::move(ref_pair.export_token));
// BufferCollectionToken zircon handles are interchangeable between fuchsia::sysmem2
// and fuchsia::sysmem(1).
args.set_buffer_collection_token(fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>(
dup_token.Unbind().TakeChannel()));
args.set_usages(fuchsia::ui::composition::RegisterBufferCollectionUsages::DEFAULT);
fuchsia::sysmem2::BufferCollectionSyncPtr buffer_collection;
fuchsia::sysmem2::AllocatorBindSharedCollectionRequest bind_request;
bind_request.set_token(std::move(local_token));
bind_request.set_buffer_collection_request(buffer_collection.NewRequest());
status = sysmem_allocator_->BindSharedCollection(std::move(bind_request));
FX_CHECK(status == ZX_OK) << "Cannot bind shared collection: " << zx_status_get_string(status);
const uint8_t kBytesPerPixel = 4;
fuchsia::sysmem2::BufferCollectionSetConstraintsRequest constraints_request;
constraints_request.set_constraints(
sysmem_helper::CreateDefaultConstraints(sysmem_helper::BufferConstraint{
.buffer_count = 1,
.image_width = image_width,
.image_height = image_height,
.bytes_per_pixel = kBytesPerPixel,
.pixel_format_type = pixel_format,
}));
buffer_collection->SetConstraints(std::move(constraints_request));
fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result result;
flatland_allocator_->RegisterBufferCollection(std::move(args), &result);
FX_CHECK(!result.is_err()) << "register buffer collection errored.";
fuchsia::sysmem2::BufferCollection_WaitForAllBuffersAllocated_Result wait_result;
status = buffer_collection->WaitForAllBuffersAllocated(&wait_result);
FX_CHECK(wait_result.is_response());
FX_CHECK(status == ZX_OK) << "WaitForBuffersAllocated failed with status: "
<< zx_status_get_string(status);
status = buffer_collection->Release();
FX_CHECK(status == ZX_OK);
uint32_t buffer_collection_idx = 0;
const auto& buffer_collection_info = wait_result.response().buffer_collection_info();
uint32_t bytes_per_row = fbl::round_up(
std::max(image_width * kBytesPerPixel,
buffer_collection_info.settings().image_format_constraints().min_bytes_per_row()),
buffer_collection_info.settings().image_format_constraints().bytes_per_row_divisor());
sysmem_helper::MapHostPointer(buffer_collection_info, buffer_collection_idx,
[write_values, bytes_per_row, image_width, image_height](
uint8_t* vmo_host, uint32_t num_bytes) {
uint32_t valid_bytes_per_row = image_width * kBytesPerPixel;
FX_CHECK(num_bytes >= bytes_per_row * image_height);
FX_CHECK(bytes_per_row >= valid_bytes_per_row);
if (bytes_per_row == valid_bytes_per_row) {
// When the memory buffer allocated is exactly the same size
// as the image to draw. Note the
// buffer_collection_info.settings.image_format_constraints.bytes_per_row_divisor
// value, buffer row size must be divisible by
// `image_format_constraints.bytes_per_row_divisor`, which isn't
// always the same as the image's actual size (width * height *
// kBytesPerPixel).
uint32_t total_bytes = valid_bytes_per_row * image_height;
memcpy(vmo_host, write_values, total_bytes);
} else {
// Copy over row-by-row.
for (size_t i = 0; i < image_height; ++i) {
memcpy(&vmo_host[i * bytes_per_row],
write_values + (i * valid_bytes_per_row),
valid_bytes_per_row);
}
}
});
if (buffer_collection_info.settings().buffer_settings().coherency_domain() ==
fuchsia::sysmem2::CoherencyDomain::RAM) {
buffer_collection_info.buffers()
.at(buffer_collection_idx)
.vmo()
.op_range(ZX_VMO_OP_CACHE_CLEAN, 0,
buffer_collection_info.settings().buffer_settings().size_bytes(), nullptr, 0);
}
// Call flatland to show image
const fuchsia::ui::composition::ContentId kChildContentId = {1};
const fuchsia::ui::composition::TransformId kChildTransformId = {2};
fuchsia::ui::composition::ImageProperties image_properties = {};
image_properties.set_size({image_width, image_height});
flatland_->CreateImage(kChildContentId, std::move(ref_pair.import_token), buffer_collection_idx,
std::move(image_properties));
flatland_->CreateTransform(kChildTransformId);
flatland_->SetContent(kChildTransformId, kChildContentId);
flatland_->SetImageDestinationSize(kChildContentId, {image_width, image_height});
flatland_->SetTranslation(kChildTransformId, {0, 0} /*translation*/);
flatland_->AddChild(fuchsia::ui::composition::TransformId{kRootTransformId}, kChildTransformId);
}
void ViewProviderImpl::GenerateColorBlockImage(uint32_t image_width, uint32_t image_height,
uint8_t* write_values) {
uint64_t i = 0;
for (uint32_t row = 0; row < image_height; row++) {
for (uint32_t col = 0; col < image_width; col++) {
// Top-left
if (row < image_height / 2 && col < image_width / 2) {
write_values[i++] = kRed[0];
write_values[i++] = kRed[1];
write_values[i++] = kRed[2];
write_values[i++] = kRed[3];
}
// Top-right
else if (row < image_height / 2 && col >= image_width / 2) {
write_values[i++] = kGreen[0];
write_values[i++] = kGreen[1];
write_values[i++] = kGreen[2];
write_values[i++] = kGreen[3];
}
// Bottom-left
else if (row >= image_height / 2 && col < image_width / 2) {
write_values[i++] = kYellow[0];
write_values[i++] = kYellow[1];
write_values[i++] = kYellow[2];
write_values[i++] = kYellow[3];
}
// Bottom-right
else if (row >= image_height / 2 && col >= image_width / 2) {
write_values[i++] = kBlue[0];
write_values[i++] = kBlue[1];
write_values[i++] = kBlue[2];
write_values[i++] = kBlue[3];
}
}
}
}
} // namespace sysmem_example