blob: 8a50fdfb45935a573ef4de25e5f7b3b8a0468283 [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/screen_recording/view_provider.h"
#include <fuchsia/sysmem2/cpp/fidl.h>
#include <fuchsia/ui/composition/cpp/fidl.h>
#include <fuchsia/ui/composition/internal/cpp/fidl.h>
#include <lib/async/cpp/wait.h>
#include <lib/async/default.h>
#include <lib/sys/cpp/component_context.h>
#include <lib/syslog/cpp/macros.h>
#include <lib/trace/event.h>
#include <lib/ui/scenic/cpp/view_creation_tokens.h>
#include <lib/ui/scenic/cpp/view_identity.h>
#include <zircon/status.h>
#include <cstdint>
#include <fbl/algorithm.h>
#include "src/ui/examples/screen_recording/screen_capture_helper.h"
#include "src/ui/scenic/lib/allocation/allocator.h"
#include "src/ui/scenic/lib/allocation/buffer_collection_import_export_tokens.h"
#include "src/ui/scenic/lib/utils/helpers.h"
namespace screen_recording_example {
using fuchsia::ui::composition::RegisterBufferCollectionUsages;
using fuchsia::ui::composition::internal::FrameInfo;
using fuchsia::ui::composition::internal::ScreenCapture;
using fuchsia::ui::composition::internal::ScreenCaptureConfig;
using fuchsia::ui::composition::internal::ScreenCaptureError;
ViewProviderImpl::ViewProviderImpl(sys::ComponentContext* component_context)
: context_(component_context) {
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) {
context_->svc()->Connect(sysmem_allocator_.NewRequest());
context_->svc()->Connect(flatland_allocator_.NewRequest());
// Set ContentId to be 2 above ContentIds used from num_buffers_ and 1 above kFilledRectId.
kSquareRectId = {num_buffers_ + 2};
flatland_connection_ =
simple_present::FlatlandConnection::Create(context_.get(), "ScreenRecordingExample");
flatland_ = flatland_connection_->flatland();
parent_watcher_.set_error_handler([](zx_status_t status) {
FX_LOGS(ERROR) << "Error from fuchsia::ui::composition::ParentViewportWatcher: "
<< zx_status_get_string(status);
});
// Create ScreenCapture client.
context_->svc()->Connect(screen_capture_.NewRequest());
screen_capture_.set_error_handler(
[](zx_status_t status) { FX_LOGS(ERROR) << "Lost connection to ScreenCapture"; });
FX_DCHECK(screen_capture_);
auto view_identity = scenic::NewViewIdentityOnCreation();
flatland_->CreateView2(std::move(*args.mutable_view_creation_token()), std::move(view_identity),
/* protocols = */ {}, parent_watcher_.NewRequest());
parent_watcher_->GetLayout([this](auto layout_info) {
const auto [width, height] = layout_info.logical_size();
display_width_ = width;
display_height_ = height;
half_display_width_ = display_width_ / 2;
num_pixels_ = display_width_ * display_height_;
SetUpFlatland();
// Create buffer collection to render into for GetNextFrame() and to duplicate for
// creating images.
allocation::BufferCollectionImportExportTokens scr_ref_pair =
allocation::BufferCollectionImportExportTokens::New();
RegisterBufferCollectionUsages usage_types =
RegisterBufferCollectionUsages::DEFAULT | RegisterBufferCollectionUsages::SCREENSHOT;
fuchsia::sysmem2::BufferCollectionInfo sc_buffer_collection_info_ =
CreateBufferCollectionInfoWithConstraints(
utils::CreateDefaultConstraints(num_buffers_, half_display_width_, display_height_),
std::move(scr_ref_pair.export_token), flatland_allocator_.get(),
sysmem_allocator_.get(), usage_types);
fuchsia::ui::composition::ImageProperties image_properties = {};
image_properties.set_size({half_display_width_, display_height_});
// Initialize images with ContentId of their buffer index + 1.
for (uint32_t i = 0; i < num_buffers_; i++) {
fuchsia::ui::composition::BufferCollectionImportToken import_token_copy;
scr_ref_pair.import_token.value.duplicate(ZX_RIGHT_SAME_RIGHTS, &import_token_copy.value);
flatland_->CreateImage({i + 1}, std::move(import_token_copy), 0, std::move(image_properties));
flatland_->SetImageBlendingFunction({i + 1}, fuchsia::ui::composition::BlendMode::SRC);
}
ScreenCaptureConfig sc_args;
sc_args.set_import_token(std::move(scr_ref_pair.import_token));
sc_args.set_image_size({half_display_width_, display_height_});
screen_capture_->Configure(std::move(sc_args),
[this](fpromise::result<void, ScreenCaptureError> result) {
if (result.is_ok()) {
present_release_fences_.reserve(num_buffers_);
ScreenCaptureCallback();
}
});
PresentCallback();
});
flatland_connection_->Present({}, [](auto) {});
}
void ViewProviderImpl::PresentCallback() {
TRACE_DURATION("gfx", "Example::PresentCallback");
DrawSquare();
flatland_connection_->Present({}, [this](auto) { PresentCallback(); });
}
void ViewProviderImpl::ScreenCaptureCallback() {
TRACE_DURATION("gfx", "Example::ScreenCaptureCallback");
screen_capture_->GetNextFrame([this](fpromise::result<FrameInfo, ScreenCaptureError> result) {
if (!result.is_ok()) {
return;
}
FX_CHECK(result.value().has_buffer_index());
uint64_t buffer_index = result.value().buffer_index();
TRACE_DURATION("gfx", "GetNextFrameCallback", "buffer_index", buffer_index);
FX_CHECK(buffer_index <= num_buffers_);
flatland_->SetContent(kChildTransformId2, {buffer_index + 1});
// Set up event to drop buffer when frame is presented.
zx::event release_fence;
zx::event::create(0, &release_fence);
present_release_fences_[buffer_index] = utils::CopyEvent(release_fence);
std::vector<zx::event> current_release_fences;
current_release_fences.push_back(std::move(release_fence));
fuchsia::ui::composition::PresentArgs present_args;
present_args.set_release_fences(std::move(current_release_fences));
present_args.set_unsquashable(true);
auto wait = std::make_shared<async::WaitOnce>(present_release_fences_[buffer_index].get(),
ZX_EVENT_SIGNALED);
zx_status_t status = wait->Begin(
async_get_default_dispatcher(),
[copy_ref = wait, token = std::move(*result.value().mutable_buffer_release_token()),
buffer_index](async_dispatcher_t*, async::WaitOnce*, zx_status_t status,
const zx_packet_signal_t* signal) mutable {
TRACE_DURATION("gfx", "ScreenCapture Frame Released", "buffer_index", buffer_index);
FX_DCHECK(status == ZX_OK);
// Drop token.
return;
});
FX_DCHECK(status == ZX_OK);
flatland_connection_->Present(std::move(present_args), [](auto) {});
ScreenCaptureCallback();
});
}
void ViewProviderImpl::SetUpFlatland() {
flatland_->CreateTransform(kRootTransformId);
flatland_->CreateTransform(kChildTransformId1);
flatland_->CreateTransform(kChildTransformId2);
flatland_->CreateTransform(kBouncingSquareTransformId);
flatland_->SetTranslation(kChildTransformId1, {0, 0});
flatland_->SetTranslation(kChildTransformId2, {static_cast<int32_t>(half_display_width_), 0});
flatland_->SetTranslation(kBouncingSquareTransformId, {bs_.x, bs_.y});
// Set up children of room transform.
flatland_->SetRootTransform(kRootTransformId);
flatland_->AddChild(kRootTransformId, kChildTransformId1);
flatland_->AddChild(kRootTransformId, kChildTransformId2);
flatland_->AddChild(kChildTransformId1, kBouncingSquareTransformId);
// Set background of left section. Id is 1 above previously set for ScreenRecordingImages.
const fuchsia::ui::composition::ContentId kFilledRectId = {num_buffers_ + 1};
flatland_->CreateFilledRect(kFilledRectId);
flatland_->SetImageBlendingFunction(kFilledRectId, fuchsia::ui::composition::BlendMode::SRC);
flatland_->SetSolidFill(kFilledRectId, {0, 0, 0, 0}, {half_display_width_, display_height_});
// Draw the bouncing square initially.
flatland_->CreateFilledRect(kSquareRectId);
flatland_->SetImageBlendingFunction(kSquareRectId, fuchsia::ui::composition::BlendMode::SRC);
flatland_->SetSolidFill(kSquareRectId, {1, 1, 0, 1}, {bs_.size.width, bs_.size.height});
flatland_->SetContent(kChildTransformId1, kFilledRectId);
flatland_->SetContent(kBouncingSquareTransformId, kSquareRectId);
}
void ViewProviderImpl::DrawSquare() {
bs_.x += bs_.x_speed;
bs_.y += bs_.y_speed;
flatland_->SetTranslation(kBouncingSquareTransformId, {bs_.x, bs_.y});
CheckHit();
}
void ViewProviderImpl::CheckHit() {
if (bs_.x + bs_.size.width >= half_display_width_ || bs_.x <= 0) {
bs_.x_speed *= -1;
flatland_->SetSolidFill(kSquareRectId, RandomColor(), {bs_.size.width, bs_.size.height});
}
if (bs_.y + bs_.size.height >= display_height_ || bs_.y <= 0) {
bs_.y_speed *= -1;
flatland_->SetSolidFill(kSquareRectId, RandomColor(), {bs_.size.width, bs_.size.height});
}
}
fuchsia::ui::composition::ColorRgba ViewProviderImpl::RandomColor() {
float r = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
float g = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
float b = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
float a = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
return {r, g, b, a};
}
} // namespace screen_recording_example