| // Copyright 2018 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/host_image.h" |
| |
| #include "garnet/lib/ui/gfx/tests/session_test.h" |
| #include "garnet/lib/ui/gfx/tests/vk_session_test.h" |
| #include "gtest/gtest.h" |
| #include "lib/images/cpp/images.h" |
| #include "lib/ui/scenic/cpp/commands.h" |
| #include "src/ui/lib/escher/test/gtest_vulkan.h" |
| #include "src/ui/lib/escher/vk/vulkan_device_queues.h" |
| |
| using namespace escher; |
| |
| namespace { |
| |
| const uint32_t kVmoSize = 65536; |
| // If you change the size of this buffer, make sure that the YUV test in |
| // scenic_pixel_test.cc is also updated. Unlike this unit test, |
| // scenic_pixel_test.cc has no way to confirm that it is going through the |
| // direct-to-GPU path. |
| // TODO(SCN-1387): This number needs to be queried via sysmem or vulkan. |
| const uint32_t kSize = 64; |
| const uint32_t kMemoryId = 1; |
| const uint32_t kImageId = 2; |
| const uint32_t kImagePipeId = 3; |
| |
| class ImageFactoryListener : public ImageFactory { |
| public: |
| ImageFactoryListener(ImageFactory* factory) : factory_(factory) {} |
| ImagePtr NewImage(const ImageInfo& info, GpuMemPtr* out_ptr = nullptr) { |
| ++images_created_; |
| return factory_->NewImage(info, out_ptr); |
| } |
| |
| uint32_t images_created_ = 0; |
| ImageFactory* factory_; |
| }; |
| |
| } // namespace |
| |
| namespace scenic_impl { |
| namespace gfx { |
| namespace test { |
| |
| class HostImageTest : public VkSessionTest { |
| public: |
| void OnSessionContextCreated(SessionContext* context) override { |
| ASSERT_FALSE(listener); |
| listener = |
| std::make_unique<ImageFactoryListener>(context->escher_image_factory); |
| context->escher_image_factory = listener.get(); |
| } |
| |
| std::unique_ptr<ImageFactoryListener> listener; |
| }; |
| |
| VK_TEST_F(HostImageTest, FindResource) { |
| zx::vmo vmo; |
| zx_status_t status = zx::vmo::create(kVmoSize, 0u, &vmo); |
| ASSERT_EQ(ZX_OK, status); |
| |
| ASSERT_TRUE(Apply( |
| scenic::NewCreateMemoryCmd(kMemoryId, std::move(vmo), kVmoSize, |
| fuchsia::images::MemoryType::HOST_MEMORY))); |
| |
| fuchsia::images::ImageInfo image_info{ |
| .width = kSize, |
| .height = kSize, |
| .stride = static_cast<uint32_t>( |
| kSize * images::StrideBytesPerWidthPixel( |
| fuchsia::images::PixelFormat::BGRA_8)), |
| .pixel_format = fuchsia::images::PixelFormat::BGRA_8, |
| }; |
| |
| ASSERT_TRUE( |
| Apply(scenic::NewCreateImageCmd(kImageId, kMemoryId, 0, image_info))); |
| |
| fidl::InterfacePtr<fuchsia::images::ImagePipe> image_pipe; |
| ASSERT_TRUE(Apply( |
| scenic::NewCreateImagePipeCmd(kImagePipeId, image_pipe.NewRequest()))); |
| |
| // Host images should be findable as their concrete sub-class. |
| auto host_image_resource = FindResource<HostImage>(kImageId); |
| EXPECT_TRUE(host_image_resource); |
| // Host images should also be findable as their base class (i.e., Image). |
| auto image_resource = FindResource<Image>(kImageId); |
| EXPECT_TRUE(image_resource); |
| // Memory should not be findable as the same base class. |
| auto memory_as_image_resource = FindResource<Image>(kMemoryId); |
| EXPECT_FALSE(memory_as_image_resource); |
| // Image pipes should not be findable as the Image class (even though they are |
| // an ImageBase, the next class down). |
| auto image_pipe_as_image_resource = FindResource<Image>(kImagePipeId); |
| EXPECT_FALSE(image_pipe_as_image_resource); |
| } |
| |
| VK_TEST_F(HostImageTest, RgbaImport) { |
| zx::vmo vmo; |
| zx_status_t status = zx::vmo::create(kVmoSize, 0u, &vmo); |
| ASSERT_EQ(ZX_OK, status); |
| |
| ASSERT_TRUE(Apply( |
| scenic::NewCreateMemoryCmd(kMemoryId, std::move(vmo), kVmoSize, |
| fuchsia::images::MemoryType::HOST_MEMORY))); |
| |
| fuchsia::images::ImageInfo image_info{ |
| .width = kSize, |
| .height = kSize, |
| .stride = static_cast<uint32_t>( |
| kSize * images::StrideBytesPerWidthPixel( |
| fuchsia::images::PixelFormat::BGRA_8)), |
| .pixel_format = fuchsia::images::PixelFormat::BGRA_8, |
| }; |
| |
| ASSERT_TRUE( |
| Apply(scenic::NewCreateImageCmd(kImageId, kMemoryId, 0, image_info))); |
| |
| auto image_resource = FindResource<HostImage>(kImageId); |
| ASSERT_TRUE(image_resource); |
| |
| EXPECT_FALSE(image_resource->IsDirectlyMapped()); |
| // Before updating pixels, image resources should never return a valid Escher |
| // image. |
| EXPECT_FALSE(image_resource->GetEscherImage()); |
| // Updating shouldn't crash when passesd a null gpu_uploader, but it should |
| // also keep the image dirty, because the copy from CPU to GPU memory has not |
| // occured yet. |
| image_resource->UpdateEscherImage(nullptr); |
| // Because we did not provide a valid batch uploader, the image is still dirty |
| // and in need of an update. Until that succeeds, GetEscherImage() should not |
| // return a valid image. |
| EXPECT_FALSE(image_resource->GetEscherImage()); |
| // A backing image should have been constructed through the image factory. |
| EXPECT_EQ(1u, listener->images_created_); |
| } |
| |
| VK_TEST_F(HostImageTest, YuvImportOnUmaPlatform) { |
| auto vulkan_queues = CreateVulkanDeviceQueues(); |
| auto device = vulkan_queues->vk_device(); |
| auto physical_device = vulkan_queues->vk_physical_device(); |
| |
| if (!Memory::HasSharedMemoryPools(device, physical_device)) { |
| FXL_LOG(INFO) |
| << "Could not find UMA compatible memory pool, aborting test."; |
| } |
| |
| zx::vmo vmo; |
| zx_status_t status = zx::vmo::create(kVmoSize, 0u, &vmo); |
| ASSERT_EQ(ZX_OK, status); |
| |
| ASSERT_TRUE(Apply( |
| scenic::NewCreateMemoryCmd(kMemoryId, std::move(vmo), kVmoSize, |
| fuchsia::images::MemoryType::HOST_MEMORY))); |
| |
| fuchsia::images::ImageInfo image_info{ |
| .width = kSize, |
| .height = kSize, |
| .stride = static_cast<uint32_t>( |
| kSize * |
| images::StrideBytesPerWidthPixel(fuchsia::images::PixelFormat::NV12)), |
| .pixel_format = fuchsia::images::PixelFormat::NV12, |
| }; |
| |
| ASSERT_TRUE( |
| Apply(scenic::NewCreateImageCmd(kImageId, kMemoryId, 0, image_info))); |
| |
| auto image_resource = FindResource<HostImage>(kImageId); |
| ASSERT_TRUE(image_resource); |
| |
| EXPECT_TRUE(image_resource->IsDirectlyMapped()); |
| // Even direct mapped images don't return a valid Escher image until at least |
| // one call to UpdatePixels has occured. |
| EXPECT_FALSE(image_resource->GetEscherImage()); |
| // Updating should be a no-op, so it shouldn't crash when passesd a null |
| // gpu_uploader, but it should also remove the dirty bit, meaning there is no |
| // additional work to do. |
| image_resource->UpdateEscherImage(nullptr); |
| // Despite not updating, the resource should have a valid Escher image, since |
| // we mapped it directly with zero copies. |
| EXPECT_TRUE(image_resource->GetEscherImage()); |
| // The images should have been constructed directly, not through the image |
| // factory. |
| EXPECT_EQ(0u, listener->images_created_); |
| } |
| |
| } // namespace test |
| } // namespace gfx |
| } // namespace scenic_impl |