blob: 836641dc2b7ebb1d87dd30e4037d69d6c732ab24 [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 <fuchsia/sysmem/cpp/fidl.h>
#include <lib/fdio/directory.h>
#include <lib/zx/channel.h>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <fbl/algorithm.h>
#include <gtest/gtest.h>
#include <vulkan/vulkan.h>
#include "config_query.h"
#include "src/graphics/tests/common/utils.h"
#include "src/graphics/tests/common/vulkan_context.h"
#include "src/lib/fsl/handles/object_info.h"
#include "vulkan_extension_test.h"
#include "vulkan/vulkan_enums.hpp"
#include "vulkan/vulkan_handles.hpp"
#include <vulkan/vulkan.hpp>
namespace {
constexpr uint32_t kDefaultWidth = 64;
constexpr uint32_t kDefaultHeight = 64;
constexpr VkFormat kDefaultFormat = VK_FORMAT_R8G8B8A8_UNORM;
constexpr VkFormat kDefaultYuvFormat = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
// Parameter is true if the image should be linear.
class VulkanImageExtensionTest : public VulkanExtensionTest,
public ::testing::WithParamInterface<bool> {};
TEST_P(VulkanImageExtensionTest, BufferCollectionNV12_1026) {
ASSERT_TRUE(Initialize());
// TODO(https://fxbug.dev/42137913): Enable the test when YUV sysmem images are
// supported on emulators.
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (!SupportsSysmemYuv())
GTEST_SKIP();
ASSERT_TRUE(Exec(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, 1026, 64, GetParam(), false));
}
TEST_P(VulkanImageExtensionTest, BufferCollectionRGBA) {
ASSERT_TRUE(Initialize());
ASSERT_TRUE(Exec(VK_FORMAT_R8G8B8A8_UNORM, 64, 64, GetParam(), false));
}
TEST_P(VulkanImageExtensionTest, BufferCollectionRGBA_1026) {
ASSERT_TRUE(Initialize());
ASSERT_TRUE(Exec(VK_FORMAT_R8G8B8A8_UNORM, 1026, 64, GetParam(), false));
}
TEST_P(VulkanImageExtensionTest, BufferCollectionNV12) {
ASSERT_TRUE(Initialize());
// TODO(https://fxbug.dev/42137913): Enable the test when YUV sysmem images are
// supported on emulators.
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (!SupportsSysmemYuv())
GTEST_SKIP();
ASSERT_TRUE(Exec(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, 64, 64, GetParam(), false));
}
TEST_P(VulkanImageExtensionTest, BufferCollectionI420) {
ASSERT_TRUE(Initialize());
// TODO(https://fxbug.dev/42137913): Enable the test when YUV sysmem images are
// supported on emulators.
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (!SupportsSysmemYuv())
GTEST_SKIP();
ASSERT_TRUE(Exec(VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM, 64, 64, GetParam(), false));
}
TEST_P(VulkanImageExtensionTest, BufferCollectionNV12_1280_546) {
ASSERT_TRUE(Initialize());
// TODO(https://fxbug.dev/42137913): Enable the test when YUV sysmem images are
// supported on emulators.
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (!SupportsSysmemYuv())
GTEST_SKIP();
ASSERT_TRUE(Exec(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, 8192, 546, GetParam(), false));
}
TEST_P(VulkanImageExtensionTest, BufferCollectionRGB565) {
ASSERT_TRUE(Initialize());
ASSERT_TRUE(Exec(VK_FORMAT_R5G6B5_UNORM_PACK16, 64, 64, GetParam(), false));
}
TEST_P(VulkanImageExtensionTest, BufferCollectionMultipleFormats) {
ASSERT_TRUE(Initialize());
fuchsia::sysmem::ImageFormatConstraints nv12_image_constraints =
GetDefaultSysmemImageFormatConstraints();
nv12_image_constraints.pixel_format = {fuchsia::sysmem::PixelFormatType::NV12, false, {}};
nv12_image_constraints.color_space[0].type = fuchsia::sysmem::ColorSpaceType::REC709;
fuchsia::sysmem::ImageFormatConstraints bgra_image_constraints =
GetDefaultSysmemImageFormatConstraints();
fuchsia::sysmem::ImageFormatConstraints bgra_tiled_image_constraints =
GetDefaultSysmemImageFormatConstraints();
bgra_tiled_image_constraints.pixel_format = {
fuchsia::sysmem::PixelFormatType::BGRA32,
true,
{fuchsia::sysmem::FORMAT_MODIFIER_INTEL_I915_X_TILED}};
std::vector<fuchsia::sysmem::ImageFormatConstraints> all_constraints{
nv12_image_constraints, bgra_image_constraints, bgra_tiled_image_constraints};
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (SupportsSysmemYuv()) {
ASSERT_TRUE(
Exec(VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, 64, 64, GetParam(), false, all_constraints));
}
vk_device_memory_ = {};
ASSERT_TRUE(Exec(VK_FORMAT_B8G8R8A8_UNORM, 64, 64, GetParam(), false, all_constraints));
}
TEST_P(VulkanImageExtensionTest, MultiImageFormatEntrypoint) {
ASSERT_TRUE(Initialize());
auto [vulkan_token] = MakeSharedCollection<1>();
bool linear = GetParam();
auto image_create_info = GetDefaultImageCreateInfo(use_protected_memory_, kDefaultFormat,
kDefaultWidth, kDefaultHeight, linear);
vk::ImageFormatConstraintsInfoFUCHSIA constraints = GetDefaultRgbImageFormatConstraintsInfo();
constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection =
CreateVkBufferCollectionForImage(std::move(vulkan_token), constraints);
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
if (linear) {
CheckLinearSubresourceLayout(kDefaultFormat, kDefaultWidth);
}
ASSERT_TRUE(InitializeDirectImageMemory(*collection));
}
TEST_P(VulkanImageExtensionTest, R8) {
ASSERT_TRUE(Initialize());
auto [vulkan_token, sysmem_token] = MakeSharedCollection<2>();
bool linear = GetParam();
// TODO(https://fxbug.dev/42137913): Enable the test on emulators when goldfish host-visible heap
// supports R8 linear images.
if (linear && !SupportsSysmemLinearNonRgba())
GTEST_SKIP();
auto image_create_info = GetDefaultImageCreateInfo(use_protected_memory_, VK_FORMAT_R8_UNORM,
kDefaultWidth, kDefaultHeight, linear);
vk::ImageFormatConstraintsInfoFUCHSIA constraints = GetDefaultRgbImageFormatConstraintsInfo();
constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection =
CreateVkBufferCollectionForImage(std::move(vulkan_token), constraints);
auto sysmem_collection_info = AllocateSysmemCollection({}, std::move(sysmem_token));
EXPECT_EQ(fuchsia::sysmem::PixelFormatType::R8,
sysmem_collection_info.settings.image_format_constraints.pixel_format.type);
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
if (linear) {
CheckLinearSubresourceLayout(VK_FORMAT_R8_UNORM, kDefaultWidth);
}
ASSERT_TRUE(InitializeDirectImageMemory(*collection));
vk::BufferCollectionPropertiesFUCHSIA properties;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collection, &properties, loader_));
EXPECT_EQ(static_cast<uint32_t>(fuchsia::sysmem::PixelFormatType::R8),
properties.sysmemPixelFormat);
}
TEST_P(VulkanImageExtensionTest, R8G8) {
ASSERT_TRUE(Initialize());
auto [vulkan_token] = MakeSharedCollection<1>();
bool linear = GetParam();
// TODO(https://fxbug.dev/42137913): Enable the test on emulators when goldfish host-visible heap
// supports R8G8 linear images.
if (linear && !SupportsSysmemLinearNonRgba())
GTEST_SKIP();
auto image_create_info = GetDefaultImageCreateInfo(use_protected_memory_, VK_FORMAT_R8G8_UNORM,
kDefaultWidth, kDefaultHeight, linear);
vk::ImageFormatConstraintsInfoFUCHSIA constraints = GetDefaultRgbImageFormatConstraintsInfo();
constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection =
CreateVkBufferCollectionForImage(std::move(vulkan_token), constraints);
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
if (linear) {
CheckLinearSubresourceLayout(VK_FORMAT_R8G8_UNORM, kDefaultWidth);
}
ASSERT_TRUE(InitializeDirectImageMemory(*collection));
}
TEST_P(VulkanImageExtensionTest, R8ToL8) {
ASSERT_TRUE(Initialize());
auto [vulkan_token, sysmem_token] = MakeSharedCollection<2>();
bool linear = GetParam();
// TODO(https://fxbug.dev/42137913): Enable the test on emulators when goldfish host-visible heap
// supports R8/L8 linear images.
if (linear && !SupportsSysmemLinearNonRgba())
GTEST_SKIP();
auto image_create_info = GetDefaultImageCreateInfo(use_protected_memory_, VK_FORMAT_R8_UNORM,
kDefaultWidth, kDefaultHeight, linear);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.sysmemPixelFormat =
static_cast<uint64_t>(fuchsia::sysmem::PixelFormatType::L8);
format_constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection =
CreateVkBufferCollectionForImage(std::move(vulkan_token), format_constraints);
auto sysmem_collection_info = AllocateSysmemCollection({}, std::move(sysmem_token));
EXPECT_EQ(fuchsia::sysmem::PixelFormatType::L8,
sysmem_collection_info.settings.image_format_constraints.pixel_format.type);
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
if (linear) {
CheckLinearSubresourceLayout(VK_FORMAT_R8_UNORM, kDefaultWidth);
}
ASSERT_TRUE(InitializeDirectImageMemory(*collection));
vk::BufferCollectionPropertiesFUCHSIA properties;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collection, &properties, loader_));
EXPECT_EQ(static_cast<uint32_t>(fuchsia::sysmem::PixelFormatType::L8),
properties.sysmemPixelFormat);
}
TEST_P(VulkanImageExtensionTest, NonPackedImage) {
ASSERT_TRUE(Initialize());
auto [vulkan_token, sysmem_token] = MakeSharedCollection<2>();
bool linear = GetParam();
auto image_create_info = GetDefaultImageCreateInfo(
use_protected_memory_, VK_FORMAT_B8G8R8A8_UNORM, kDefaultWidth, kDefaultHeight, linear);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection =
CreateVkBufferCollectionForImage(std::move(vulkan_token), format_constraints);
fuchsia::sysmem::BufferCollectionConstraints constraints;
constraints.usage.vulkan = fuchsia::sysmem::vulkanUsageTransferDst;
constraints.image_format_constraints_count = 1;
constraints.image_format_constraints[0] = GetDefaultSysmemImageFormatConstraints();
constraints.image_format_constraints[0].min_coded_width = 64;
constraints.image_format_constraints[0].min_bytes_per_row = 1024;
auto sysmem_collection_info = AllocateSysmemCollection(constraints, std::move(sysmem_token));
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
if (linear) {
CheckLinearSubresourceLayout(VK_FORMAT_R8_UNORM, kDefaultWidth);
}
ASSERT_TRUE(InitializeDirectImageMemory(*collection));
vk::BufferCollectionPropertiesFUCHSIA properties;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collection, &properties, loader_));
}
TEST_P(VulkanImageExtensionTest, ImageCpuAccessible) {
ASSERT_TRUE(Initialize());
auto [vulkan_token] = MakeSharedCollection<1>();
bool linear = GetParam();
auto image_create_info = GetDefaultImageCreateInfo(use_protected_memory_, kDefaultFormat,
kDefaultWidth, kDefaultHeight, linear);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection =
CreateVkBufferCollectionForImage(std::move(vulkan_token), format_constraints,
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuReadOften |
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuWriteOften);
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
if (linear) {
CheckLinearSubresourceLayout(kDefaultFormat, kDefaultWidth);
}
ASSERT_TRUE(InitializeDirectImageMemory(*collection));
{
// Check that all memory types are host visible.
vk::BufferCollectionPropertiesFUCHSIA properties;
vk::Result result1 =
ctx_->device()->getBufferCollectionPropertiesFUCHSIA(*collection, &properties, loader_);
EXPECT_EQ(result1, vk::Result::eSuccess);
VkPhysicalDeviceMemoryProperties memory_properties;
vkGetPhysicalDeviceMemoryProperties(ctx_->physical_device(), &memory_properties);
for (uint32_t i = 0; i < memory_properties.memoryTypeCount; ++i) {
if (properties.memoryTypeBits & (1 << i)) {
EXPECT_TRUE(memory_properties.memoryTypes[i].propertyFlags &
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
if (!(memory_properties.memoryTypes[i].propertyFlags &
VK_MEMORY_PROPERTY_HOST_CACHED_BIT)) {
printf(
"WARNING: read-often buffer may be using non-cached memory. This will work but may "
"be slow.\n");
fflush(stdout);
}
}
}
}
void *data;
EXPECT_EQ(vk::Result::eSuccess,
ctx_->device()->mapMemory(*vk_device_memory_, 0, VK_WHOLE_SIZE, {}, &data));
auto volatile_data = static_cast<volatile uint8_t *>(data);
*volatile_data = 1;
EXPECT_EQ(1u, *volatile_data);
}
TEST_P(VulkanImageExtensionTest, BadSysmemFormat) {
ASSERT_TRUE(Initialize());
auto [vulkan_token] = MakeSharedCollection<1>();
constexpr VkFormat kFormat = VK_FORMAT_R8G8B8A8_UNORM;
bool linear = GetParam();
auto image_create_info =
GetDefaultImageCreateInfo(false, kFormat, kDefaultWidth, kDefaultHeight, linear);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = image_create_info;
format_constraints.sysmemPixelFormat = static_cast<int>(fuchsia::sysmem::PixelFormatType::NV12);
vk::BufferCollectionCreateInfoFUCHSIA import_info(vulkan_token.Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = &format_constraints;
constraints_info.formatConstraintsCount = 1;
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
// NV12 and R8G8B8A8 aren't compatible, so combining them should fail.
EXPECT_NE(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collection, constraints_info, loader_));
}
TEST_P(VulkanImageExtensionTest, BadColorSpace) {
ASSERT_TRUE(Initialize());
auto [vulkan_token] = MakeSharedCollection<1>();
bool linear = GetParam();
std::array<vk::SysmemColorSpaceFUCHSIA, 2> color_spaces;
color_spaces[0].colorSpace = static_cast<uint32_t>(fuchsia::sysmem::ColorSpaceType::REC601_NTSC);
color_spaces[1].colorSpace = static_cast<uint32_t>(fuchsia::sysmem::ColorSpaceType::REC709);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo =
GetDefaultImageCreateInfo(false, kDefaultFormat, kDefaultWidth, kDefaultHeight, linear);
format_constraints.pColorSpaces = color_spaces.data();
format_constraints.colorSpaceCount = color_spaces.size();
vk::BufferCollectionCreateInfoFUCHSIA import_info(vulkan_token.Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = &format_constraints;
constraints_info.formatConstraintsCount = 1;
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collection, constraints_info, loader_));
// REC601 and REC709 aren't compatible with R8G8B8A8, so allocation should fail.
vk::BufferCollectionPropertiesFUCHSIA properties;
EXPECT_NE(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collection, &properties, loader_));
}
TEST_P(VulkanImageExtensionTest, YUVProperties) {
ASSERT_TRUE(Initialize());
// TODO(https://fxbug.dev/42137913): Enable the test when YUV sysmem images are
// supported on emulators.
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (!SupportsSysmemYuv())
GTEST_SKIP();
auto [vulkan_token] = MakeSharedCollection<1>();
bool linear = GetParam();
std::array<vk::SysmemColorSpaceFUCHSIA, 1> color_spaces;
color_spaces[0].colorSpace = static_cast<uint32_t>(fuchsia::sysmem::ColorSpaceType::REC709);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultYuvImageFormatConstraintsInfo();
format_constraints.imageCreateInfo =
GetDefaultImageCreateInfo(false, kDefaultYuvFormat, kDefaultWidth, kDefaultHeight, linear);
format_constraints.pColorSpaces = color_spaces.data();
format_constraints.colorSpaceCount = color_spaces.size();
format_constraints.sysmemPixelFormat =
static_cast<uint64_t>(fuchsia::sysmem::PixelFormatType::NV12);
UniqueBufferCollection collection =
CreateVkBufferCollectionForImage(std::move(vulkan_token), format_constraints);
vk::BufferCollectionPropertiesFUCHSIA properties;
ASSERT_EQ(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collection, &properties, loader_));
EXPECT_EQ(static_cast<uint32_t>(fuchsia::sysmem::ColorSpaceType::REC709),
properties.sysmemColorSpaceIndex.colorSpace);
EXPECT_EQ(static_cast<uint32_t>(fuchsia::sysmem::PixelFormatType::NV12),
properties.sysmemPixelFormat);
EXPECT_EQ(0u, properties.createInfoIndex);
EXPECT_EQ(1u, properties.bufferCount);
EXPECT_TRUE(properties.formatFeatures & vk::FormatFeatureFlagBits::eSampledImage);
// The driver could represent these differently, but all current drivers want the identity.
EXPECT_EQ(vk::ComponentSwizzle::eIdentity, properties.samplerYcbcrConversionComponents.r);
EXPECT_EQ(vk::ComponentSwizzle::eIdentity, properties.samplerYcbcrConversionComponents.g);
EXPECT_EQ(vk::ComponentSwizzle::eIdentity, properties.samplerYcbcrConversionComponents.b);
EXPECT_EQ(vk::ComponentSwizzle::eIdentity, properties.samplerYcbcrConversionComponents.a);
EXPECT_EQ(vk::SamplerYcbcrModelConversion::eYcbcr709, properties.suggestedYcbcrModel);
EXPECT_EQ(vk::SamplerYcbcrRange::eItuNarrow, properties.suggestedYcbcrRange);
// Match h.264 default sitings by default.
EXPECT_EQ(vk::ChromaLocation::eCositedEven, properties.suggestedXChromaOffset);
EXPECT_EQ(vk::ChromaLocation::eMidpoint, properties.suggestedYChromaOffset);
}
// Check that if a collection could be used with two different formats, that sysmem can negotiate a
// common format.
TEST_P(VulkanImageExtensionTest, MultiFormat) {
ASSERT_TRUE(Initialize());
// TODO(https://fxbug.dev/42137913): Enable the test when YUV sysmem images are
// supported on emulators.
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (!SupportsSysmemYuv())
GTEST_SKIP();
auto tokens = MakeSharedCollection(2u);
bool linear = GetParam();
auto nv12_create_info =
GetDefaultImageCreateInfo(false, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM, 1, 1, linear);
auto rgb_create_info = GetDefaultImageCreateInfo(false, VK_FORMAT_R8G8B8A8_UNORM, 1, 1, linear);
auto rgb_create_info_full_size = GetDefaultImageCreateInfo(false, VK_FORMAT_R8G8B8A8_UNORM,
kDefaultWidth, kDefaultHeight, linear);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints_info =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints_info.imageCreateInfo = rgb_create_info;
std::vector<UniqueBufferCollection> collections;
for (uint32_t i = 0; i < 2; i++) {
vk::BufferCollectionCreateInfoFUCHSIA import_info(tokens[i].Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
collections.push_back(std::move(collection));
}
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = &format_constraints_info;
constraints_info.formatConstraintsCount = 1;
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
constraints_info.bufferCollectionConstraints.minBufferCountForCamping = 1;
constraints_info.bufferCollectionConstraints.minBufferCountForSharedSlack = 2;
constraints_info.bufferCollectionConstraints.minBufferCountForDedicatedSlack = 3;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collections[0], constraints_info, loader_));
std::array<vk::ImageFormatConstraintsInfoFUCHSIA, 2> format_constraints_infos = {
GetDefaultYuvImageFormatConstraintsInfo(),
GetDefaultRgbImageFormatConstraintsInfo(),
};
format_constraints_infos[0].imageCreateInfo = nv12_create_info;
format_constraints_infos[1].imageCreateInfo = rgb_create_info_full_size;
constraints_info.pFormatConstraints = format_constraints_infos.data();
constraints_info.formatConstraintsCount = format_constraints_infos.size();
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collections[1], constraints_info, loader_));
const uint32_t kExpectedImageCount =
constraints_info.bufferCollectionConstraints.minBufferCountForCamping * 2 +
constraints_info.bufferCollectionConstraints.minBufferCountForDedicatedSlack * 2 +
constraints_info.bufferCollectionConstraints.minBufferCountForSharedSlack;
for (uint32_t i = 0; i < 2; i++) {
vk::BufferCollectionPropertiesFUCHSIA properties;
ASSERT_EQ(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collections[i], &properties, loader_));
EXPECT_EQ(i == 0 ? 0u : 1u, properties.createInfoIndex);
EXPECT_EQ(kExpectedImageCount, properties.bufferCount);
EXPECT_TRUE(properties.formatFeatures & vk::FormatFeatureFlagBits::eSampledImage);
}
vk::BufferCollectionImageCreateInfoFUCHSIA image_format_fuchsia;
image_format_fuchsia.collection = *collections[0];
image_format_fuchsia.index = 3;
rgb_create_info_full_size.pNext = &image_format_fuchsia;
auto [result, vk_image] = ctx_->device()->createImageUnique(rgb_create_info_full_size, nullptr);
EXPECT_EQ(result, vk::Result::eSuccess);
vk_image_ = std::move(vk_image);
ASSERT_TRUE(InitializeDirectImageMemory(*collections[0], kExpectedImageCount));
}
TEST_P(VulkanImageExtensionTest, MaxBufferCountCheck) {
ASSERT_TRUE(Initialize());
// TODO(https://fxbug.dev/42137913): Enable the test when YUV sysmem images are
// supported on emulators.
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (!device_supports_protected_memory_)
GTEST_SKIP();
auto tokens = MakeSharedCollection(2u);
bool linear = GetParam();
auto nv12_create_info = GetDefaultImageCreateInfo(false, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
kDefaultWidth, kDefaultHeight, linear);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints_info =
GetDefaultYuvImageFormatConstraintsInfo();
format_constraints_info.imageCreateInfo = nv12_create_info;
std::vector<UniqueBufferCollection> collections;
for (uint32_t i = 0; i < 2; i++) {
vk::BufferCollectionCreateInfoFUCHSIA import_info(tokens[i].Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
collections.push_back(std::move(collection));
}
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = &format_constraints_info;
constraints_info.formatConstraintsCount = 1;
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
constraints_info.bufferCollectionConstraints.maxBufferCount = 1;
constraints_info.bufferCollectionConstraints.minBufferCountForCamping = 1;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collections[0], constraints_info, loader_));
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collections[1], constraints_info, loader_));
// Total buffer count for camping (2) exceeds maxBufferCount, so allocation should fail.
for (auto &collection : collections) {
vk::BufferCollectionPropertiesFUCHSIA properties;
EXPECT_NE(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collection, &properties, loader_));
}
}
TEST_P(VulkanImageExtensionTest, ManyIdenticalFormats) {
ASSERT_TRUE(Initialize());
// TODO(https://fxbug.dev/42137913): Enable the test when YUV sysmem images are
// supported on emulators.
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (!SupportsSysmemYuv())
GTEST_SKIP();
auto [token] = MakeSharedCollection<1>();
bool linear = GetParam();
auto nv12_create_info = GetDefaultImageCreateInfo(false, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
kDefaultWidth, kDefaultHeight, linear);
vk::BufferCollectionCreateInfoFUCHSIA import_info(token.Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
// All create info are identical, so the driver should be able to deduplicate them even though
// there are more formats than sysmem supports.
std::vector<vk::ImageFormatConstraintsInfoFUCHSIA> format_constraints_infos(
64, GetDefaultYuvImageFormatConstraintsInfo());
for (uint32_t i = 0; i < format_constraints_infos.size(); i++) {
format_constraints_infos[i].imageCreateInfo = nv12_create_info;
}
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = format_constraints_infos.data();
constraints_info.formatConstraintsCount = static_cast<uint32_t>(format_constraints_infos.size());
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
ASSERT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collection, constraints_info, loader_));
vk::BufferCollectionPropertiesFUCHSIA properties;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collection, &properties, loader_));
EXPECT_GT(format_constraints_infos.size(), properties.createInfoIndex);
}
// Check that createInfoIndex keeps track of multiple colorspaces properly.
TEST_P(VulkanImageExtensionTest, ColorSpaceSubset) {
ASSERT_TRUE(Initialize());
// TODO(https://fxbug.dev/42137913): Enable the test when YUV sysmem images are
// supported on emulators.
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (!SupportsSysmemYuv())
GTEST_SKIP();
auto tokens = MakeSharedCollection(2u);
bool linear = GetParam();
auto nv12_create_info = GetDefaultImageCreateInfo(false, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
kDefaultWidth, kDefaultHeight, linear);
std::vector<UniqueBufferCollection> collections;
for (uint32_t i = 0; i < 2; i++) {
vk::BufferCollectionCreateInfoFUCHSIA import_info(tokens[i].Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
collections.push_back(std::move(collection));
}
// Two different create info, where the only difference is the supported set of sysmem
// colorspaces.
std::array<vk::ImageFormatConstraintsInfoFUCHSIA, 2> format_constraints = {
GetDefaultYuvImageFormatConstraintsInfo(),
GetDefaultYuvImageFormatConstraintsInfo(),
};
format_constraints[0].imageCreateInfo = nv12_create_info;
format_constraints[1].imageCreateInfo = nv12_create_info;
std::array<vk::SysmemColorSpaceFUCHSIA, 2> color_spaces_601;
color_spaces_601[0].colorSpace =
static_cast<uint32_t>(fuchsia::sysmem::ColorSpaceType::REC601_NTSC);
color_spaces_601[1].colorSpace =
static_cast<uint32_t>(fuchsia::sysmem::ColorSpaceType::REC601_PAL);
format_constraints[0].setColorSpaceCount(color_spaces_601.size());
format_constraints[0].setPColorSpaces(color_spaces_601.data());
vk::SysmemColorSpaceFUCHSIA color_space_709;
color_space_709.colorSpace = static_cast<uint32_t>(fuchsia::sysmem::ColorSpaceType::REC709);
format_constraints[1].setColorSpaceCount(1);
format_constraints[1].setPColorSpaces(&color_space_709);
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = format_constraints.data();
constraints_info.formatConstraintsCount = format_constraints.size();
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collections[0], constraints_info, loader_));
constraints_info.pFormatConstraints = &format_constraints[1];
constraints_info.formatConstraintsCount = 1;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collections[1], constraints_info, loader_));
vk::BufferCollectionPropertiesFUCHSIA properties;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collections[0], &properties, loader_));
EXPECT_EQ(1u, properties.createInfoIndex);
}
TEST_P(VulkanImageExtensionTest, WeirdFormat) {
ASSERT_TRUE(Initialize());
// TODO(https://fxbug.dev/42137913): Enable the test when YUV sysmem images are
// supported on emulators.
// TODO(https://fxbug.dev/321072153): Enable the test when YUV sysmem images are
// supported on Lavapipe.
if (!SupportsSysmemYuv())
GTEST_SKIP();
auto [token] = MakeSharedCollection<1>();
bool linear = GetParam();
auto nv12_create_info = GetDefaultImageCreateInfo(false, VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,
kDefaultWidth, kDefaultHeight, linear);
// Currently there's no sysmem format corresponding to R16G16B16, so this format should just be
// ignored.
auto rgb16_create_info = GetDefaultImageCreateInfo(false, VK_FORMAT_R16G16B16_SSCALED,
kDefaultWidth, kDefaultHeight, linear);
vk::BufferCollectionCreateInfoFUCHSIA import_info(token.Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
std::array<vk::ImageFormatConstraintsInfoFUCHSIA, 2> format_constraints = {
GetDefaultRgbImageFormatConstraintsInfo(),
GetDefaultYuvImageFormatConstraintsInfo(),
};
format_constraints[0].imageCreateInfo = rgb16_create_info;
format_constraints[1].imageCreateInfo = nv12_create_info;
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = format_constraints.data();
constraints_info.formatConstraintsCount = format_constraints.size();
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collection, constraints_info, loader_));
vk::BufferCollectionPropertiesFUCHSIA properties;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collection, &properties, loader_));
EXPECT_EQ(1u, properties.createInfoIndex);
}
TEST_P(VulkanImageExtensionTest, NoValidFormat) {
ASSERT_TRUE(Initialize());
auto [token] = MakeSharedCollection<1>();
bool linear = GetParam();
auto rgb16_create_info = GetDefaultImageCreateInfo(false, VK_FORMAT_R16G16B16_SSCALED,
kDefaultWidth, kDefaultHeight, linear);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = rgb16_create_info;
vk::BufferCollectionCreateInfoFUCHSIA import_info(token.Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = &format_constraints;
constraints_info.formatConstraintsCount = 1;
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
// Currently there's no sysmem format corresponding to R16G16B16, so this should return an error
// since no input format is valid.
EXPECT_EQ(vk::Result::eErrorFormatNotSupported,
ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(*collection,
constraints_info, loader_));
}
INSTANTIATE_TEST_SUITE_P(, VulkanImageExtensionTest, ::testing::Bool(),
[](testing::TestParamInfo<bool> info) {
return info.param ? "Linear" : "Tiled";
});
// Check that linear and optimal images are compatible with each other.
TEST_F(VulkanExtensionTest, LinearOptimalCompatible) {
ASSERT_TRUE(Initialize());
auto tokens = MakeSharedCollection(2u);
auto linear_create_info =
GetDefaultImageCreateInfo(false, kDefaultFormat, kDefaultWidth, kDefaultHeight, true);
auto optimal_create_info =
GetDefaultImageCreateInfo(false, kDefaultFormat, kDefaultWidth, kDefaultHeight, false);
std::vector<UniqueBufferCollection> collections;
for (uint32_t i = 0; i < 2; i++) {
vk::BufferCollectionCreateInfoFUCHSIA import_info(tokens[i].Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = i == 0 ? linear_create_info : optimal_create_info;
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = &format_constraints;
constraints_info.formatConstraintsCount = 1;
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collection, constraints_info, loader_));
collections.push_back(std::move(collection));
}
for (uint32_t i = 0; i < 2; i++) {
// Use the same info as was originally used when setting constraints.
vk::ImageCreateInfo info = i == 0 ? linear_create_info : optimal_create_info;
vk::BufferCollectionImageCreateInfoFUCHSIA image_format_fuchsia;
image_format_fuchsia.collection = *collections[i];
info.pNext = &image_format_fuchsia;
auto [result, vk_image] = ctx_->device()->createImageUnique(info, nullptr);
EXPECT_EQ(result, vk::Result::eSuccess);
vk_image_ = std::move(vk_image);
if (i == 0)
CheckLinearSubresourceLayout(kDefaultFormat, kDefaultWidth);
ASSERT_TRUE(InitializeDirectImageMemory(*collections[i], 1));
vk_device_memory_ = {};
}
}
TEST_F(VulkanExtensionTest, BadRequiredFormatFeatures) {
ASSERT_TRUE(Initialize());
auto [vulkan_token] = MakeSharedCollection<1>();
constexpr VkFormat kFormat = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
constexpr bool kLinear = false;
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultYuvImageFormatConstraintsInfo();
format_constraints.imageCreateInfo =
GetDefaultImageCreateInfo(false, kFormat, kDefaultWidth, kDefaultHeight, kLinear);
format_constraints.requiredFormatFeatures = vk::FormatFeatureFlagBits::eVertexBuffer;
auto properties = ctx_->physical_device().getFormatProperties(vk::Format(kFormat));
if ((properties.linearTilingFeatures & format_constraints.requiredFormatFeatures) ==
format_constraints.requiredFormatFeatures) {
printf("Linear supports format features");
fflush(stdout);
GTEST_SKIP();
return;
}
if ((properties.optimalTilingFeatures & format_constraints.requiredFormatFeatures) ==
format_constraints.requiredFormatFeatures) {
printf("Optimal supports format features");
fflush(stdout);
GTEST_SKIP();
return;
}
vk::BufferCollectionCreateInfoFUCHSIA import_info(vulkan_token.Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = &format_constraints;
constraints_info.formatConstraintsCount = 1;
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
// Creating the constraints should fail because the driver doesn't support the features with
// either linear or optimal.
EXPECT_NE(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collection, constraints_info, loader_));
}
TEST_F(VulkanExtensionTest, BadRequiredFormatFeatures2) {
ASSERT_TRUE(Initialize());
auto [vulkan_token] = MakeSharedCollection<1>();
// TODO(https://fxbug.dev/321072153): Lavapipe doesn't support
// `VK_FORMAT_G8_B8R8_2PLANE_420_UNORM`, so we use RGBA when Lavapipe is detected via
// `UseCpuGpu()`.
const VkFormat kFormat =
!SupportsSysmemYuv() ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
bool is_yuv = kFormat == VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
constexpr bool kLinear = false;
auto image_create_info =
GetDefaultImageCreateInfo(false, kFormat, kDefaultWidth, kDefaultHeight, kLinear);
auto properties = ctx_->physical_device().getFormatProperties(vk::Format(kFormat));
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultImageFormatConstraintsInfo(is_yuv);
format_constraints.requiredFormatFeatures = vk::FormatFeatureFlagBits::eVertexBuffer;
if ((properties.linearTilingFeatures & format_constraints.requiredFormatFeatures) ==
format_constraints.requiredFormatFeatures) {
printf("Linear supports format features");
fflush(stdout);
GTEST_SKIP();
return;
}
if ((properties.optimalTilingFeatures & format_constraints.requiredFormatFeatures) ==
format_constraints.requiredFormatFeatures) {
printf("Optimal supports format features");
fflush(stdout);
GTEST_SKIP();
return;
}
vk::BufferCollectionCreateInfoFUCHSIA import_info(vulkan_token.Unbind().TakeChannel().release());
auto [result, collection] =
ctx_->device()->createBufferCollectionFUCHSIAUnique(import_info, nullptr, loader_);
EXPECT_EQ(result, vk::Result::eSuccess);
std::array<vk::ImageFormatConstraintsInfoFUCHSIA, 2> format_infos{
format_constraints, GetDefaultImageFormatConstraintsInfo(is_yuv)};
format_infos[0].imageCreateInfo = image_create_info;
format_infos[1].imageCreateInfo = image_create_info;
vk::ImageConstraintsInfoFUCHSIA constraints_info;
constraints_info.pFormatConstraints = format_infos.data();
constraints_info.formatConstraintsCount = format_infos.size();
constraints_info.bufferCollectionConstraints.minBufferCount = 1;
// The version with a invalid format feature should fail, but the one with an allowed format
// feature should allow everything to continue.
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->setBufferCollectionImageConstraintsFUCHSIA(
*collection, constraints_info, loader_));
vk::BufferCollectionPropertiesFUCHSIA collection_properties;
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->getBufferCollectionPropertiesFUCHSIA(
*collection, &collection_properties, loader_));
EXPECT_EQ(1u, collection_properties.createInfoIndex);
}
TEST_F(VulkanExtensionTest, BufferCollectionBuffer1024) {
ASSERT_TRUE(Initialize());
ASSERT_TRUE(ExecBuffer(1024));
}
TEST_F(VulkanExtensionTest, BufferCollectionBuffer16384) {
ASSERT_TRUE(Initialize());
ASSERT_TRUE(ExecBuffer(16384));
}
TEST_F(VulkanExtensionTest, ImportAliasing) {
ASSERT_TRUE(Initialize());
constexpr bool kUseProtectedMemory = false;
constexpr bool kUseLinear = true;
constexpr uint32_t kSrcHeight = kDefaultHeight;
constexpr uint32_t kDstHeight = kSrcHeight * 2;
constexpr uint32_t kPattern = 0xaabbccdd;
vk::UniqueImage src_image1, src_image2;
vk::UniqueDeviceMemory src_memory1, src_memory2;
{
auto [vulkan_token] = MakeSharedCollection<1>();
vk::ImageCreateInfo image_create_info = GetDefaultImageCreateInfo(
kUseProtectedMemory, kDefaultFormat, kDefaultWidth, kSrcHeight, kUseLinear);
image_create_info.setUsage(vk::ImageUsageFlagBits::eTransferSrc);
image_create_info.setInitialLayout(vk::ImageLayout::ePreinitialized);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection = CreateVkBufferCollectionForImage(
std::move(vulkan_token), format_constraints,
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuReadOften |
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuWriteOften);
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
std::optional<uint32_t> init_img_memory_result = InitializeDirectImageMemory(*collection);
ASSERT_TRUE(init_img_memory_result);
uint32_t memoryTypeIndex = init_img_memory_result.value();
bool src_is_coherent = IsMemoryTypeCoherent(memoryTypeIndex);
src_image1 = std::move(vk_image_);
src_memory1 = std::move(vk_device_memory_);
WriteLinearImage(src_memory1.get(), src_is_coherent, kDefaultWidth, kSrcHeight, kPattern);
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
ASSERT_TRUE(InitializeDirectImageMemory(*collection));
// src2 is alias of src1
src_image2 = std::move(vk_image_);
src_memory2 = std::move(vk_device_memory_);
}
vk::UniqueImage dst_image;
vk::UniqueDeviceMemory dst_memory;
bool dst_is_coherent;
{
auto [vulkan_token] = MakeSharedCollection<1>();
vk::ImageCreateInfo image_create_info = GetDefaultImageCreateInfo(
kUseProtectedMemory, kDefaultFormat, kDefaultWidth, kDstHeight, kUseLinear);
image_create_info.setUsage(vk::ImageUsageFlagBits::eTransferDst);
image_create_info.setInitialLayout(vk::ImageLayout::ePreinitialized);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection = CreateVkBufferCollectionForImage(
std::move(vulkan_token), format_constraints,
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuReadOften |
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuWriteOften);
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
std::optional<uint32_t> init_img_memory_result = InitializeDirectImageMemory(*collection);
ASSERT_TRUE(init_img_memory_result);
uint32_t memoryTypeIndex = init_img_memory_result.value();
dst_is_coherent = IsMemoryTypeCoherent(memoryTypeIndex);
dst_image = std::move(vk_image_);
dst_memory = std::move(vk_device_memory_);
WriteLinearImage(dst_memory.get(), dst_is_coherent, kDefaultWidth, kDstHeight, 0xffffffff);
}
vk::UniqueCommandPool command_pool;
{
auto info =
vk::CommandPoolCreateInfo().setQueueFamilyIndex(vulkan_context().queue_family_index());
auto result = vulkan_context().device()->createCommandPoolUnique(info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
command_pool = std::move(result.value);
}
std::vector<vk::UniqueCommandBuffer> command_buffers;
{
auto info = vk::CommandBufferAllocateInfo()
.setCommandPool(command_pool.get())
.setLevel(vk::CommandBufferLevel::ePrimary)
.setCommandBufferCount(1);
auto result = vulkan_context().device()->allocateCommandBuffersUnique(info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
command_buffers = std::move(result.value);
}
{
auto info = vk::CommandBufferBeginInfo();
EXPECT_EQ(vk::Result::eSuccess, command_buffers[0]->begin(&info));
}
for (vk::Image image : std::vector<vk::Image>{src_image1.get(), src_image2.get()}) {
auto range = vk::ImageSubresourceRange()
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setLevelCount(1)
.setLayerCount(1);
auto barrier = vk::ImageMemoryBarrier()
.setImage(image)
.setSrcAccessMask(vk::AccessFlagBits::eHostWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferRead)
.setOldLayout(vk::ImageLayout::ePreinitialized)
.setNewLayout(vk::ImageLayout::eTransferSrcOptimal)
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eHost, /* srcStageMask */
vk::PipelineStageFlagBits::eTransfer, /* dstStageMask */
vk::DependencyFlags{}, 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
0 /* bufferMemoryBarrierCount */, nullptr /* pBufferMemoryBarriers */,
1 /* imageMemoryBarrierCount */, &barrier);
}
{
auto range = vk::ImageSubresourceRange()
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setLevelCount(1)
.setLayerCount(1);
auto barrier = vk::ImageMemoryBarrier()
.setImage(dst_image.get())
.setSrcAccessMask(vk::AccessFlagBits::eHostWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
.setOldLayout(vk::ImageLayout::ePreinitialized)
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eHost, /* srcStageMask */
vk::PipelineStageFlagBits::eTransfer, /* dstStageMask */
vk::DependencyFlags{}, 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
0 /* bufferMemoryBarrierCount */, nullptr /* pBufferMemoryBarriers */,
1 /* imageMemoryBarrierCount */, &barrier);
}
{
auto layer = vk::ImageSubresourceLayers()
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setLayerCount(1);
auto copy1 = vk::ImageCopy()
.setSrcSubresource(layer)
.setDstSubresource(layer)
.setSrcOffset({0, 0, 0})
.setDstOffset({0, 0, 0})
.setExtent({kDefaultWidth, kSrcHeight, 1});
command_buffers[0]->copyImage(src_image1.get(), vk::ImageLayout::eTransferSrcOptimal,
dst_image.get(), vk::ImageLayout::eTransferDstOptimal, 1, &copy1);
auto copy2 = vk::ImageCopy()
.setSrcSubresource(layer)
.setDstSubresource(layer)
.setSrcOffset({0, 0, 0})
.setDstOffset({0, kSrcHeight, 0})
.setExtent({kDefaultWidth, kSrcHeight, 1});
command_buffers[0]->copyImage(src_image2.get(), vk::ImageLayout::eTransferSrcOptimal,
dst_image.get(), vk::ImageLayout::eTransferDstOptimal, 1, &copy2);
}
{
auto range = vk::ImageSubresourceRange()
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setLevelCount(1)
.setLayerCount(1);
auto barrier = vk::ImageMemoryBarrier()
.setImage(dst_image.get())
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eHostRead)
.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
.setNewLayout(vk::ImageLayout::eGeneral)
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer, /* srcStageMask */
vk::PipelineStageFlagBits::eHost, /* dstStageMask */
vk::DependencyFlags{}, 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
0 /* bufferMemoryBarrierCount */, nullptr /* pBufferMemoryBarriers */,
1 /* imageMemoryBarrierCount */, &barrier);
}
EXPECT_EQ(vk::Result::eSuccess, command_buffers[0]->end());
{
auto command_buffer_temp = command_buffers[0].get();
auto info = vk::SubmitInfo().setCommandBufferCount(1).setPCommandBuffers(&command_buffer_temp);
EXPECT_EQ(vk::Result::eSuccess, vulkan_context().queue().submit(1, &info, vk::Fence()));
}
EXPECT_EQ(vk::Result::eSuccess, vulkan_context().queue().waitIdle());
CheckLinearImage(dst_memory.get(), dst_is_coherent, kDefaultWidth, kDstHeight, kPattern);
}
class VulkanFormatTest : public VulkanExtensionTest,
public ::testing::WithParamInterface<VkFormat> {};
TEST(ByteOffsetCalculation, YTiling) {
// In pixels. 2 tiles by 2 tiles.
constexpr size_t kWidth = 256 / 4;
constexpr size_t kHeight = 64;
std::vector<uint32_t> tile_data(4096 * 2 * 2);
fuchsia::sysmem::BufferCollectionInfo_2 info;
info.settings.has_image_format_constraints = true;
auto &image_format_constraints = info.settings.image_format_constraints;
image_format_constraints.pixel_format.format_modifier.value =
fuchsia::sysmem::FORMAT_MODIFIER_INTEL_I915_Y_TILED;
image_format_constraints.bytes_per_row_divisor = 256;
for (size_t y = 0; y < kHeight; y++) {
for (size_t x = 0; x < kWidth; x++) {
size_t offset = GetImageByteOffset(x, y, info, kWidth, kHeight);
EXPECT_EQ(offset % 4, 0u);
tile_data[offset]++;
}
}
// Every pixel should be returned once.
for (size_t i = 0; i < tile_data.size(); i += 4) {
EXPECT_EQ(tile_data[i], 1u);
}
EXPECT_EQ(0u, GetImageByteOffset(0, 0, info, kWidth, kHeight));
constexpr uint32_t kOWordSize = 16;
// Spot check that (0, 1) starts the next OWord after (0, 0).
EXPECT_EQ(kOWordSize, GetImageByteOffset(0, 1, info, kWidth, kHeight));
// Spot check that (4, 0) (the beginning of the next OWord horizontally) occurs after all 32 rows.
EXPECT_EQ(32u * kOWordSize, GetImageByteOffset(kOWordSize / 4, 0, info, kWidth, kHeight));
}
// Test that any fast clears are resolved by a foreign queue transition.
TEST_P(VulkanFormatTest, FastClear) {
ASSERT_TRUE(Initialize());
// This test reuqests a sysmem image with linear tiling and color attachment
// usage, which is not supported by FEMU. So we skip this test on FEMU.
//
// TODO(fxbug.com/100837): Instead of skipping the test on specific platforms,
// we should check if the features needed (i.e. tiled image of specific
// formats, or linear image with some specific usages) are supported by all
// the sysmem clients. Sysmem should send better error messages and we could
// use this to determine if the test should be skipped due to unsupported
// platforms.
if (!SupportsSysmemRenderableLinear()) {
GTEST_SKIP();
}
constexpr bool kUseProtectedMemory = false;
constexpr bool kUseLinear = false;
constexpr uint32_t kPattern = 0xaabbccdd;
const VkFormat format = GetParam();
vk::UniqueImage image;
vk::UniqueDeviceMemory memory;
fuchsia::sysmem::BufferCollectionInfo_2 sysmem_collection;
bool src_is_coherent;
{
auto [vulkan_token, local_token] = MakeSharedCollection<2>();
vk::ImageCreateInfo image_create_info = GetDefaultImageCreateInfo(
kUseProtectedMemory, format, kDefaultWidth, kDefaultHeight, kUseLinear);
image_create_info.setUsage(vk::ImageUsageFlagBits::eColorAttachment |
vk::ImageUsageFlagBits::eTransferDst);
image_create_info.setInitialLayout(vk::ImageLayout::ePreinitialized);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.requiredFormatFeatures |= vk::FormatFeatureFlagBits::eColorAttachment;
format_constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection = CreateVkBufferCollectionForImage(
std::move(vulkan_token), format_constraints,
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuReadOften |
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuWriteOften);
fuchsia::sysmem::BufferCollectionConstraints constraints;
constraints.usage.cpu = fuchsia::sysmem::cpuUsageRead;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints.cpu_domain_supported = true;
constraints.buffer_memory_constraints.ram_domain_supported = true;
constraints.image_format_constraints_count = 2;
{
// Intel needs Y or YF tiling to do a fast clear.
auto &image_constraints = constraints.image_format_constraints[0];
image_constraints.pixel_format.type = fuchsia::sysmem::PixelFormatType::R8G8B8A8;
image_constraints.pixel_format.has_format_modifier = true;
image_constraints.pixel_format.format_modifier.value =
fuchsia::sysmem::FORMAT_MODIFIER_INTEL_I915_Y_TILED;
image_constraints.color_spaces_count = 1;
image_constraints.color_space[0].type = fuchsia::sysmem::ColorSpaceType::SRGB;
}
{
auto &image_constraints = constraints.image_format_constraints[1];
image_constraints.pixel_format.type = fuchsia::sysmem::PixelFormatType::R8G8B8A8;
image_constraints.pixel_format.has_format_modifier = true;
image_constraints.pixel_format.format_modifier.value =
fuchsia::sysmem::FORMAT_MODIFIER_LINEAR;
image_constraints.color_spaces_count = 1;
image_constraints.color_space[0].type = fuchsia::sysmem::ColorSpaceType::SRGB;
}
sysmem_collection = AllocateSysmemCollection(constraints, std::move(local_token));
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
std::optional<uint32_t> init_img_memory_result = InitializeDirectImageMemory(*collection);
ASSERT_TRUE(init_img_memory_result);
uint32_t memoryTypeIndex = init_img_memory_result.value();
src_is_coherent = IsMemoryTypeCoherent(memoryTypeIndex);
image = std::move(vk_image_);
memory = std::move(vk_device_memory_);
WriteLinearImage(memory.get(), src_is_coherent, kDefaultWidth, kDefaultHeight, kPattern);
}
vk::UniqueCommandPool command_pool;
{
auto info =
vk::CommandPoolCreateInfo().setQueueFamilyIndex(vulkan_context().queue_family_index());
auto result = vulkan_context().device()->createCommandPoolUnique(info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
command_pool = std::move(result.value);
}
std::vector<vk::UniqueCommandBuffer> command_buffers;
{
auto info = vk::CommandBufferAllocateInfo()
.setCommandPool(command_pool.get())
.setLevel(vk::CommandBufferLevel::ePrimary)
.setCommandBufferCount(1);
auto result = vulkan_context().device()->allocateCommandBuffersUnique(info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
command_buffers = std::move(result.value);
}
{
auto info = vk::CommandBufferBeginInfo();
EXPECT_EQ(vk::Result::eSuccess, command_buffers[0]->begin(&info));
}
vk::UniqueRenderPass render_pass;
{
std::array<vk::AttachmentDescription, 1> attachments;
auto &color_attachment = attachments[0];
color_attachment.format = static_cast<vk::Format>(format);
color_attachment.initialLayout = vk::ImageLayout::ePreinitialized;
color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
color_attachment.samples = vk::SampleCountFlagBits::e1;
color_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
color_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
color_attachment.storeOp = vk::AttachmentStoreOp::eStore;
color_attachment.finalLayout = vk::ImageLayout::eColorAttachmentOptimal;
vk::AttachmentReference color_attachment_ref;
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
vk::SubpassDescription subpass;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment_ref;
subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
vk::RenderPassCreateInfo render_pass_info;
render_pass_info.attachmentCount = 1;
render_pass_info.pAttachments = &color_attachment;
render_pass_info.pSubpasses = &subpass;
render_pass_info.subpassCount = 1;
auto result = vulkan_context().device()->createRenderPassUnique(render_pass_info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
render_pass = std::move(result.value);
}
vk::UniqueImageView image_view;
{
vk::ImageSubresourceRange range;
range.aspectMask = vk::ImageAspectFlagBits::eColor;
range.layerCount = 1;
range.levelCount = 1;
vk::ImageViewCreateInfo info;
info.image = *image;
info.viewType = vk::ImageViewType::e2D;
info.format = static_cast<vk::Format>(format);
info.subresourceRange = range;
auto result = vulkan_context().device()->createImageViewUnique(info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
image_view = std::move(result.value);
}
vk::UniqueFramebuffer frame_buffer;
{
vk::FramebufferCreateInfo create_info;
create_info.renderPass = *render_pass;
create_info.attachmentCount = 1;
std::array<vk::ImageView, 1> attachments{*image_view};
create_info.setAttachments(attachments);
create_info.width = kDefaultWidth;
create_info.height = kDefaultHeight;
create_info.layers = 1;
auto result = vulkan_context().device()->createFramebufferUnique(create_info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
frame_buffer = std::move(result.value);
}
vk::RenderPassBeginInfo render_pass_info;
vk::ClearValue clear_color;
clear_color.color = std::array<float, 4>{1.0f, 1.0f, 1.0f, 1.0f};
render_pass_info.renderPass = *render_pass;
render_pass_info.renderArea =
vk::Rect2D(0 /* offset */, vk::Extent2D(kDefaultWidth, kDefaultHeight));
render_pass_info.clearValueCount = 1;
render_pass_info.pClearValues = &clear_color;
render_pass_info.framebuffer = *frame_buffer;
// Clears and stores the framebuffer.
command_buffers[0]->beginRenderPass(render_pass_info, vk::SubpassContents::eInline);
command_buffers[0]->endRenderPass();
{
auto range = vk::ImageSubresourceRange()
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setLevelCount(1)
.setLayerCount(1);
// TODO(https://fxbug.dev/42174999): Test transitioning to
// VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL. That's broken with SRGB on the
// current version of Mesa.
auto barrier = vk::ImageMemoryBarrier()
.setImage(image.get())
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentRead |
vk::AccessFlagBits::eColorAttachmentWrite)
.setOldLayout(vk::ImageLayout::eColorAttachmentOptimal)
.setNewLayout(vk::ImageLayout::eGeneral)
.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_FOREIGN_EXT)
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eColorAttachmentOutput, /* srcStageMask */
vk::PipelineStageFlagBits::eColorAttachmentOutput, /* dstStageMask */
vk::DependencyFlagBits::eByRegion, 0 /* memoryBarrierCount */,
nullptr /* pMemoryBarriers */, 0 /* bufferMemoryBarrierCount */,
nullptr /* pBufferMemoryBarriers */, 1 /* imageMemoryBarrierCount */, &barrier);
}
EXPECT_EQ(vk::Result::eSuccess, command_buffers[0]->end());
{
auto command_buffer_temp = command_buffers[0].get();
auto info = vk::SubmitInfo().setCommandBufferCount(1).setPCommandBuffers(&command_buffer_temp);
EXPECT_EQ(vk::Result::eSuccess, vulkan_context().queue().submit(1, &info, vk::Fence()));
}
EXPECT_EQ(vk::Result::eSuccess, vulkan_context().queue().waitIdle());
ASSERT_TRUE(sysmem_collection.settings.has_image_format_constraints);
{
void *addr;
vk::Result result = ctx_->device()->mapMemory(*memory, 0 /* offset */, VK_WHOLE_SIZE,
vk::MemoryMapFlags{}, &addr);
ASSERT_EQ(vk::Result::eSuccess, result);
if (!src_is_coherent) {
auto range = vk::MappedMemoryRange().setMemory(*memory).setSize(VK_WHOLE_SIZE);
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->invalidateMappedMemoryRanges(1, &range));
}
CheckImageFill(kDefaultWidth, kDefaultHeight, addr, sysmem_collection, 0xffffffff);
ctx_->device()->unmapMemory(*memory);
}
}
// Test on UNORM and SRGB, because on older Intel devices UNORM supports CCS_E, but SRGB only
// supports CCS_D.
INSTANTIATE_TEST_SUITE_P(, VulkanFormatTest,
::testing::Values(VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB),
[](const testing::TestParamInfo<VulkanFormatTest::ParamType> &info) {
return vk::to_string(static_cast<vk::Format>(info.param));
});
// Test copying through an optimal format, including importing images at a
// smaller size than the constraints set on the buffer collection.
TEST_F(VulkanExtensionTest, OptimalCopy) {
ASSERT_TRUE(Initialize());
constexpr bool kUseProtectedMemory = false;
constexpr uint32_t kPattern = 0xaabbccdd;
vk::UniqueImage src_image;
vk::UniqueDeviceMemory src_memory;
bool src_is_coherent;
{
auto [vulkan_token] = MakeSharedCollection<1>();
constexpr bool kUseLinear = true;
vk::ImageCreateInfo image_create_info = GetDefaultImageCreateInfo(
kUseProtectedMemory, kDefaultFormat, kDefaultWidth, kDefaultHeight, kUseLinear);
image_create_info.setUsage(vk::ImageUsageFlagBits::eTransferSrc);
image_create_info.setInitialLayout(vk::ImageLayout::ePreinitialized);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection = CreateVkBufferCollectionForImage(
std::move(vulkan_token), format_constraints,
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuReadOften |
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuWriteOften);
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
std::optional<uint32_t> init_img_memory_result = InitializeDirectImageMemory(*collection);
ASSERT_TRUE(init_img_memory_result);
uint32_t memoryTypeIndex = init_img_memory_result.value();
src_is_coherent = IsMemoryTypeCoherent(memoryTypeIndex);
src_image = std::move(vk_image_);
src_memory = std::move(vk_device_memory_);
WriteLinearImage(src_memory.get(), src_is_coherent, kDefaultWidth, kDefaultHeight, kPattern);
}
vk::UniqueImage mid_image1, mid_image2;
vk::UniqueDeviceMemory mid_memory1, mid_memory2;
// Create a buffer collection and import it twice, once as mid_image1 and once
// as mid_image2. The two different VkBufferCollections will have different
// (larger) size constraints then the images.
{
auto [vulkan_token1, vulkan_token2] = MakeSharedCollection<2>();
constexpr bool kUseLinear = false;
UniqueBufferCollection collection1;
UniqueBufferCollection collection2;
{
vk::ImageCreateInfo image_create_info = GetDefaultImageCreateInfo(
kUseProtectedMemory, kDefaultFormat, kDefaultWidth * 2, kDefaultHeight * 2, kUseLinear);
image_create_info.setUsage(vk::ImageUsageFlagBits::eTransferDst);
image_create_info.setInitialLayout(vk::ImageLayout::ePreinitialized);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = image_create_info;
collection1 = CreateVkBufferCollectionForImage(
std::move(vulkan_token1), format_constraints,
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuReadOften |
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuWriteOften);
}
{
vk::ImageCreateInfo image_create_info =
GetDefaultImageCreateInfo(kUseProtectedMemory, kDefaultFormat, kDefaultWidth * 3 / 2,
kDefaultHeight * 3 / 2, kUseLinear);
image_create_info.setUsage(vk::ImageUsageFlagBits::eTransferSrc);
image_create_info.setInitialLayout(vk::ImageLayout::ePreinitialized);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = image_create_info;
collection2 = CreateVkBufferCollectionForImage(
std::move(vulkan_token2), format_constraints,
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuReadOften |
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuWriteOften);
}
vk::ImageCreateInfo real_image_create_info = GetDefaultImageCreateInfo(
kUseProtectedMemory, kDefaultFormat, kDefaultWidth, kDefaultHeight, kUseLinear);
real_image_create_info.setUsage(vk::ImageUsageFlagBits::eTransferDst);
real_image_create_info.setInitialLayout(vk::ImageLayout::ePreinitialized);
{
ASSERT_TRUE(InitializeDirectImage(*collection1, real_image_create_info));
std::optional<uint32_t> init_img_memory_result = InitializeDirectImageMemory(*collection1);
ASSERT_TRUE(init_img_memory_result);
uint32_t memoryTypeIndex = init_img_memory_result.value();
bool mid_is_coherent = IsMemoryTypeCoherent(memoryTypeIndex);
mid_image1 = std::move(vk_image_);
mid_memory1 = std::move(vk_device_memory_);
WriteLinearImage(mid_memory1.get(), mid_is_coherent, kDefaultWidth, kDefaultHeight,
0xffffffff);
}
{
real_image_create_info.setUsage(vk::ImageUsageFlagBits::eTransferSrc);
ASSERT_TRUE(InitializeDirectImage(*collection2, real_image_create_info));
std::optional<uint32_t> init_img_memory_result = InitializeDirectImageMemory(*collection1);
ASSERT_TRUE(init_img_memory_result);
mid_image2 = std::move(vk_image_);
mid_memory2 = std::move(vk_device_memory_);
}
}
vk::UniqueImage dst_image;
vk::UniqueDeviceMemory dst_memory;
bool dst_is_coherent;
{
auto [vulkan_token] = MakeSharedCollection<1>();
constexpr bool kUseLinear = true;
vk::ImageCreateInfo image_create_info = GetDefaultImageCreateInfo(
kUseProtectedMemory, kDefaultFormat, kDefaultWidth, kDefaultHeight, kUseLinear);
image_create_info.setUsage(vk::ImageUsageFlagBits::eTransferDst);
image_create_info.setInitialLayout(vk::ImageLayout::ePreinitialized);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection = CreateVkBufferCollectionForImage(
std::move(vulkan_token), format_constraints,
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuReadOften |
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuWriteOften);
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
std::optional<uint32_t> init_img_memory_result = InitializeDirectImageMemory(*collection);
ASSERT_TRUE(init_img_memory_result);
uint32_t memoryTypeIndex = init_img_memory_result.value();
dst_is_coherent = IsMemoryTypeCoherent(memoryTypeIndex);
dst_image = std::move(vk_image_);
dst_memory = std::move(vk_device_memory_);
WriteLinearImage(dst_memory.get(), dst_is_coherent, kDefaultWidth, kDefaultHeight, 0xffffffff);
}
auto range = vk::ImageSubresourceRange()
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setLevelCount(1)
.setLayerCount(1);
auto layer =
vk::ImageSubresourceLayers().setAspectMask(vk::ImageAspectFlagBits::eColor).setLayerCount(1);
vk::UniqueCommandPool command_pool;
{
auto info =
vk::CommandPoolCreateInfo().setQueueFamilyIndex(vulkan_context().queue_family_index());
auto result = vulkan_context().device()->createCommandPoolUnique(info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
command_pool = std::move(result.value);
}
std::vector<vk::UniqueCommandBuffer> command_buffers;
{
auto info = vk::CommandBufferAllocateInfo()
.setCommandPool(command_pool.get())
.setLevel(vk::CommandBufferLevel::ePrimary)
.setCommandBufferCount(1);
auto result = vulkan_context().device()->allocateCommandBuffersUnique(info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
command_buffers = std::move(result.value);
}
{
auto info = vk::CommandBufferBeginInfo();
EXPECT_EQ(vk::Result::eSuccess, command_buffers[0]->begin(&info));
}
// transition src_image to be readable by transfer.
{
auto barrier = vk::ImageMemoryBarrier()
.setImage(src_image.get())
.setSrcAccessMask(vk::AccessFlagBits::eHostWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferRead)
.setOldLayout(vk::ImageLayout::ePreinitialized)
.setNewLayout(vk::ImageLayout::eTransferSrcOptimal)
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eHost, /* srcStageMask */
vk::PipelineStageFlagBits::eTransfer, /* dstStageMask */
vk::DependencyFlags{}, 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
0 /* bufferMemoryBarrierCount */, nullptr /* pBufferMemoryBarriers */,
1 /* imageMemoryBarrierCount */, &barrier);
}
// transition mid_image1 to be readable by transfer.
{
auto barrier = vk::ImageMemoryBarrier()
.setImage(mid_image1.get())
.setSrcAccessMask(vk::AccessFlagBits::eHostWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
.setOldLayout(vk::ImageLayout::ePreinitialized)
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eHost, /* srcStageMask */
vk::PipelineStageFlagBits::eTransfer, /* dstStageMask */
vk::DependencyFlags{}, 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
0 /* bufferMemoryBarrierCount */, nullptr /* pBufferMemoryBarriers */,
1 /* imageMemoryBarrierCount */, &barrier);
}
{
auto copy = vk::ImageCopy()
.setSrcSubresource(layer)
.setDstSubresource(layer)
.setSrcOffset({0, 0, 0})
.setDstOffset({0, 0, 0})
.setExtent({kDefaultWidth, kDefaultHeight, 1});
command_buffers[0]->copyImage(src_image.get(), vk::ImageLayout::eTransferSrcOptimal,
mid_image1.get(), vk::ImageLayout::eTransferDstOptimal, copy);
}
// Do a transfer of mid_image1 to the foreign queue family.
{
auto barrier = vk::ImageMemoryBarrier()
.setImage(mid_image1.get())
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask({})
.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
.setSrcQueueFamilyIndex(ctx_->queue_family_index())
.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_FOREIGN_EXT)
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer, /* srcStageMask */
vk::PipelineStageFlagBits::eTransfer, /* dstStageMask */
vk::DependencyFlags{}, 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
0 /* bufferMemoryBarrierCount */, nullptr /* pBufferMemoryBarriers */,
1 /* imageMemoryBarrierCount */, &barrier);
}
// Do a transfer of mid_image2 to the foreign queue family.
{
auto barrier = vk::ImageMemoryBarrier()
.setImage(mid_image2.get())
.setSrcAccessMask({})
.setDstAccessMask(vk::AccessFlagBits::eTransferRead)
.setOldLayout(vk::ImageLayout::eTransferSrcOptimal)
.setNewLayout(vk::ImageLayout::eTransferSrcOptimal)
.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_FOREIGN_EXT)
.setDstQueueFamilyIndex(ctx_->queue_family_index())
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer, /* srcStageMask */
vk::PipelineStageFlagBits::eTransfer, /* dstStageMask */
vk::DependencyFlags{}, 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
0 /* bufferMemoryBarrierCount */, nullptr /* pBufferMemoryBarriers */,
1 /* imageMemoryBarrierCount */, &barrier);
}
// Transition dst_image to be writable by transfer stage.
{
auto barrier = vk::ImageMemoryBarrier()
.setImage(dst_image.get())
.setSrcAccessMask(vk::AccessFlagBits::eHostWrite)
.setDstAccessMask(vk::AccessFlagBits::eTransferWrite)
.setOldLayout(vk::ImageLayout::ePreinitialized)
.setNewLayout(vk::ImageLayout::eTransferDstOptimal)
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eHost, /* srcStageMask */
vk::PipelineStageFlagBits::eTransfer, /* dstStageMask */
vk::DependencyFlags{}, 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
0 /* bufferMemoryBarrierCount */, nullptr /* pBufferMemoryBarriers */,
1 /* imageMemoryBarrierCount */, &barrier);
}
{
auto copy2 = vk::ImageCopy()
.setSrcSubresource(layer)
.setDstSubresource(layer)
.setSrcOffset({0, 0, 0})
.setDstOffset({0, 0, 0})
.setExtent({kDefaultWidth, kDefaultHeight, 1});
command_buffers[0]->copyImage(mid_image2.get(), vk::ImageLayout::eTransferSrcOptimal,
dst_image.get(), vk::ImageLayout::eTransferDstOptimal, 1, &copy2);
}
// Transition dst image to be readable on the CPU.
{
auto barrier = vk::ImageMemoryBarrier()
.setImage(dst_image.get())
.setSrcAccessMask(vk::AccessFlagBits::eTransferWrite)
.setDstAccessMask(vk::AccessFlagBits::eHostRead)
.setOldLayout(vk::ImageLayout::eTransferDstOptimal)
.setNewLayout(vk::ImageLayout::eGeneral)
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eTransfer, /* srcStageMask */
vk::PipelineStageFlagBits::eHost, /* dstStageMask */
vk::DependencyFlags{}, 0 /* memoryBarrierCount */, nullptr /* pMemoryBarriers */,
0 /* bufferMemoryBarrierCount */, nullptr /* pBufferMemoryBarriers */,
1 /* imageMemoryBarrierCount */, &barrier);
}
EXPECT_EQ(vk::Result::eSuccess, command_buffers[0]->end());
{
auto command_buffer_temp = command_buffers[0].get();
auto info = vk::SubmitInfo().setCommandBufferCount(1).setPCommandBuffers(&command_buffer_temp);
EXPECT_EQ(vk::Result::eSuccess, vulkan_context().queue().submit(1, &info, vk::Fence()));
}
EXPECT_EQ(vk::Result::eSuccess, vulkan_context().queue().waitIdle());
CheckLinearImage(dst_memory.get(), dst_is_coherent, kDefaultWidth, kDefaultHeight, kPattern);
}
// Test that the correct pixels are written to with linear images with non-packed strides.
TEST_F(VulkanExtensionTest, LinearNonPackedStride) {
ASSERT_TRUE(Initialize());
if (!SupportsSysmemRenderableLinear()) {
GTEST_SKIP();
}
constexpr bool kUseProtectedMemory = false;
constexpr uint32_t kPattern = 0xaabbccdd;
constexpr size_t kBytesPerPixel = 4;
vk::UniqueImage image;
vk::UniqueDeviceMemory memory;
bool src_is_coherent;
fuchsia::sysmem::BufferCollectionInfo_2 sysmem_collection;
{
auto [vulkan_token, sysmem_token] = MakeSharedCollection<2>();
constexpr bool kUseLinear = true;
vk::ImageCreateInfo image_create_info = GetDefaultImageCreateInfo(
kUseProtectedMemory, kDefaultFormat, kDefaultWidth, kDefaultHeight, kUseLinear);
image_create_info.setUsage(vk::ImageUsageFlagBits::eTransferSrc |
vk::ImageUsageFlagBits::eColorAttachment);
image_create_info.setInitialLayout(vk::ImageLayout::ePreinitialized);
vk::ImageFormatConstraintsInfoFUCHSIA format_constraints =
GetDefaultRgbImageFormatConstraintsInfo();
format_constraints.imageCreateInfo = image_create_info;
UniqueBufferCollection collection = CreateVkBufferCollectionForImage(
std::move(vulkan_token), format_constraints,
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuReadOften |
vk::ImageConstraintsInfoFlagBitsFUCHSIA::eCpuWriteOften);
fuchsia::sysmem::ImageFormatConstraints bgra_image_constraints;
bgra_image_constraints.required_min_coded_width = 64;
bgra_image_constraints.required_min_coded_height = 64;
bgra_image_constraints.required_max_coded_width = 64;
bgra_image_constraints.required_max_coded_height = 64;
bgra_image_constraints.max_coded_width = 8192;
bgra_image_constraints.max_coded_height = 8192;
bgra_image_constraints.max_bytes_per_row = 0xffffffff;
bgra_image_constraints.bytes_per_row_divisor = 1024;
bgra_image_constraints.pixel_format.type = fuchsia::sysmem::PixelFormatType::R8G8B8A8;
bgra_image_constraints.pixel_format.has_format_modifier = true;
bgra_image_constraints.pixel_format.format_modifier.value =
fuchsia::sysmem::FORMAT_MODIFIER_LINEAR;
bgra_image_constraints.color_spaces_count = 1;
bgra_image_constraints.color_space[0].type = fuchsia::sysmem::ColorSpaceType::SRGB;
EXPECT_LT(kDefaultWidth * 4, bgra_image_constraints.bytes_per_row_divisor);
fuchsia::sysmem::BufferCollectionConstraints constraints;
constraints.usage.cpu = fuchsia::sysmem::cpuUsageRead;
constraints.has_buffer_memory_constraints = true;
constraints.buffer_memory_constraints.cpu_domain_supported = true;
constraints.buffer_memory_constraints.ram_domain_supported = true;
constraints.image_format_constraints_count = 1;
constraints.image_format_constraints[0] = bgra_image_constraints;
sysmem_collection = AllocateSysmemCollection(constraints, std::move(sysmem_token));
ASSERT_TRUE(InitializeDirectImage(*collection, image_create_info));
std::optional<uint32_t> init_img_memory_result = InitializeDirectImageMemory(*collection);
ASSERT_TRUE(init_img_memory_result);
uint32_t memoryTypeIndex = init_img_memory_result.value();
src_is_coherent = IsMemoryTypeCoherent(memoryTypeIndex);
image = std::move(vk_image_);
memory = std::move(vk_device_memory_);
WriteLinearColorImageComplete(memory.get(), image.get(), src_is_coherent, kDefaultWidth,
kDefaultHeight, kPattern);
}
size_t bytes_per_row = fbl::round_up(
std::max(kDefaultWidth * kBytesPerPixel,
static_cast<size_t>(
sysmem_collection.settings.image_format_constraints.min_bytes_per_row)),
sysmem_collection.settings.image_format_constraints.bytes_per_row_divisor);
auto layout = vulkan_context().device()->getImageSubresourceLayout(
image.get(), vk::ImageSubresource(vk::ImageAspectFlagBits::eColor, 0, 0));
EXPECT_EQ(bytes_per_row, layout.rowPitch);
vk::UniqueCommandPool command_pool;
{
auto info =
vk::CommandPoolCreateInfo().setQueueFamilyIndex(vulkan_context().queue_family_index());
auto result = vulkan_context().device()->createCommandPoolUnique(info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
command_pool = std::move(result.value);
}
std::vector<vk::UniqueCommandBuffer> command_buffers;
{
auto info = vk::CommandBufferAllocateInfo()
.setCommandPool(command_pool.get())
.setLevel(vk::CommandBufferLevel::ePrimary)
.setCommandBufferCount(1);
auto result = vulkan_context().device()->allocateCommandBuffersUnique(info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
command_buffers = std::move(result.value);
}
{
auto info = vk::CommandBufferBeginInfo();
EXPECT_EQ(vk::Result::eSuccess, command_buffers[0]->begin(&info));
}
vk::UniqueRenderPass render_pass;
{
std::array<vk::AttachmentDescription, 1> attachments;
auto &color_attachment = attachments[0];
color_attachment.format = static_cast<vk::Format>(kDefaultFormat);
color_attachment.initialLayout = vk::ImageLayout::ePreinitialized;
color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
color_attachment.samples = vk::SampleCountFlagBits::e1;
color_attachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
color_attachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
color_attachment.storeOp = vk::AttachmentStoreOp::eStore;
color_attachment.finalLayout = vk::ImageLayout::eColorAttachmentOptimal;
vk::AttachmentReference color_attachment_ref;
color_attachment_ref.attachment = 0;
color_attachment_ref.layout = vk::ImageLayout::eColorAttachmentOptimal;
vk::SubpassDescription subpass;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_attachment_ref;
subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
vk::RenderPassCreateInfo render_pass_info;
render_pass_info.attachmentCount = 1;
render_pass_info.pAttachments = &color_attachment;
render_pass_info.pSubpasses = &subpass;
render_pass_info.subpassCount = 1;
auto result = vulkan_context().device()->createRenderPassUnique(render_pass_info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
render_pass = std::move(result.value);
}
vk::UniqueImageView image_view;
{
vk::ImageSubresourceRange range;
range.aspectMask = vk::ImageAspectFlagBits::eColor;
range.layerCount = 1;
range.levelCount = 1;
vk::ImageViewCreateInfo info;
info.image = *image;
info.viewType = vk::ImageViewType::e2D;
info.format = static_cast<vk::Format>(kDefaultFormat);
info.subresourceRange = range;
auto result = vulkan_context().device()->createImageViewUnique(info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
image_view = std::move(result.value);
}
vk::UniqueFramebuffer frame_buffer;
{
vk::FramebufferCreateInfo create_info;
create_info.renderPass = *render_pass;
create_info.attachmentCount = 1;
std::array<vk::ImageView, 1> attachments{*image_view};
create_info.setAttachments(attachments);
create_info.width = kDefaultWidth;
create_info.height = kDefaultHeight;
create_info.layers = 1;
auto result = vulkan_context().device()->createFramebufferUnique(create_info);
ASSERT_EQ(vk::Result::eSuccess, result.result);
frame_buffer = std::move(result.value);
}
// Clear everything but the first line (which should stay the same).
vk::RenderPassBeginInfo render_pass_info;
vk::ClearValue clear_color;
clear_color.color = std::array<float, 4>{1.0f, 1.0f, 1.0f, 1.0f};
render_pass_info.renderPass = *render_pass;
render_pass_info.renderArea =
vk::Rect2D(vk::Offset2D(0, 1), vk::Extent2D(kDefaultWidth, kDefaultHeight - 1));
render_pass_info.clearValueCount = 1;
render_pass_info.pClearValues = &clear_color;
render_pass_info.framebuffer = *frame_buffer;
// Clears and stores the framebuffer.
command_buffers[0]->beginRenderPass(render_pass_info, vk::SubpassContents::eInline);
command_buffers[0]->endRenderPass();
{
auto range = vk::ImageSubresourceRange()
.setAspectMask(vk::ImageAspectFlagBits::eColor)
.setLevelCount(1)
.setLayerCount(1);
auto barrier = vk::ImageMemoryBarrier()
.setImage(image.get())
.setSrcAccessMask(vk::AccessFlagBits::eColorAttachmentWrite)
.setDstAccessMask(vk::AccessFlagBits::eColorAttachmentRead |
vk::AccessFlagBits::eColorAttachmentWrite)
.setOldLayout(vk::ImageLayout::eColorAttachmentOptimal)
.setNewLayout(vk::ImageLayout::eGeneral)
.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_FOREIGN_EXT)
.setSubresourceRange(range);
command_buffers[0]->pipelineBarrier(
vk::PipelineStageFlagBits::eColorAttachmentOutput, /* srcStageMask */
vk::PipelineStageFlagBits::eColorAttachmentOutput, /* dstStageMask */
vk::DependencyFlagBits::eByRegion, 0 /* memoryBarrierCount */,
nullptr /* pMemoryBarriers */, 0 /* bufferMemoryBarrierCount */,
nullptr /* pBufferMemoryBarriers */, 1 /* imageMemoryBarrierCount */, &barrier);
}
EXPECT_EQ(vk::Result::eSuccess, command_buffers[0]->end());
{
auto command_buffer_temp = command_buffers[0].get();
auto info = vk::SubmitInfo().setCommandBufferCount(1).setPCommandBuffers(&command_buffer_temp);
EXPECT_EQ(vk::Result::eSuccess, vulkan_context().queue().submit(1, &info, vk::Fence()));
}
EXPECT_EQ(vk::Result::eSuccess, vulkan_context().queue().waitIdle());
ASSERT_TRUE(sysmem_collection.settings.has_image_format_constraints);
{
void *addr;
vk::Result result = ctx_->device()->mapMemory(*memory, 0 /* offset */, VK_WHOLE_SIZE,
vk::MemoryMapFlags{}, &addr);
ASSERT_EQ(vk::Result::eSuccess, result);
if (!src_is_coherent) {
auto range = vk::MappedMemoryRange().setMemory(*memory).setSize(VK_WHOLE_SIZE);
EXPECT_EQ(vk::Result::eSuccess, ctx_->device()->invalidateMappedMemoryRanges(1, &range));
}
uint32_t error_count = 0;
constexpr uint32_t kMaxErrors = 10;
bool skip = false;
for (size_t y = 0; (y < kDefaultHeight) && !skip; y++) {
for (size_t x = 0; (x < kDefaultWidth) && !skip; x++) {
size_t byte_offset =
GetImageByteOffset(x, y, sysmem_collection, kDefaultWidth, kDefaultHeight);
uint32_t *pixel_addr =
reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(addr) + byte_offset);
// The first line should keep the original pattern, but everything else should be filled to
// all 1s. If the row pitch is calculated incorrectly by the driver then it will write to
// the wrong bytes.
uint32_t fill = (y == 0) ? kPattern : 0xffffffff;
EXPECT_EQ(fill, *pixel_addr) << "byte_offset " << byte_offset << " x " << x << " y " << y;
if (*pixel_addr != fill) {
error_count++;
if (error_count > kMaxErrors) {
printf("Skipping reporting remaining errors\n");
skip = true;
}
}
}
}
ctx_->device()->unmapMemory(*memory);
}
}
} // namespace