blob: d5b88d323742215abfa143720bd970206b0e4c64 [file] [log] [blame]
// Copyright 2023 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 <fuchsia/sysmem/cpp/fidl.h>
#include <fuchsia/ui/composition/cpp/fidl.h>
#include <lib/sys/component/cpp/testing/realm_builder.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 <iostream>
#include <gtest/gtest.h>
#include "src/ui/scenic/lib/allocation/buffer_collection_import_export_tokens.h"
#include "src/ui/scenic/lib/utils/helpers.h"
#include "src/ui/scenic/tests/utils/blocking_present.h"
#include "src/ui/scenic/tests/utils/logging_event_loop.h"
#include "src/ui/scenic/tests/utils/scenic_realm_builder.h"
#include "src/ui/scenic/tests/utils/utils.h"
#include "src/ui/testing/util/screenshot_helper.h"
namespace integration_tests {
using fuchsia::ui::composition::ChildViewWatcher;
using fuchsia::ui::composition::ContentId;
using fuchsia::ui::composition::FlatlandPtr;
using fuchsia::ui::composition::ParentViewportWatcher;
using fuchsia::ui::composition::TransformId;
using ui_testing::Screenshot;
class NullRendererIntegrationTest : public LoggingEventLoop, public ::testing::Test {
public:
NullRendererIntegrationTest()
: realm_(ScenicRealmBuilder({.renderer_type_config = "null"})
.AddRealmProtocol(fuchsia::ui::composition::Flatland::Name_)
.AddRealmProtocol(fuchsia::ui::composition::FlatlandDisplay::Name_)
.AddRealmProtocol(fuchsia::ui::composition::Allocator::Name_)
.Build()) {
auto context = sys::ComponentContext::Create();
context->svc()->Connect(sysmem_allocator_.NewRequest());
flatland_display_ = realm_.component().Connect<fuchsia::ui::composition::FlatlandDisplay>();
flatland_display_.set_error_handler([](zx_status_t status) {
FAIL() << "Lost connection to Scenic: " << zx_status_get_string(status);
});
flatland_allocator_ = realm_.component().ConnectSync<fuchsia::ui::composition::Allocator>();
root_flatland_ = realm_.component().Connect<fuchsia::ui::composition::Flatland>();
root_flatland_.set_error_handler([](zx_status_t status) {
FAIL() << "Lost connection to Scenic: " << zx_status_get_string(status);
});
// Attach |root_flatland_| as the only Flatland under |flatland_display_|.
auto [child_token, parent_token] = scenic::ViewCreationTokenPair::New();
fidl::InterfacePtr<ChildViewWatcher> child_view_watcher;
flatland_display_->SetContent(std::move(parent_token), child_view_watcher.NewRequest());
fidl::InterfacePtr<ParentViewportWatcher> parent_viewport_watcher;
root_flatland_->CreateView2(std::move(child_token), scenic::NewViewIdentityOnCreation(), {},
parent_viewport_watcher.NewRequest());
// Get the display's width and height. Since there is no Present in FlatlandDisplay, receiving
// this callback ensures that all |flatland_display_| calls are processed.
std::optional<fuchsia::ui::composition::LayoutInfo> info;
parent_viewport_watcher->GetLayout([&info](auto result) { info = std::move(result); });
RunLoopUntil([&info] { return info.has_value(); });
display_width_ = info->logical_size().width;
display_height_ = info->logical_size().height;
screenshotter_ = realm_.component().ConnectSync<fuchsia::ui::composition::Screenshot>();
}
protected:
void SetConstraintsAndAllocateBuffer(fuchsia::sysmem2::BufferCollectionTokenSyncPtr token) {
fuchsia::sysmem2::BufferCollectionSyncPtr buffer_collection;
fuchsia::sysmem2::AllocatorBindSharedCollectionRequest bind_shared_request;
bind_shared_request.set_token(std::move(token));
bind_shared_request.set_buffer_collection_request(buffer_collection.NewRequest());
auto status = sysmem_allocator_->BindSharedCollection(std::move(bind_shared_request));
ASSERT_EQ(status, ZX_OK);
fuchsia::sysmem2::BufferCollectionSetConstraintsRequest set_constraints_request;
auto& constraints = *set_constraints_request.mutable_constraints();
constraints.mutable_usage()->set_none(fuchsia::sysmem2::NONE_USAGE);
constraints.set_min_buffer_count(1);
uint32_t constraints_min_buffer_count = constraints.min_buffer_count();
auto& image_constraints = constraints.mutable_image_format_constraints()->emplace_back();
image_constraints.set_pixel_format(fuchsia::images2::PixelFormat::B8G8R8A8);
image_constraints.mutable_color_spaces()->emplace_back(fuchsia::images2::ColorSpace::SRGB);
image_constraints.set_required_min_size(
fuchsia::math::SizeU{.width = display_width_, .height = display_height_});
image_constraints.set_required_max_size(
fuchsia::math::SizeU{.width = display_width_, .height = display_height_});
status = buffer_collection->SetConstraints(std::move(set_constraints_request));
ASSERT_EQ(status, ZX_OK);
fuchsia::sysmem2::BufferCollection_WaitForAllBuffersAllocated_Result wait_result;
status = buffer_collection->WaitForAllBuffersAllocated(&wait_result);
ASSERT_EQ(ZX_OK, status);
ASSERT_TRUE(!wait_result.is_framework_err());
ASSERT_TRUE(!wait_result.is_err());
ASSERT_TRUE(wait_result.is_response());
auto buffer_collection_info =
std::move(*wait_result.response().mutable_buffer_collection_info());
EXPECT_EQ(constraints_min_buffer_count, buffer_collection_info.buffers().size());
ASSERT_EQ(ZX_OK, buffer_collection->Release());
}
const TransformId kRootTransform{.value = 1};
uint32_t display_width_ = 0;
uint32_t display_height_ = 0;
fuchsia::sysmem2::AllocatorSyncPtr sysmem_allocator_;
fuchsia::ui::composition::AllocatorSyncPtr flatland_allocator_;
FlatlandPtr root_flatland_;
fuchsia::ui::composition::ScreenshotSyncPtr screenshotter_;
private:
component_testing::RealmRoot realm_;
fuchsia::ui::composition::FlatlandDisplayPtr flatland_display_;
};
TEST_F(NullRendererIntegrationTest, RendersContent) {
auto [local_token, scenic_token] = utils::CreateSysmemTokens(sysmem_allocator_.get());
// Send one token to Flatland Allocator.
allocation::BufferCollectionImportExportTokens bc_tokens =
allocation::BufferCollectionImportExportTokens::New();
fuchsia::ui::composition::RegisterBufferCollectionArgs rbc_args = {};
rbc_args.set_export_token(std::move(bc_tokens.export_token));
rbc_args.set_buffer_collection_token(
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>(
scenic_token.Unbind().TakeChannel()));
fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result result;
flatland_allocator_->RegisterBufferCollection(std::move(rbc_args), &result);
ASSERT_FALSE(result.is_err());
// Use the local token to allocate a protected buffer. NullRenderer sets constraint to complete
// the allocation.
SetConstraintsAndAllocateBuffer(std::move(local_token));
// Create the image in the Flatland instance.
fuchsia::ui::composition::ImageProperties image_properties = {};
image_properties.set_size({display_width_, display_height_});
const ContentId kImageContentId{.value = 1};
root_flatland_->CreateImage(kImageContentId, std::move(bc_tokens.import_token),
/*buffer_collection_index=*/0, std::move(image_properties));
BlockingPresent(this, root_flatland_);
// Present the created Image. Verify that render happened without any errors.
root_flatland_->CreateTransform(kRootTransform);
root_flatland_->SetRootTransform(kRootTransform);
root_flatland_->SetContent(kRootTransform, kImageContentId);
fuchsia::ui::composition::PresentArgs args;
args.set_release_fences(utils::CreateEventArray(1));
auto release_fence_copy = utils::CopyEvent(args.release_fences()[0]);
BlockingPresent(this, root_flatland_, std::move(args));
// Ensure that release fence for the previous frame is singalled after a Present.
root_flatland_->Clear();
BlockingPresent(this, root_flatland_);
EXPECT_TRUE(utils::IsEventSignalled(release_fence_copy, ZX_EVENT_SIGNALED));
}
TEST_F(NullRendererIntegrationTest, ScreenshotIsAllZeroes) {
auto [local_token, scenic_token] = utils::CreateSysmemTokens(sysmem_allocator_.get());
// Send one token to Flatland Allocator.
allocation::BufferCollectionImportExportTokens bc_tokens =
allocation::BufferCollectionImportExportTokens::New();
fuchsia::ui::composition::RegisterBufferCollectionArgs rbc_args = {};
rbc_args.set_export_token(std::move(bc_tokens.export_token));
rbc_args.set_buffer_collection_token(
fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>(
scenic_token.Unbind().TakeChannel()));
fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result result;
flatland_allocator_->RegisterBufferCollection(std::move(rbc_args), &result);
ASSERT_FALSE(result.is_err());
// Use the local token to allocate a protected buffer.
SetConstraintsAndAllocateBuffer(std::move(local_token));
// Create the image in the Flatland instance.
fuchsia::ui::composition::ImageProperties image_properties = {};
image_properties.set_size({display_width_, display_height_});
const ContentId kImageContentId{.value = 1};
root_flatland_->CreateImage(kImageContentId, std::move(bc_tokens.import_token),
/*buffer_collection_index=*/0, std::move(image_properties));
BlockingPresent(this, root_flatland_);
// Present the created Image.
root_flatland_->CreateTransform(kRootTransform);
root_flatland_->SetRootTransform(kRootTransform);
root_flatland_->SetContent(kRootTransform, kImageContentId);
BlockingPresent(this, root_flatland_);
// Verify that screenshot works and is all zeroes.
auto screenshot = TakeScreenshot(screenshotter_, display_width_, display_height_);
EXPECT_EQ(screenshot.Histogram()[utils::Pixel(0, 0, 0, 0)],
screenshot.width() * screenshot.height());
}
} // namespace integration_tests