| // Copyright 2019 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 <fcntl.h> |
| #include <fuchsia/hardware/goldfish/llcpp/fidl.h> |
| #include <fuchsia/sysmem/llcpp/fidl.h> |
| #include <lib/fdio/directory.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/time.h> |
| #include <lib/zx/vmar.h> |
| #include <lib/zx/vmo.h> |
| #include <unistd.h> |
| #include <zircon/syscalls.h> |
| #include <zircon/types.h> |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/lib/fsl/handles/object_info.h" |
| |
| namespace { |
| llcpp::fuchsia::sysmem::Allocator::SyncClient CreateSysmemAllocator() { |
| zx::channel allocator_client; |
| zx::channel allocator_server; |
| EXPECT_EQ(zx::channel::create(0, &allocator_client, &allocator_server), ZX_OK); |
| EXPECT_EQ(fdio_service_connect("/svc/fuchsia.sysmem.Allocator", allocator_server.release()), |
| ZX_OK); |
| |
| llcpp::fuchsia::sysmem::Allocator::SyncClient allocator(std::move(allocator_client)); |
| |
| allocator.SetDebugClientInfo(fidl::unowned_str(fsl::GetCurrentProcessName()), |
| fsl::GetCurrentProcessKoid()); |
| return allocator; |
| } |
| |
| void SetDefaultCollectionName(llcpp::fuchsia::sysmem::BufferCollection::SyncClient& collection) { |
| constexpr uint32_t kTestNamePriority = 1000u; |
| std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name(); |
| EXPECT_TRUE(collection.SetName(kTestNamePriority, fidl::unowned_str(test_name)).ok()); |
| } |
| } // namespace |
| |
| TEST(GoldfishPipeTests, GoldfishPipeTest) { |
| int fd = open("/dev/class/goldfish-pipe/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK); |
| |
| zx::channel pipe_client; |
| zx::channel pipe_server; |
| EXPECT_EQ(zx::channel::create(0, &pipe_client, &pipe_server), ZX_OK); |
| |
| llcpp::fuchsia::hardware::goldfish::PipeDevice::SyncClient pipe_device(std::move(channel)); |
| EXPECT_TRUE(pipe_device.OpenPipe(std::move(pipe_server)).ok()); |
| |
| llcpp::fuchsia::hardware::goldfish::Pipe::SyncClient pipe(std::move(pipe_client)); |
| const size_t kSize = 3 * 4096; |
| { |
| auto result = pipe.SetBufferSize(kSize); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| } |
| |
| zx::vmo vmo; |
| { |
| auto result = pipe.GetBuffer(); |
| ASSERT_TRUE(result.ok()); |
| vmo = std::move(result.Unwrap()->vmo); |
| } |
| |
| // Connect to pingpong service. |
| constexpr char kPipeName[] = "pipe:pingpong"; |
| size_t bytes = strlen(kPipeName) + 1; |
| EXPECT_EQ(vmo.write(kPipeName, 0, bytes), ZX_OK); |
| |
| { |
| auto result = pipe.Write(bytes, 0); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_EQ(result.Unwrap()->actual, bytes); |
| } |
| |
| // Write 1 byte. |
| const uint8_t kSentinel = 0xaa; |
| EXPECT_EQ(vmo.write(&kSentinel, 0, 1), ZX_OK); |
| { |
| auto result = pipe.Write(1, 0); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_EQ(result.Unwrap()->actual, 1U); |
| } |
| |
| // Read 1 byte result. |
| { |
| auto result = pipe.Read(1, 0); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_EQ(result.Unwrap()->actual, 1U); |
| } |
| |
| uint8_t result = 0; |
| EXPECT_EQ(vmo.read(&result, 0, 1), ZX_OK); |
| // pingpong service should have returned the data received. |
| EXPECT_EQ(result, kSentinel); |
| |
| // Write 3 * 4096 bytes. |
| uint8_t send_buffer[kSize]; |
| memset(send_buffer, kSentinel, kSize); |
| EXPECT_EQ(vmo.write(send_buffer, 0, kSize), ZX_OK); |
| { |
| auto result = pipe.Write(kSize, 0); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_EQ(result.Unwrap()->actual, kSize); |
| } |
| |
| // Read 3 * 4096 bytes. |
| { |
| auto result = pipe.Read(kSize, 0); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_EQ(result.Unwrap()->actual, kSize); |
| } |
| uint8_t recv_buffer[kSize]; |
| EXPECT_EQ(vmo.read(recv_buffer, 0, kSize), ZX_OK); |
| |
| // pingpong service should have returned the data received. |
| EXPECT_EQ(memcmp(send_buffer, recv_buffer, kSize), 0); |
| |
| // Write & Read 4096 bytes. |
| const size_t kSmallSize = kSize / 3; |
| const size_t kRecvOffset = kSmallSize; |
| memset(send_buffer, kSentinel, kSmallSize); |
| EXPECT_EQ(vmo.write(send_buffer, 0, kSmallSize), ZX_OK); |
| |
| { |
| auto result = pipe.DoCall(kSmallSize, 0u, kSmallSize, kRecvOffset); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_EQ(result.Unwrap()->actual, 2 * kSmallSize); |
| } |
| |
| EXPECT_EQ(vmo.read(recv_buffer, kRecvOffset, kSmallSize), ZX_OK); |
| |
| // pingpong service should have returned the data received. |
| EXPECT_EQ(memcmp(send_buffer, recv_buffer, kSmallSize), 0); |
| } |
| |
| TEST(GoldfishControlTests, GoldfishControlTest) { |
| int fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK); |
| |
| auto allocator = CreateSysmemAllocator(); |
| EXPECT_TRUE(allocator.channel()); |
| |
| zx::channel token_client; |
| zx::channel token_server; |
| EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK); |
| EXPECT_TRUE(allocator.AllocateSharedCollection(std::move(token_server)).ok()); |
| |
| zx::channel collection_client; |
| zx::channel collection_server; |
| EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK); |
| EXPECT_TRUE( |
| allocator.BindSharedCollection(std::move(token_client), std::move(collection_server)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints; |
| constraints.usage.vulkan = llcpp::fuchsia::sysmem::VULKAN_IMAGE_USAGE_TRANSFER_DST; |
| constraints.min_buffer_count_for_camping = 1; |
| constraints.has_buffer_memory_constraints = true; |
| constraints.buffer_memory_constraints = llcpp::fuchsia::sysmem::BufferMemoryConstraints{ |
| .min_size_bytes = 4 * 1024, |
| .max_size_bytes = 4 * 1024, |
| .physically_contiguous_required = false, |
| .secure_required = false, |
| .ram_domain_supported = false, |
| .cpu_domain_supported = false, |
| .inaccessible_domain_supported = true, |
| .heap_permitted_count = 1, |
| .heap_permitted = {llcpp::fuchsia::sysmem::HeapType::GOLDFISH_DEVICE_LOCAL}}; |
| |
| llcpp::fuchsia::sysmem::BufferCollection::SyncClient collection(std::move(collection_client)); |
| |
| SetDefaultCollectionName(collection); |
| EXPECT_TRUE(collection.SetConstraints(true, std::move(constraints)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info; |
| { |
| auto result = collection.WaitForBuffersAllocated(); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->status, ZX_OK); |
| |
| info = std::move(result.Unwrap()->buffer_collection_info); |
| EXPECT_EQ(info.buffer_count, 1U); |
| EXPECT_TRUE(info.buffers[0].vmo.is_valid()); |
| } |
| |
| zx::vmo vmo = std::move(info.buffers[0].vmo); |
| EXPECT_TRUE(vmo.is_valid()); |
| |
| EXPECT_TRUE(collection.Close().ok()); |
| |
| zx::vmo vmo_copy; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| |
| llcpp::fuchsia::hardware::goldfish::ControlDevice::SyncClient control(std::move(channel)); |
| { |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| .set_width(std::make_unique<uint32_t>(64)) |
| .set_height(std::make_unique<uint32_t>(64)) |
| .set_format(std::make_unique<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>( |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA)) |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy), std::move(create_params)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| } |
| |
| zx::vmo vmo_copy2; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK); |
| |
| { |
| auto result = control.GetBufferHandle(std::move(vmo_copy2)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_NE(result.Unwrap()->id, 0u); |
| EXPECT_EQ(result.Unwrap()->type, |
| llcpp::fuchsia::hardware::goldfish::BufferHandleType::COLOR_BUFFER); |
| } |
| |
| zx::vmo vmo_copy3; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy3), ZX_OK); |
| |
| { |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| .set_width(std::make_unique<uint32_t>(64)) |
| .set_height(std::make_unique<uint32_t>(64)) |
| .set_format(std::make_unique<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>( |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA)) |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy3), std::move(create_params)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_ALREADY_EXISTS); |
| } |
| } |
| |
| TEST(GoldfishControlTests, GoldfishControlTest_HostVisible) { |
| int fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK); |
| |
| auto allocator = CreateSysmemAllocator(); |
| EXPECT_TRUE(allocator.channel()); |
| |
| zx::channel token_client; |
| zx::channel token_server; |
| EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK); |
| EXPECT_TRUE(allocator.AllocateSharedCollection(std::move(token_server)).ok()); |
| |
| zx::channel collection_client; |
| zx::channel collection_server; |
| EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK); |
| EXPECT_TRUE( |
| allocator.BindSharedCollection(std::move(token_client), std::move(collection_server)).ok()); |
| |
| const size_t kMinSizeBytes = 4 * 1024; |
| const size_t kMaxSizeBytes = 4 * 4096; |
| llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints; |
| constraints.usage.vulkan = llcpp::fuchsia::sysmem::VULKAN_IMAGE_USAGE_TRANSFER_DST; |
| constraints.min_buffer_count_for_camping = 1; |
| constraints.has_buffer_memory_constraints = true; |
| constraints.buffer_memory_constraints = llcpp::fuchsia::sysmem::BufferMemoryConstraints{ |
| .min_size_bytes = kMinSizeBytes, |
| .max_size_bytes = kMaxSizeBytes, |
| .physically_contiguous_required = false, |
| .secure_required = false, |
| .ram_domain_supported = false, |
| .cpu_domain_supported = true, |
| .inaccessible_domain_supported = false, |
| .heap_permitted_count = 1, |
| .heap_permitted = {llcpp::fuchsia::sysmem::HeapType::GOLDFISH_HOST_VISIBLE}}; |
| constraints.image_format_constraints_count = 1; |
| constraints.image_format_constraints[0] = llcpp::fuchsia::sysmem::ImageFormatConstraints{ |
| .pixel_format = |
| llcpp::fuchsia::sysmem::PixelFormat{ |
| .type = llcpp::fuchsia::sysmem::PixelFormatType::BGRA32, |
| .has_format_modifier = false, |
| .format_modifier = {}, |
| }, |
| .color_spaces_count = 1, |
| .color_space = |
| { |
| llcpp::fuchsia::sysmem::ColorSpace{.type = |
| llcpp::fuchsia::sysmem::ColorSpaceType::SRGB}, |
| }, |
| .min_coded_width = 32, |
| .min_coded_height = 32, |
| }; |
| |
| llcpp::fuchsia::sysmem::BufferCollection::SyncClient collection(std::move(collection_client)); |
| SetDefaultCollectionName(collection); |
| EXPECT_TRUE(collection.SetConstraints(true, std::move(constraints)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info; |
| { |
| auto result = collection.WaitForBuffersAllocated(); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->status, ZX_OK); |
| |
| info = std::move(result.Unwrap()->buffer_collection_info); |
| EXPECT_EQ(info.buffer_count, 1U); |
| EXPECT_TRUE(info.buffers[0].vmo.is_valid()); |
| EXPECT_EQ(info.settings.buffer_settings.coherency_domain, |
| llcpp::fuchsia::sysmem::CoherencyDomain::CPU); |
| } |
| |
| zx::vmo vmo = std::move(info.buffers[0].vmo); |
| EXPECT_TRUE(vmo.is_valid()); |
| |
| uint64_t vmo_size; |
| EXPECT_EQ(vmo.get_size(&vmo_size), ZX_OK); |
| EXPECT_GE(vmo_size, kMinSizeBytes); |
| EXPECT_LE(vmo_size, kMaxSizeBytes); |
| |
| // Test if the vmo is mappable. |
| zx_vaddr_t addr; |
| EXPECT_EQ(zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, /*vmar_offset*/ 0, vmo, |
| /*vmo_offset*/ 0, vmo_size, &addr), |
| ZX_OK); |
| |
| // Test if write and read works correctly. |
| uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); |
| std::vector<uint8_t> copy_target(vmo_size, 0u); |
| for (uint32_t trial = 0; trial < 10u; trial++) { |
| memset(ptr, trial, vmo_size); |
| memcpy(copy_target.data(), ptr, vmo_size); |
| zx_cache_flush(ptr, vmo_size, ZX_CACHE_FLUSH_DATA | ZX_CACHE_FLUSH_INVALIDATE); |
| EXPECT_EQ(memcmp(copy_target.data(), ptr, vmo_size), 0); |
| } |
| |
| EXPECT_EQ(zx::vmar::root_self()->unmap(addr, PAGE_SIZE), ZX_OK); |
| |
| EXPECT_TRUE(collection.Close().ok()); |
| } |
| |
| TEST(GoldfishControlTests, GoldfishControlTest_HostVisible_MultiClients) { |
| using llcpp::fuchsia::sysmem::BufferCollection; |
| using llcpp::fuchsia::sysmem::BufferCollectionConstraints; |
| |
| int fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK); |
| |
| auto allocator = CreateSysmemAllocator(); |
| EXPECT_TRUE(allocator.channel()); |
| |
| constexpr size_t kNumClients = 2; |
| zx::channel token_client[kNumClients]; |
| zx::channel token_server[kNumClients]; |
| zx::channel collection_client[kNumClients]; |
| zx::channel collection_server[kNumClients]; |
| |
| EXPECT_EQ(zx::channel::create(0, &token_client[0], &token_server[0]), ZX_OK); |
| EXPECT_EQ(zx::channel::create(0, &token_client[1], &token_server[1]), ZX_OK); |
| EXPECT_TRUE(allocator.AllocateSharedCollection(std::move(token_server[0])).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionToken::Call::Duplicate(token_client[0].borrow(), 0, |
| std::move(token_server[1])); |
| llcpp::fuchsia::sysmem::BufferCollectionToken::Call::Sync(token_client[0].borrow()); |
| |
| for (size_t i = 0; i < kNumClients; i++) { |
| EXPECT_EQ(zx::channel::create(0, &collection_client[i], &collection_server[i]), ZX_OK); |
| EXPECT_TRUE( |
| allocator.BindSharedCollection(std::move(token_client[i]), std::move(collection_server[i])) |
| .ok()); |
| } |
| |
| const size_t kMinSizeBytes = 4 * 1024; |
| const size_t kMaxSizeBytes = 4 * 1024 * 512; |
| const size_t kTargetSizeBytes = 4 * 1024 * 512; |
| BufferCollectionConstraints constraints[kNumClients]; |
| for (size_t i = 0; i < kNumClients; i++) { |
| constraints[i].usage.vulkan = llcpp::fuchsia::sysmem::VULKAN_IMAGE_USAGE_TRANSFER_DST; |
| constraints[i].min_buffer_count = 1; |
| constraints[i].has_buffer_memory_constraints = true; |
| constraints[i].buffer_memory_constraints = llcpp::fuchsia::sysmem::BufferMemoryConstraints{ |
| .min_size_bytes = kMinSizeBytes, |
| .max_size_bytes = kMaxSizeBytes, |
| .physically_contiguous_required = false, |
| .secure_required = false, |
| .ram_domain_supported = false, |
| .cpu_domain_supported = true, |
| .inaccessible_domain_supported = false, |
| .heap_permitted_count = 1, |
| .heap_permitted = {llcpp::fuchsia::sysmem::HeapType::GOLDFISH_HOST_VISIBLE}}; |
| constraints[i].image_format_constraints_count = 1; |
| constraints[i].image_format_constraints[0] = llcpp::fuchsia::sysmem::ImageFormatConstraints{ |
| .pixel_format = |
| llcpp::fuchsia::sysmem::PixelFormat{ |
| .type = llcpp::fuchsia::sysmem::PixelFormatType::BGRA32, |
| .has_format_modifier = false, |
| .format_modifier = {}, |
| }, |
| .color_spaces_count = 1, |
| .color_space = |
| { |
| llcpp::fuchsia::sysmem::ColorSpace{ |
| .type = llcpp::fuchsia::sysmem::ColorSpaceType::SRGB}, |
| }, |
| }; |
| } |
| |
| // Set different min_coded_width and required_max_coded_width for each client. |
| constraints[0].image_format_constraints[0].min_coded_width = 32; |
| constraints[0].image_format_constraints[0].min_coded_height = 64; |
| constraints[1].image_format_constraints[0].min_coded_width = 16; |
| constraints[1].image_format_constraints[0].min_coded_height = 512; |
| constraints[1].image_format_constraints[0].required_max_coded_width = 1024; |
| constraints[1].image_format_constraints[0].required_max_coded_height = 256; |
| |
| BufferCollection::SyncClient collection[kNumClients]; |
| for (size_t i = 0; i < kNumClients; i++) { |
| collection[i] = BufferCollection::SyncClient(std::move(collection_client[i])), |
| SetDefaultCollectionName(collection[i]); |
| EXPECT_TRUE(collection[i].SetConstraints(true, std::move(constraints[i])).ok()); |
| }; |
| |
| llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info; |
| { |
| auto result = collection[0].WaitForBuffersAllocated(); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->status, ZX_OK); |
| |
| info = std::move(result.Unwrap()->buffer_collection_info); |
| EXPECT_EQ(info.buffer_count, 1u); |
| EXPECT_TRUE(info.buffers[0].vmo.is_valid()); |
| EXPECT_EQ(info.settings.buffer_settings.coherency_domain, |
| llcpp::fuchsia::sysmem::CoherencyDomain::CPU); |
| |
| const auto& image_format_constraints = |
| result.value().buffer_collection_info.settings.image_format_constraints; |
| |
| EXPECT_EQ(image_format_constraints.min_coded_width, 32u); |
| EXPECT_EQ(image_format_constraints.min_coded_height, 512u); |
| EXPECT_EQ(image_format_constraints.required_max_coded_width, 1024u); |
| EXPECT_EQ(image_format_constraints.required_max_coded_height, 256u); |
| |
| // Expected coded_width = max(min_coded_width, requied_max_coded_width); |
| // Expected coded_height = max(min_coded_height, requied_max_coded_height). |
| // Thus target size should be 1024 x 512 x 4. |
| EXPECT_GE(info.settings.buffer_settings.size_bytes, kTargetSizeBytes); |
| } |
| |
| zx::vmo vmo = std::move(info.buffers[0].vmo); |
| EXPECT_TRUE(vmo.is_valid()); |
| |
| uint64_t vmo_size; |
| EXPECT_EQ(vmo.get_size(&vmo_size), ZX_OK); |
| EXPECT_GE(vmo_size, kTargetSizeBytes); |
| EXPECT_LE(vmo_size, kMaxSizeBytes); |
| |
| // Test if the vmo is mappable. |
| zx_vaddr_t addr; |
| EXPECT_EQ(zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, /*vmar_offset*/ 0, vmo, |
| /*vmo_offset*/ 0, vmo_size, &addr), |
| ZX_OK); |
| |
| // Test if write and read works correctly. |
| uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); |
| std::vector<uint8_t> copy_target(vmo_size, 0u); |
| for (uint32_t trial = 0; trial < 10u; trial++) { |
| memset(ptr, trial, vmo_size); |
| memcpy(copy_target.data(), ptr, vmo_size); |
| zx_cache_flush(ptr, vmo_size, ZX_CACHE_FLUSH_DATA | ZX_CACHE_FLUSH_INVALIDATE); |
| EXPECT_EQ(memcmp(copy_target.data(), ptr, vmo_size), 0); |
| } |
| |
| EXPECT_EQ(zx::vmar::root_self()->unmap(addr, PAGE_SIZE), ZX_OK); |
| |
| for (size_t i = 0; i < kNumClients; i++) { |
| EXPECT_TRUE(collection[i].Close().ok()); |
| } |
| } |
| |
| TEST(GoldfishControlTests, GoldfishControlTest_HostVisibleBuffer) { |
| int fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK); |
| |
| auto allocator = CreateSysmemAllocator(); |
| EXPECT_TRUE(allocator.channel()); |
| |
| zx::channel token_client; |
| zx::channel token_server; |
| EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK); |
| EXPECT_TRUE(allocator.AllocateSharedCollection(std::move(token_server)).ok()); |
| |
| zx::channel collection_client; |
| zx::channel collection_server; |
| EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK); |
| EXPECT_TRUE( |
| allocator.BindSharedCollection(std::move(token_client), std::move(collection_server)).ok()); |
| |
| const size_t kMinSizeBytes = 4 * 1024; |
| const size_t kMaxSizeBytes = 4 * 4096; |
| llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints; |
| constraints.usage.vulkan = llcpp::fuchsia::sysmem::vulkanUsageTransferDst; |
| constraints.min_buffer_count_for_camping = 1; |
| constraints.has_buffer_memory_constraints = true; |
| constraints.buffer_memory_constraints = llcpp::fuchsia::sysmem::BufferMemoryConstraints{ |
| .min_size_bytes = kMinSizeBytes, |
| .max_size_bytes = kMaxSizeBytes, |
| .physically_contiguous_required = false, |
| .secure_required = false, |
| .ram_domain_supported = false, |
| .cpu_domain_supported = true, |
| .inaccessible_domain_supported = false, |
| .heap_permitted_count = 1, |
| .heap_permitted = {llcpp::fuchsia::sysmem::HeapType::GOLDFISH_HOST_VISIBLE}}; |
| constraints.image_format_constraints_count = 0; |
| |
| llcpp::fuchsia::sysmem::BufferCollection::SyncClient collection(std::move(collection_client)); |
| SetDefaultCollectionName(collection); |
| EXPECT_TRUE(collection.SetConstraints(true, std::move(constraints)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info; |
| { |
| auto result = collection.WaitForBuffersAllocated(); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->status, ZX_OK); |
| |
| info = std::move(result.Unwrap()->buffer_collection_info); |
| EXPECT_EQ(info.buffer_count, 1U); |
| EXPECT_TRUE(info.buffers[0].vmo.is_valid()); |
| EXPECT_EQ(info.settings.buffer_settings.coherency_domain, |
| llcpp::fuchsia::sysmem::CoherencyDomain::CPU); |
| } |
| |
| zx::vmo vmo = std::move(info.buffers[0].vmo); |
| EXPECT_TRUE(vmo.is_valid()); |
| |
| uint64_t vmo_size; |
| EXPECT_EQ(vmo.get_size(&vmo_size), ZX_OK); |
| EXPECT_GE(vmo_size, kMinSizeBytes); |
| EXPECT_LE(vmo_size, kMaxSizeBytes); |
| |
| // Test if the vmo is mappable. |
| zx_vaddr_t addr; |
| EXPECT_EQ(zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, /*vmar_offset*/ 0, vmo, |
| /*vmo_offset*/ 0, vmo_size, &addr), |
| ZX_OK); |
| |
| // Test if write and read works correctly. |
| uint8_t* ptr = reinterpret_cast<uint8_t*>(addr); |
| std::vector<uint8_t> copy_target(vmo_size, 0u); |
| for (uint32_t trial = 0; trial < 10u; trial++) { |
| memset(ptr, trial, vmo_size); |
| memcpy(copy_target.data(), ptr, vmo_size); |
| zx_cache_flush(ptr, vmo_size, ZX_CACHE_FLUSH_DATA | ZX_CACHE_FLUSH_INVALIDATE); |
| EXPECT_EQ(memcmp(copy_target.data(), ptr, vmo_size), 0); |
| } |
| |
| EXPECT_EQ(zx::vmar::root_self()->unmap(addr, PAGE_SIZE), ZX_OK); |
| |
| EXPECT_TRUE(collection.Close().ok()); |
| } |
| |
| TEST(GoldfishControlTests, GoldfishControlTest_DataBuffer) { |
| int fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK); |
| |
| auto allocator = CreateSysmemAllocator(); |
| EXPECT_TRUE(allocator.channel()); |
| |
| zx::channel token_client; |
| zx::channel token_server; |
| EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK); |
| EXPECT_TRUE(allocator.AllocateSharedCollection(std::move(token_server)).ok()); |
| |
| zx::channel collection_client; |
| zx::channel collection_server; |
| EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK); |
| EXPECT_TRUE( |
| allocator.BindSharedCollection(std::move(token_client), std::move(collection_server)).ok()); |
| |
| constexpr size_t kBufferSizeBytes = 4 * 1024; |
| llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints; |
| constraints.usage.vulkan = llcpp::fuchsia::sysmem::VULKAN_BUFFER_USAGE_TRANSFER_DST; |
| constraints.min_buffer_count_for_camping = 1; |
| constraints.has_buffer_memory_constraints = true; |
| constraints.buffer_memory_constraints = llcpp::fuchsia::sysmem::BufferMemoryConstraints{ |
| .min_size_bytes = kBufferSizeBytes, |
| .max_size_bytes = kBufferSizeBytes, |
| .physically_contiguous_required = false, |
| .secure_required = false, |
| .ram_domain_supported = false, |
| .cpu_domain_supported = false, |
| .inaccessible_domain_supported = true, |
| .heap_permitted_count = 1, |
| .heap_permitted = {llcpp::fuchsia::sysmem::HeapType::GOLDFISH_DEVICE_LOCAL}}; |
| |
| llcpp::fuchsia::sysmem::BufferCollection::SyncClient collection(std::move(collection_client)); |
| SetDefaultCollectionName(collection); |
| EXPECT_TRUE(collection.SetConstraints(true, std::move(constraints)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info; |
| { |
| auto result = collection.WaitForBuffersAllocated(); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->status, ZX_OK); |
| |
| info = std::move(result.Unwrap()->buffer_collection_info); |
| EXPECT_EQ(info.buffer_count, 1u); |
| EXPECT_TRUE(info.buffers[0].vmo.is_valid()); |
| } |
| |
| zx::vmo vmo = std::move(info.buffers[0].vmo); |
| EXPECT_TRUE(vmo.is_valid()); |
| |
| EXPECT_TRUE(collection.Close().ok()); |
| |
| zx::vmo vmo_copy; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| |
| llcpp::fuchsia::hardware::goldfish::ControlDevice::SyncClient control(std::move(channel)); |
| { |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateBuffer2Params::Frame>()) |
| .set_size(std::make_unique<uint64_t>(kBufferSizeBytes)) |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| |
| auto result = control.CreateBuffer2(std::move(vmo_copy), std::move(create_params)); |
| ASSERT_TRUE(result.ok()); |
| ASSERT_TRUE(result.value().result.is_response()); |
| } |
| |
| zx::vmo vmo_copy2; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK); |
| |
| { |
| auto result = control.GetBufferHandle(std::move(vmo_copy2)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_NE(result.Unwrap()->id, 0u); |
| EXPECT_EQ(result.Unwrap()->type, llcpp::fuchsia::hardware::goldfish::BufferHandleType::BUFFER); |
| } |
| |
| zx::vmo vmo_copy3; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy3), ZX_OK); |
| |
| { |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| .set_width(std::make_unique<uint32_t>(64)) |
| .set_height(std::make_unique<uint32_t>(64)) |
| .set_format(std::make_unique<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>( |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA)) |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy3), std::move(create_params)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_ALREADY_EXISTS); |
| } |
| |
| zx::vmo vmo_copy4; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy4), ZX_OK); |
| |
| { |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateBuffer2Params::Frame>()) |
| .set_size(std::make_unique<uint64_t>(kBufferSizeBytes)) |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| |
| auto result = control.CreateBuffer2(std::move(vmo_copy4), std::move(create_params)); |
| ASSERT_TRUE(result.ok()); |
| ASSERT_TRUE(result.value().result.is_err()); |
| EXPECT_EQ(result.value().result.err(), ZX_ERR_ALREADY_EXISTS); |
| } |
| } |
| |
| // In this test case we call CreateColorBuffer() and GetBufferHandle() |
| // on VMOs not registered with goldfish sysmem heap. |
| // |
| // The IPC transmission should succeed but FIDL interface should |
| // return ZX_ERR_INVALID_ARGS. |
| TEST(GoldfishControlTests, GoldfishControlTest_InvalidVmo) { |
| int fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK); |
| |
| zx::vmo non_sysmem_vmo; |
| EXPECT_EQ(zx::vmo::create(1024u, 0u, &non_sysmem_vmo), ZX_OK); |
| |
| // Call CreateColorBuffer() using vmo not registered with goldfish |
| // sysmem heap. |
| zx::vmo vmo_copy; |
| EXPECT_EQ(non_sysmem_vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| |
| llcpp::fuchsia::hardware::goldfish::ControlDevice::SyncClient control(std::move(channel)); |
| { |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| .set_width(std::make_unique<uint32_t>(16)) |
| .set_height(std::make_unique<uint32_t>(16)) |
| .set_format(std::make_unique<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>( |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA)) |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy), std::move(create_params)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_INVALID_ARGS); |
| } |
| |
| // Call GetBufferHandle() using vmo not registered with goldfish |
| // sysmem heap. |
| zx::vmo vmo_copy2; |
| EXPECT_EQ(non_sysmem_vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK); |
| |
| { |
| auto result = control.GetBufferHandle(std::move(vmo_copy2)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_INVALID_ARGS); |
| } |
| } |
| |
| // In this test case we test arguments of CreateColorBuffer2() method. |
| // If a mandatory field is missing, it should return "ZX_ERR_INVALID_ARGS". |
| TEST(GoldfishControlTests, GoldfishControlTest_CreateColorBuffer2Args) { |
| // Setup control device. |
| int control_device_fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(control_device_fd, 0); |
| |
| zx::channel control_channel; |
| EXPECT_EQ(fdio_get_service_handle(control_device_fd, control_channel.reset_and_get_address()), |
| ZX_OK); |
| |
| // ----------------------------------------------------------------------// |
| // Setup sysmem allocator and buffer collection. |
| auto allocator = CreateSysmemAllocator(); |
| EXPECT_TRUE(allocator.channel()); |
| |
| zx::channel token_client; |
| zx::channel token_server; |
| EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK); |
| EXPECT_TRUE(allocator.AllocateSharedCollection(std::move(token_server)).ok()); |
| |
| zx::channel collection_client; |
| zx::channel collection_server; |
| EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK); |
| EXPECT_TRUE( |
| allocator.BindSharedCollection(std::move(token_client), std::move(collection_server)).ok()); |
| |
| // ----------------------------------------------------------------------// |
| // Use device local heap which only *registers* the koid of vmo to control |
| // device. |
| llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints; |
| constraints.usage.vulkan = llcpp::fuchsia::sysmem::VULKAN_IMAGE_USAGE_TRANSFER_DST; |
| constraints.min_buffer_count_for_camping = 1; |
| constraints.has_buffer_memory_constraints = true; |
| constraints.buffer_memory_constraints = llcpp::fuchsia::sysmem::BufferMemoryConstraints{ |
| .min_size_bytes = 4 * 1024, |
| .max_size_bytes = 4 * 1024, |
| .physically_contiguous_required = false, |
| .secure_required = false, |
| .ram_domain_supported = false, |
| .cpu_domain_supported = false, |
| .inaccessible_domain_supported = true, |
| .heap_permitted_count = 1, |
| .heap_permitted = {llcpp::fuchsia::sysmem::HeapType::GOLDFISH_DEVICE_LOCAL}}; |
| |
| llcpp::fuchsia::sysmem::BufferCollection::SyncClient collection(std::move(collection_client)); |
| SetDefaultCollectionName(collection); |
| EXPECT_TRUE(collection.SetConstraints(true, std::move(constraints)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info; |
| { |
| auto result = collection.WaitForBuffersAllocated(); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->status, ZX_OK); |
| |
| info = std::move(result.Unwrap()->buffer_collection_info); |
| EXPECT_EQ(info.buffer_count, 1u); |
| EXPECT_TRUE(info.buffers[0].vmo.is_valid()); |
| } |
| |
| zx::vmo vmo = std::move(info.buffers[0].vmo); |
| EXPECT_TRUE(vmo.is_valid()); |
| |
| EXPECT_TRUE(collection.Close().ok()); |
| |
| // ----------------------------------------------------------------------// |
| // Try creating color buffer. |
| zx::vmo vmo_copy; |
| llcpp::fuchsia::hardware::goldfish::ControlDevice::SyncClient control(std::move(control_channel)); |
| |
| { |
| // Verify that a CreateColorBuffer2() call without width will fail. |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| // Without width |
| .set_height(std::make_unique<uint32_t>(64)) |
| .set_format(std::make_unique<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>( |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA)) |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy), std::move(create_params)); |
| |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_INVALID_ARGS); |
| EXPECT_LT(result.Unwrap()->hw_address_page_offset, 0); |
| } |
| |
| { |
| // Verify that a CreateColorBuffer2() call without height will fail. |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| .set_width(std::make_unique<uint32_t>(64)) |
| // Without height |
| .set_format(std::make_unique<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>( |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA)) |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy), std::move(create_params)); |
| |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_INVALID_ARGS); |
| EXPECT_LT(result.Unwrap()->hw_address_page_offset, 0); |
| } |
| |
| { |
| // Verify that a CreateColorBuffer2() call without color format will fail. |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| .set_width(std::make_unique<uint32_t>(64)) |
| .set_height(std::make_unique<uint32_t>(64)) |
| // Without format |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy), std::move(create_params)); |
| |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_INVALID_ARGS); |
| EXPECT_LT(result.Unwrap()->hw_address_page_offset, 0); |
| } |
| |
| { |
| // Verify that a CreateColorBuffer2() call without memory property will fail. |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| .set_width(std::make_unique<uint32_t>(64)) |
| .set_height(std::make_unique<uint32_t>(64)) |
| .set_format(std::make_unique<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>( |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA)) |
| // Without memory property |
| .build(); |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy), std::move(create_params)); |
| |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_INVALID_ARGS); |
| EXPECT_LT(result.Unwrap()->hw_address_page_offset, 0); |
| } |
| } |
| |
| // In this test case we test arguments of CreateBuffer2() method. |
| // If a mandatory field is missing, it should return "ZX_ERR_INVALID_ARGS". |
| TEST(GoldfishControlTests, GoldfishControlTest_CreateBuffer2Args) { |
| // Setup control device. |
| int control_device_fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(control_device_fd, 0); |
| |
| zx::channel control_channel; |
| EXPECT_EQ(fdio_get_service_handle(control_device_fd, control_channel.reset_and_get_address()), |
| ZX_OK); |
| |
| // ----------------------------------------------------------------------// |
| // Setup sysmem allocator and buffer collection. |
| auto allocator = CreateSysmemAllocator(); |
| EXPECT_TRUE(allocator.channel()); |
| |
| zx::channel token_client; |
| zx::channel token_server; |
| EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK); |
| EXPECT_TRUE(allocator.AllocateSharedCollection(std::move(token_server)).ok()); |
| |
| zx::channel collection_client; |
| zx::channel collection_server; |
| EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK); |
| EXPECT_TRUE( |
| allocator.BindSharedCollection(std::move(token_client), std::move(collection_server)).ok()); |
| |
| // ----------------------------------------------------------------------// |
| // Use device local heap which only *registers* the koid of vmo to control |
| // device. |
| llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints; |
| constraints.usage.vulkan = llcpp::fuchsia::sysmem::VULKAN_IMAGE_USAGE_TRANSFER_DST; |
| constraints.min_buffer_count_for_camping = 1; |
| constraints.has_buffer_memory_constraints = true; |
| constraints.buffer_memory_constraints = llcpp::fuchsia::sysmem::BufferMemoryConstraints{ |
| .min_size_bytes = 4 * 1024, |
| .max_size_bytes = 4 * 1024, |
| .physically_contiguous_required = false, |
| .secure_required = false, |
| .ram_domain_supported = false, |
| .cpu_domain_supported = false, |
| .inaccessible_domain_supported = true, |
| .heap_permitted_count = 1, |
| .heap_permitted = {llcpp::fuchsia::sysmem::HeapType::GOLDFISH_DEVICE_LOCAL}}; |
| |
| llcpp::fuchsia::sysmem::BufferCollection::SyncClient collection(std::move(collection_client)); |
| SetDefaultCollectionName(collection); |
| EXPECT_TRUE(collection.SetConstraints(true, std::move(constraints)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info; |
| { |
| auto result = collection.WaitForBuffersAllocated(); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->status, ZX_OK); |
| |
| info = std::move(result.Unwrap()->buffer_collection_info); |
| EXPECT_EQ(info.buffer_count, 1u); |
| EXPECT_TRUE(info.buffers[0].vmo.is_valid()); |
| } |
| |
| zx::vmo vmo = std::move(info.buffers[0].vmo); |
| EXPECT_TRUE(vmo.is_valid()); |
| |
| EXPECT_TRUE(collection.Close().ok()); |
| |
| // ----------------------------------------------------------------------// |
| // Try creating data buffers. |
| zx::vmo vmo_copy; |
| llcpp::fuchsia::hardware::goldfish::ControlDevice::SyncClient control(std::move(control_channel)); |
| |
| { |
| // Verify that a CreateBuffer2() call without width will fail. |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateBuffer2Params::Frame>()) |
| // Without size |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| auto result = control.CreateBuffer2(std::move(vmo_copy), std::move(create_params)); |
| |
| ASSERT_TRUE(result.ok()); |
| ASSERT_TRUE(result.value().result.is_err()); |
| EXPECT_EQ(result.value().result.err(), ZX_ERR_INVALID_ARGS); |
| } |
| |
| { |
| // Verify that a CreateBuffer2() call without memory property will fail. |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateBuffer2Params::Frame>()) |
| .set_size(std::make_unique<uint64_t>(4096)) |
| // Without memory property |
| .build(); |
| auto result = control.CreateBuffer2(std::move(vmo_copy), std::move(create_params)); |
| |
| ASSERT_TRUE(result.ok()); |
| ASSERT_TRUE(result.value().result.is_err()); |
| EXPECT_EQ(result.value().result.err(), ZX_ERR_INVALID_ARGS); |
| } |
| } |
| |
| // In this test case we call GetBufferHandle() on a vmo |
| // registered to the control device but we haven't created |
| // the color buffer yet. |
| // |
| // The FIDL interface should return ZX_ERR_NOT_FOUND. |
| TEST(GoldfishControlTests, GoldfishControlTest_GetNotCreatedColorBuffer) { |
| int fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK); |
| |
| auto allocator = CreateSysmemAllocator(); |
| EXPECT_TRUE(allocator.channel()); |
| |
| zx::channel token_client; |
| zx::channel token_server; |
| EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK); |
| EXPECT_TRUE(allocator.AllocateSharedCollection(std::move(token_server)).ok()); |
| |
| zx::channel collection_client; |
| zx::channel collection_server; |
| EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK); |
| EXPECT_TRUE( |
| allocator.BindSharedCollection(std::move(token_client), std::move(collection_server)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints; |
| constraints.usage.vulkan = llcpp::fuchsia::sysmem::VULKAN_IMAGE_USAGE_TRANSFER_DST; |
| constraints.min_buffer_count_for_camping = 1; |
| constraints.has_buffer_memory_constraints = true; |
| constraints.buffer_memory_constraints = llcpp::fuchsia::sysmem::BufferMemoryConstraints{ |
| .min_size_bytes = 4 * 1024, |
| .max_size_bytes = 4 * 1024, |
| .physically_contiguous_required = false, |
| .secure_required = false, |
| .ram_domain_supported = false, |
| .cpu_domain_supported = false, |
| .inaccessible_domain_supported = true, |
| .heap_permitted_count = 1, |
| .heap_permitted = {llcpp::fuchsia::sysmem::HeapType::GOLDFISH_DEVICE_LOCAL}}; |
| |
| llcpp::fuchsia::sysmem::BufferCollection::SyncClient collection(std::move(collection_client)); |
| SetDefaultCollectionName(collection); |
| EXPECT_TRUE(collection.SetConstraints(true, std::move(constraints)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info; |
| { |
| auto result = collection.WaitForBuffersAllocated(); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->status, ZX_OK); |
| |
| info = std::move(result.Unwrap()->buffer_collection_info); |
| EXPECT_EQ(info.buffer_count, 1u); |
| EXPECT_TRUE(info.buffers[0].vmo.is_valid()); |
| } |
| |
| zx::vmo vmo = std::move(info.buffers[0].vmo); |
| EXPECT_TRUE(vmo.is_valid()); |
| |
| EXPECT_TRUE(collection.Close().ok()); |
| |
| zx::vmo vmo_copy; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| |
| llcpp::fuchsia::hardware::goldfish::ControlDevice::SyncClient control(std::move(channel)); |
| { |
| auto result = control.GetBufferHandle(std::move(vmo_copy)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_NOT_FOUND); |
| } |
| } |
| |
| TEST(GoldfishAddressSpaceTests, GoldfishAddressSpaceTest) { |
| int fd = open("/dev/class/goldfish-address-space/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel parent_channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, parent_channel.reset_and_get_address()), ZX_OK); |
| |
| zx::channel child_channel; |
| zx::channel child_channel2; |
| EXPECT_EQ(zx::channel::create(0, &child_channel, &child_channel2), ZX_OK); |
| |
| llcpp::fuchsia::hardware::goldfish::AddressSpaceDevice::SyncClient asd_parent( |
| std::move(parent_channel)); |
| { |
| auto result = asd_parent.OpenChildDriver( |
| llcpp::fuchsia::hardware::goldfish::AddressSpaceChildDriverType::DEFAULT, |
| std::move(child_channel)); |
| ASSERT_TRUE(result.ok()); |
| } |
| |
| constexpr uint64_t kHeapSize = 16ULL * 1048576ULL; |
| |
| llcpp::fuchsia::hardware::goldfish::AddressSpaceChildDriver::SyncClient asd_child( |
| std::move(child_channel2)); |
| uint64_t paddr = 0; |
| zx::vmo vmo; |
| { |
| auto result = asd_child.AllocateBlock(kHeapSize); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| |
| paddr = result.Unwrap()->paddr; |
| EXPECT_NE(paddr, 0U); |
| |
| vmo = std::move(result.Unwrap()->vmo); |
| EXPECT_EQ(vmo.is_valid(), true); |
| uint64_t actual_size = 0; |
| EXPECT_EQ(vmo.get_size(&actual_size), ZX_OK); |
| EXPECT_GE(actual_size, kHeapSize); |
| } |
| |
| zx::vmo vmo2; |
| uint64_t paddr2 = 0; |
| { |
| auto result = asd_child.AllocateBlock(kHeapSize); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| |
| paddr2 = result.Unwrap()->paddr; |
| EXPECT_NE(paddr2, 0U); |
| EXPECT_NE(paddr2, paddr); |
| |
| vmo2 = std::move(result.Unwrap()->vmo); |
| EXPECT_EQ(vmo2.is_valid(), true); |
| uint64_t actual_size = 0; |
| EXPECT_EQ(vmo2.get_size(&actual_size), ZX_OK); |
| EXPECT_GE(actual_size, kHeapSize); |
| } |
| |
| { |
| auto result = asd_child.DeallocateBlock(paddr); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| } |
| |
| { |
| auto result = asd_child.DeallocateBlock(paddr2); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| } |
| |
| // No testing into this too much, as it's going to be child driver-specific. |
| // Use fixed values for shared offset/size and ping metadata. |
| const uint64_t shared_offset = 4096; |
| const uint64_t shared_size = 4096; |
| |
| const uint64_t overlap_offsets[] = { |
| 4096, |
| 0, |
| 8191, |
| }; |
| const uint64_t overlap_sizes[] = { |
| 2048, |
| 4097, |
| 4096, |
| }; |
| |
| const size_t overlaps_to_test = sizeof(overlap_offsets) / sizeof(overlap_offsets[0]); |
| |
| using llcpp::fuchsia::hardware::goldfish::AddressSpaceChildDriverPingMessage; |
| |
| AddressSpaceChildDriverPingMessage msg; |
| msg.metadata = 0; |
| |
| EXPECT_TRUE(asd_child.Ping(std::move(msg)).ok()); |
| |
| { |
| auto result = asd_child.ClaimSharedBlock(shared_offset, shared_size); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| } |
| |
| // Test that overlapping blocks cannot be claimed in the same connection. |
| for (size_t i = 0; i < overlaps_to_test; ++i) { |
| auto result = asd_child.ClaimSharedBlock(overlap_offsets[i], overlap_sizes[i]); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_INVALID_ARGS); |
| } |
| |
| { |
| auto result = asd_child.UnclaimSharedBlock(shared_offset); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| } |
| |
| // Test that removed or unknown offsets cannot be unclaimed. |
| { |
| auto result = asd_child.UnclaimSharedBlock(shared_offset); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_INVALID_ARGS); |
| } |
| |
| { |
| auto result = asd_child.UnclaimSharedBlock(0); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_INVALID_ARGS); |
| } |
| } |
| |
| // This is a test case testing goldfish Heap, control device, address space |
| // device, and host implementation of host-visible memory allocation. |
| // |
| // This test case using a device-local Heap and a pre-allocated address space |
| // block to simulate a host-visible sysmem Heap. It does the following things: |
| // |
| // 1) It allocates a memory block (vmo = |address_space_vmo| and gpa = |
| // |physical_addr|) from address space device. |
| // |
| // 2) It allocates an vmo (vmo = |vmo|) from the goldfish device-local Heap |
| // so that |vmo| is registered for color buffer creation. |
| // |
| // 3) It calls goldfish Control FIDL API to create a color buffer using |vmo|. |
| // and maps memory to |physical_addr|. |
| // |
| // 4) The color buffer creation and memory process should work correctly, and |
| // heap offset should be a non-negative value. |
| // |
| TEST(GoldfishHostMemoryTests, GoldfishHostVisibleColorBuffer) { |
| // Setup control device. |
| int control_device_fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(control_device_fd, 0); |
| |
| zx::channel control_channel; |
| EXPECT_EQ(fdio_get_service_handle(control_device_fd, control_channel.reset_and_get_address()), |
| ZX_OK); |
| |
| // ----------------------------------------------------------------------// |
| // Setup sysmem allocator and buffer collection. |
| auto allocator = CreateSysmemAllocator(); |
| EXPECT_TRUE(allocator.channel()); |
| |
| zx::channel token_client; |
| zx::channel token_server; |
| EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK); |
| EXPECT_TRUE(allocator.AllocateSharedCollection(std::move(token_server)).ok()); |
| |
| zx::channel collection_client; |
| zx::channel collection_server; |
| EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK); |
| EXPECT_TRUE( |
| allocator.BindSharedCollection(std::move(token_client), std::move(collection_server)).ok()); |
| |
| // ----------------------------------------------------------------------// |
| // Setup address space driver. |
| int address_space_fd = open("/dev/class/goldfish-address-space/000", O_RDWR); |
| EXPECT_GE(address_space_fd, 0); |
| |
| zx::channel parent_channel; |
| EXPECT_EQ(fdio_get_service_handle(address_space_fd, parent_channel.reset_and_get_address()), |
| ZX_OK); |
| |
| zx::channel child_channel; |
| zx::channel child_channel2; |
| EXPECT_EQ(zx::channel::create(0, &child_channel, &child_channel2), ZX_OK); |
| |
| llcpp::fuchsia::hardware::goldfish::AddressSpaceDevice::SyncClient asd_parent( |
| std::move(parent_channel)); |
| { |
| auto result = asd_parent.OpenChildDriver( |
| llcpp::fuchsia::hardware::goldfish::AddressSpaceChildDriverType::DEFAULT, |
| std::move(child_channel)); |
| ASSERT_TRUE(result.ok()); |
| } |
| |
| // Allocate device memory block using address space device. |
| constexpr uint64_t kHeapSize = 32768ULL; |
| |
| llcpp::fuchsia::hardware::goldfish::AddressSpaceChildDriver::SyncClient asd_child( |
| std::move(child_channel2)); |
| uint64_t physical_addr = 0; |
| zx::vmo address_space_vmo; |
| { |
| auto result = asd_child.AllocateBlock(kHeapSize); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| |
| physical_addr = result.Unwrap()->paddr; |
| EXPECT_NE(physical_addr, 0U); |
| |
| address_space_vmo = std::move(result.Unwrap()->vmo); |
| EXPECT_EQ(address_space_vmo.is_valid(), true); |
| uint64_t actual_size = 0; |
| EXPECT_EQ(address_space_vmo.get_size(&actual_size), ZX_OK); |
| EXPECT_GE(actual_size, kHeapSize); |
| } |
| |
| // ----------------------------------------------------------------------// |
| // Use device local heap which only *registers* the koid of vmo to control |
| // device. |
| llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints; |
| constraints.usage.vulkan = llcpp::fuchsia::sysmem::VULKAN_IMAGE_USAGE_TRANSFER_DST; |
| constraints.min_buffer_count_for_camping = 1; |
| constraints.has_buffer_memory_constraints = true; |
| constraints.buffer_memory_constraints = llcpp::fuchsia::sysmem::BufferMemoryConstraints{ |
| .min_size_bytes = 4 * 1024, |
| .max_size_bytes = 4 * 1024, |
| .physically_contiguous_required = false, |
| .secure_required = false, |
| .ram_domain_supported = false, |
| .cpu_domain_supported = false, |
| .inaccessible_domain_supported = true, |
| .heap_permitted_count = 1, |
| .heap_permitted = {llcpp::fuchsia::sysmem::HeapType::GOLDFISH_DEVICE_LOCAL}}; |
| |
| llcpp::fuchsia::sysmem::BufferCollection::SyncClient collection(std::move(collection_client)); |
| SetDefaultCollectionName(collection); |
| EXPECT_TRUE(collection.SetConstraints(true, std::move(constraints)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info; |
| { |
| auto result = collection.WaitForBuffersAllocated(); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->status, ZX_OK); |
| |
| info = std::move(result.Unwrap()->buffer_collection_info); |
| EXPECT_EQ(info.buffer_count, 1U); |
| EXPECT_TRUE(info.buffers[0].vmo.is_valid()); |
| } |
| |
| zx::vmo vmo = std::move(info.buffers[0].vmo); |
| EXPECT_TRUE(vmo.is_valid()); |
| |
| EXPECT_TRUE(collection.Close().ok()); |
| |
| // ----------------------------------------------------------------------// |
| // Creates color buffer and map host memory. |
| zx::vmo vmo_copy; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| |
| llcpp::fuchsia::hardware::goldfish::ControlDevice::SyncClient control(std::move(control_channel)); |
| { |
| // Verify that a CreateColorBuffer2() call with host-visible memory property, |
| // but without physical address will fail. |
| auto create_info = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| .set_width(std::make_unique<uint32_t>(64)) |
| .set_height(std::make_unique<uint32_t>(64)) |
| .set_format(std::make_unique<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>( |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA)) |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_HOST_VISIBLE)) |
| // Without physical address |
| .build(); |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy), std::move(create_info)); |
| |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_ERR_INVALID_ARGS); |
| EXPECT_LT(result.Unwrap()->hw_address_page_offset, 0); |
| } |
| |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| { |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| .set_width(std::make_unique<uint32_t>(64)) |
| .set_height(std::make_unique<uint32_t>(64)) |
| .set_format(std::make_unique<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>( |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA)) |
| .set_memory_property(std::make_unique<uint32_t>(0x02u)) |
| .set_physical_address(std::make_unique<uint64_t>(physical_addr)) |
| .build(); |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy), std::move(create_params)); |
| |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_GE(result.Unwrap()->hw_address_page_offset, 0); |
| } |
| |
| // Verify if the color buffer works correctly. |
| zx::vmo vmo_copy2; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK); |
| { |
| auto result = control.GetBufferHandle(std::move(vmo_copy2)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_NE(result.Unwrap()->id, 0u); |
| EXPECT_EQ(result.Unwrap()->type, |
| llcpp::fuchsia::hardware::goldfish::BufferHandleType::COLOR_BUFFER); |
| } |
| |
| // Cleanup. |
| { |
| auto result = asd_child.DeallocateBlock(physical_addr); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| } |
| } |
| |
| using GoldfishCreateColorBufferTest = |
| testing::TestWithParam<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>; |
| |
| TEST_P(GoldfishCreateColorBufferTest, CreateColorBufferWithFormat) { |
| int fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK); |
| |
| auto allocator = CreateSysmemAllocator(); |
| EXPECT_TRUE(allocator.channel()); |
| |
| zx::channel token_client; |
| zx::channel token_server; |
| EXPECT_EQ(zx::channel::create(0, &token_client, &token_server), ZX_OK); |
| EXPECT_TRUE(allocator.AllocateSharedCollection(std::move(token_server)).ok()); |
| |
| zx::channel collection_client; |
| zx::channel collection_server; |
| EXPECT_EQ(zx::channel::create(0, &collection_client, &collection_server), ZX_OK); |
| EXPECT_TRUE( |
| allocator.BindSharedCollection(std::move(token_client), std::move(collection_server)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionConstraints constraints; |
| constraints.usage.vulkan = llcpp::fuchsia::sysmem::VULKAN_IMAGE_USAGE_TRANSFER_DST; |
| constraints.min_buffer_count_for_camping = 1; |
| constraints.has_buffer_memory_constraints = true; |
| constraints.buffer_memory_constraints = llcpp::fuchsia::sysmem::BufferMemoryConstraints{ |
| .min_size_bytes = 4 * 1024, |
| .max_size_bytes = 4 * 1024, |
| .physically_contiguous_required = false, |
| .secure_required = false, |
| .ram_domain_supported = false, |
| .cpu_domain_supported = false, |
| .inaccessible_domain_supported = true, |
| .heap_permitted_count = 1, |
| .heap_permitted = {llcpp::fuchsia::sysmem::HeapType::GOLDFISH_DEVICE_LOCAL}}; |
| |
| llcpp::fuchsia::sysmem::BufferCollection::SyncClient collection(std::move(collection_client)); |
| SetDefaultCollectionName(collection); |
| EXPECT_TRUE(collection.SetConstraints(true, std::move(constraints)).ok()); |
| |
| llcpp::fuchsia::sysmem::BufferCollectionInfo_2 info; |
| { |
| auto result = collection.WaitForBuffersAllocated(); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->status, ZX_OK); |
| |
| info = std::move(result.Unwrap()->buffer_collection_info); |
| EXPECT_EQ(info.buffer_count, 1U); |
| EXPECT_TRUE(info.buffers[0].vmo.is_valid()); |
| } |
| |
| zx::vmo vmo = std::move(info.buffers[0].vmo); |
| EXPECT_TRUE(vmo.is_valid()); |
| |
| EXPECT_TRUE(collection.Close().ok()); |
| |
| zx::vmo vmo_copy; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy), ZX_OK); |
| |
| llcpp::fuchsia::hardware::goldfish::ControlDevice::SyncClient control(std::move(channel)); |
| { |
| auto create_params = |
| llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Builder( |
| std::make_unique<llcpp::fuchsia::hardware::goldfish::CreateColorBuffer2Params::Frame>()) |
| .set_width(std::make_unique<uint32_t>(64)) |
| .set_height(std::make_unique<uint32_t>(64)) |
| .set_format(std::make_unique<llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType>( |
| GetParam())) |
| .set_memory_property(std::make_unique<uint32_t>( |
| llcpp::fuchsia::hardware::goldfish::MEMORY_PROPERTY_DEVICE_LOCAL)) |
| .build(); |
| |
| auto result = control.CreateColorBuffer2(std::move(vmo_copy), std::move(create_params)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| } |
| |
| zx::vmo vmo_copy2; |
| EXPECT_EQ(vmo.duplicate(ZX_RIGHT_SAME_RIGHTS, &vmo_copy2), ZX_OK); |
| |
| { |
| auto result = control.GetBufferHandle(std::move(vmo_copy2)); |
| ASSERT_TRUE(result.ok()); |
| EXPECT_EQ(result.Unwrap()->res, ZX_OK); |
| EXPECT_NE(result.Unwrap()->id, 0u); |
| EXPECT_EQ(result.Unwrap()->type, |
| llcpp::fuchsia::hardware::goldfish::BufferHandleType::COLOR_BUFFER); |
| } |
| } |
| |
| TEST(GoldfishControlTests, CreateSyncKhr) { |
| if (access("/dev/sys/platform/acpi/goldfish-sync", F_OK) != 0) { |
| GTEST_FAIL() << "Cannot access goldfish-sync device"; |
| } |
| |
| int fd = open("/dev/class/goldfish-control/000", O_RDWR); |
| EXPECT_GE(fd, 0); |
| |
| zx::channel channel; |
| EXPECT_EQ(fdio_get_service_handle(fd, channel.reset_and_get_address()), ZX_OK); |
| |
| llcpp::fuchsia::hardware::goldfish::ControlDevice::SyncClient control(std::move(channel)); |
| |
| zx::eventpair event_client, event_server; |
| zx_status_t status = zx::eventpair::create(0u, &event_client, &event_server); |
| { |
| auto result = control.CreateSyncFence(std::move(event_server)); |
| ASSERT_TRUE(result.ok()); |
| } |
| |
| zx_signals_t pending; |
| status = event_client.wait_one(ZX_EVENTPAIR_SIGNALED, zx::deadline_after(zx::sec(10)), &pending); |
| EXPECT_EQ(status, ZX_OK); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| ColorBufferTests, GoldfishCreateColorBufferTest, |
| testing::Values(llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::RGBA, |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA, |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::RG, |
| llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::LUMINANCE), |
| [](const testing::TestParamInfo<GoldfishCreateColorBufferTest::ParamType>& info) |
| -> std::string { |
| switch (info.param) { |
| case llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::RGBA: |
| return "RGBA"; |
| case llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::BGRA: |
| return "BGRA"; |
| case llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::RG: |
| return "RG"; |
| case llcpp::fuchsia::hardware::goldfish::ColorBufferFormatType::LUMINANCE: |
| return "LUMINANCE"; |
| } |
| }); |
| |
| int main(int argc, char** argv) { |
| if (access("/dev/sys/platform/acpi/goldfish", F_OK) != -1) { |
| testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |
| return 0; |
| } |