blob: f3ff20c2ae6b466bbcf1cfdabbf2dbefc401e9ae [file] [log] [blame]
/*
* Copyright (c) 2015-2025 The Khronos Group Inc.
* Copyright (c) 2015-2025 Valve Corporation
* Copyright (c) 2015-2025 LunarG, Inc.
* Copyright (c) 2015-2024 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#include "../framework/layer_validation_tests.h"
#include "../framework/external_memory_sync.h"
#include "utils/math_utils.h"
class PositiveExternalMemorySync : public ExternalMemorySyncTest {};
TEST_F(PositiveExternalMemorySync, GetMemoryFdHandle) {
TEST_DESCRIPTION("Get POXIS handle for memory allocation");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
VkExportMemoryAllocateInfo export_info = vku::InitStructHelper();
export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper(&export_info);
alloc_info.allocationSize = 1024;
alloc_info.memoryTypeIndex = 0;
vkt::DeviceMemory memory(*m_device, alloc_info);
VkMemoryGetFdInfoKHR get_handle_info = vku::InitStructHelper();
get_handle_info.memory = memory;
get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
int fd = -1;
vk::GetMemoryFdKHR(*m_device, &get_handle_info, &fd);
}
TEST_F(PositiveExternalMemorySync, ImportMemoryFd) {
TEST_DESCRIPTION("Basic importing of POXIS handle for memory allocation");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
// Required to pass in various memory flags without querying for corresponding extensions.
AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
IgnoreHandleTypeError(m_errorMonitor);
VkExternalMemoryBufferCreateInfo external_buffer_info = vku::InitStructHelper();
external_buffer_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
auto buffer_info = vkt::Buffer::CreateInfo(1024, VK_BUFFER_USAGE_TRANSFER_DST_BIT, {}, &external_buffer_info);
if (!FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT)) {
GTEST_SKIP() << "Unable to find exportable handle type";
}
if (!FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) {
GTEST_SKIP() << "Unable to find importable handle type";
}
const auto compatible_types = GetCompatibleHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT);
if ((VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT & compatible_types) == 0) {
GTEST_SKIP() << "Cannot find handle types that are supported but not compatible with each other";
}
vkt::Buffer buffer(*m_device, buffer_info, vkt::no_mem);
VkMemoryDedicatedAllocateInfo dedicated_info = vku::InitStructHelper();
dedicated_info.image = VK_NULL_HANDLE;
dedicated_info.buffer = buffer;
VkExportMemoryAllocateInfo export_info = vku::InitStructHelper(&dedicated_info);
export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
auto alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer.MemoryRequirements(), 0, &export_info);
vkt::DeviceMemory memory_export(*m_device, alloc_info);
VkMemoryGetFdInfoKHR mgfi = vku::InitStructHelper();
mgfi.memory = memory_export;
mgfi.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
int fd;
vk::GetMemoryFdKHR(device(), &mgfi, &fd);
VkImportMemoryFdInfoKHR import_info = vku::InitStructHelper(&dedicated_info);
import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
import_info.fd = fd;
alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer.MemoryRequirements(), 0, &import_info);
vkt::DeviceMemory memory_import(*m_device, alloc_info);
}
TEST_F(PositiveExternalMemorySync, ImportMemoryHost) {
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
VkPhysicalDeviceExternalMemoryHostPropertiesEXT memory_host_props = vku::InitStructHelper();
GetPhysicalDeviceProperties2(memory_host_props);
VkDeviceSize alloc_size = memory_host_props.minImportedHostPointerAlignment;
void *host_memory = ::operator new((size_t)alloc_size, std::align_val_t(alloc_size));
if (!host_memory) {
GTEST_SKIP() << "Can't allocate host memory";
}
VkMemoryHostPointerPropertiesEXT host_pointer_props = vku::InitStructHelper();
vk::GetMemoryHostPointerPropertiesEXT(*m_device, VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT, host_memory,
&host_pointer_props);
// test it is ignored when using null handle
VkMemoryDedicatedAllocateInfo dedicated_info = vku::InitStructHelper();
dedicated_info.buffer = VK_NULL_HANDLE;
dedicated_info.image = VK_NULL_HANDLE;
VkImportMemoryHostPointerInfoEXT import_info = vku::InitStructHelper(&dedicated_info);
import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_HOST_ALLOCATION_BIT_EXT;
import_info.pHostPointer = host_memory;
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper(&import_info);
alloc_info.allocationSize = alloc_size;
if (!m_device->Physical().SetMemoryType(host_pointer_props.memoryTypeBits, &alloc_info, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
::operator delete(host_memory, std::align_val_t(alloc_size));
GTEST_SKIP() << "Failed to set memory type.";
}
vkt::DeviceMemory memory_import(*m_device, alloc_info);
::operator delete(host_memory, std::align_val_t(alloc_size));
}
TEST_F(PositiveExternalMemorySync, ExternalMemory) {
TEST_DESCRIPTION("Perform a copy through a pair of buffers linked by external memory");
#ifdef _WIN32
const auto ext_mem_extension_name = VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME;
const auto handle_type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
#else
const auto ext_mem_extension_name = VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME;
const auto handle_type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
#endif
AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
AddRequiredExtensions(ext_mem_extension_name);
RETURN_IF_SKIP(InitFramework());
// Check for import/export capability
VkPhysicalDeviceExternalBufferInfoKHR ebi = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_BUFFER_INFO_KHR, nullptr, 0,
VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, handle_type};
VkExternalBufferPropertiesKHR ebp = vku::InitStructHelper();
vk::GetPhysicalDeviceExternalBufferPropertiesKHR(Gpu(), &ebi, &ebp);
if (!(ebp.externalMemoryProperties.compatibleHandleTypes & handle_type) ||
!(ebp.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT) ||
!(ebp.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) {
GTEST_SKIP() << "External buffer does not support importing and exporting";
}
// Check if dedicated allocation is required
bool dedicated_allocation = ebp.externalMemoryProperties.externalMemoryFeatures & VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT;
if (dedicated_allocation && !IsExtensionsEnabled(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME)) {
GTEST_SKIP() << "Dedicated allocation extension not supported";
}
RETURN_IF_SKIP(InitState());
VkMemoryPropertyFlags mem_flags = 0;
const VkDeviceSize buffer_size = 1024;
// Create export and import buffers
const VkExternalMemoryBufferCreateInfo external_buffer_info = {VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO_KHR,
nullptr, handle_type};
auto buffer_info = vkt::Buffer::CreateInfo(buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT);
buffer_info.pNext = &external_buffer_info;
vkt::Buffer buffer_export(*m_device, buffer_info, vkt::no_mem);
vkt::Buffer buffer_import(*m_device, buffer_info, vkt::no_mem);
// Allocation info
auto alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer_export.MemoryRequirements(), mem_flags);
// Add export allocation info to pNext chain
VkExportMemoryAllocateInfo export_info = {VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR, nullptr, handle_type};
alloc_info.pNext = &export_info;
// Add dedicated allocation info to pNext chain if required
VkMemoryDedicatedAllocateInfo dedicated_info = {VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, nullptr, VK_NULL_HANDLE,
buffer_export.handle()};
if (dedicated_allocation) {
export_info.pNext = &dedicated_info;
}
// Allocate memory to be exported
vkt::DeviceMemory memory_export(*m_device, alloc_info);
// Bind exported memory
buffer_export.BindMemory(memory_export, 0);
#ifdef _WIN32
// Export memory to handle
VkMemoryGetWin32HandleInfoKHR mghi = {VK_STRUCTURE_TYPE_MEMORY_GET_WIN32_HANDLE_INFO_KHR, nullptr, memory_export.handle(),
handle_type};
HANDLE handle;
ASSERT_EQ(VK_SUCCESS, vk::GetMemoryWin32HandleKHR(device(), &mghi, &handle));
VkImportMemoryWin32HandleInfoKHR import_info = {VK_STRUCTURE_TYPE_IMPORT_MEMORY_WIN32_HANDLE_INFO_KHR, nullptr, handle_type,
handle};
#else
// Export memory to fd
VkMemoryGetFdInfoKHR mgfi = {VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, nullptr, memory_export.handle(), handle_type};
int fd;
ASSERT_EQ(VK_SUCCESS, vk::GetMemoryFdKHR(device(), &mgfi, &fd));
VkImportMemoryFdInfoKHR import_info = {VK_STRUCTURE_TYPE_IMPORT_MEMORY_FD_INFO_KHR, nullptr, handle_type, fd};
#endif
// Import
if (dedicated_allocation) {
dedicated_info.buffer = buffer_import;
import_info.pNext = &dedicated_info;
}
alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer_import.MemoryRequirements(), mem_flags);
alloc_info.pNext = &import_info;
vkt::DeviceMemory memory_import(*m_device, alloc_info);
// Bind imported memory
buffer_import.BindMemory(memory_import, 0);
// Create test buffers and fill input buffer
vkt::Buffer buffer_input(*m_device, buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
auto input_mem = (uint8_t*)buffer_input.Memory().Map();
for (uint32_t i = 0; i < buffer_size; i++) {
input_mem[i] = (i & 0xFF);
}
vkt::Buffer buffer_output(*m_device, buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
// Copy from input buffer to output buffer through the exported/imported memory
m_command_buffer.Begin();
VkBufferCopy copy_info = {0, 0, buffer_size};
vk::CmdCopyBuffer(m_command_buffer, buffer_input, buffer_export, 1, &copy_info);
// Insert memory barrier to guarantee copy order
VkMemoryBarrier mem_barrier = {VK_STRUCTURE_TYPE_MEMORY_BARRIER, nullptr, VK_ACCESS_TRANSFER_WRITE_BIT,
VK_ACCESS_TRANSFER_READ_BIT};
vk::CmdPipelineBarrier(m_command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 1, &mem_barrier, 0,
nullptr, 0, nullptr);
vk::CmdCopyBuffer(m_command_buffer, buffer_import, buffer_output, 1, &copy_info);
m_command_buffer.End();
m_default_queue->SubmitAndWait(m_command_buffer);
}
TEST_F(PositiveExternalMemorySync, BufferDedicatedAllocation) {
TEST_DESCRIPTION("Create external buffer that requires dedicated allocation.");
SetTargetApiVersion(VK_API_VERSION_1_1);
// Required to pass in various memory flags without querying for corresponding extensions.
AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
IgnoreHandleTypeError(m_errorMonitor);
VkExternalMemoryBufferCreateInfo external_buffer_info = vku::InitStructHelper();
const auto buffer_info = vkt::Buffer::CreateInfo(4096, VK_BUFFER_USAGE_TRANSFER_DST_BIT, {}, &external_buffer_info);
const auto exportable_types =
FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT);
if (!exportable_types) {
GTEST_SKIP() << "Unable to find exportable handle type";
}
auto exportable_dedicated_types = FindSupportedExternalMemoryHandleTypes(
Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT);
if (!exportable_dedicated_types) {
GTEST_SKIP() << "Unable to find exportable handle type that requires dedicated allocation";
}
const auto handle_type = LeastSignificantFlag<VkExternalMemoryHandleTypeFlagBits>(exportable_dedicated_types);
external_buffer_info.handleTypes = handle_type;
vkt::Buffer buffer(*m_device, buffer_info, vkt::no_mem);
VkMemoryDedicatedAllocateInfo dedicated_info = vku::InitStructHelper();
dedicated_info.buffer = buffer;
VkExportMemoryAllocateInfo export_memory_info = vku::InitStructHelper(&dedicated_info);
export_memory_info.handleTypes = handle_type;
buffer.AllocateAndBindMemory(*m_device, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &export_memory_info);
}
TEST_F(PositiveExternalMemorySync, SyncFdSemaphore) {
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
const auto handle_type = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
VkPhysicalDeviceExternalSemaphoreInfo external_semahpore_info = vku::InitStructHelper();
external_semahpore_info.handleType = handle_type;
VkExternalSemaphoreProperties external_semahpore_props = vku::InitStructHelper();
vk::GetPhysicalDeviceExternalSemaphoreProperties(Gpu(), &external_semahpore_info, &external_semahpore_props);
if (!(external_semahpore_props.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT) ||
!(external_semahpore_props.externalSemaphoreFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT)) {
GTEST_SKIP() << "External semaphore does not support importing and exporting";
}
if (!(external_semahpore_props.compatibleHandleTypes & handle_type)) {
GTEST_SKIP() << "External semaphore does not support VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT";
}
VkExportSemaphoreCreateInfo esci = vku::InitStructHelper();
esci.handleTypes = handle_type;
VkSemaphoreTypeCreateInfo stci = vku::InitStructHelper(&esci);
stci.semaphoreType = VK_SEMAPHORE_TYPE_BINARY;
VkSemaphoreCreateInfo sci = vku::InitStructHelper(&stci);
vkt::Semaphore binary_sem(*m_device, sci);
VkSubmitInfo si = vku::InitStructHelper();
si.signalSemaphoreCount = 1;
si.pSignalSemaphores = &binary_sem.handle();
vk::QueueSubmit(m_default_queue->handle(), 1, &si, VK_NULL_HANDLE);
int fd_handle = -1;
binary_sem.ExportHandle(fd_handle, handle_type);
vkt::Semaphore import_semaphore(*m_device);
import_semaphore.ImportHandle(fd_handle, handle_type, VK_SEMAPHORE_IMPORT_TEMPORARY_BIT);
m_default_queue->Wait();
}
#ifdef VK_USE_PLATFORM_METAL_EXT
TEST_F(PositiveExternalMemorySync, ExportMetalObjects) {
TEST_DESCRIPTION("Test vkExportMetalObjectsEXT");
AddRequiredExtensions(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
AddRequiredExtensions(VK_EXT_METAL_OBJECTS_EXTENSION_NAME);
// Initialize framework
{
VkExportMetalObjectCreateInfoEXT queue_info = vku::InitStructHelper();
queue_info.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_COMMAND_QUEUE_BIT_EXT;
VkExportMetalObjectCreateInfoEXT metal_info = vku::InitStructHelper(&queue_info);
metal_info.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_DEVICE_BIT_EXT;
RETURN_IF_SKIP(InitFramework(&metal_info));
VkPhysicalDevicePortabilitySubsetFeaturesKHR portability_features = vku::InitStructHelper();
auto features2 = GetPhysicalDeviceFeatures2(portability_features);
RETURN_IF_SKIP(InitState(nullptr, &features2));
}
const VkDevice device = this->device();
// Get Metal Device and Metal Command Queue in 1 call
{
const VkQueue queue = m_default_queue->handle();
VkExportMetalCommandQueueInfoEXT queueInfo = vku::InitStructHelper();
queueInfo.queue = queue;
VkExportMetalDeviceInfoEXT deviceInfo = vku::InitStructHelper(&queueInfo);
VkExportMetalObjectsInfoEXT objectsInfo = vku::InitStructHelper(&deviceInfo);
// This tests both device, queue, and pNext chaining
vk::ExportMetalObjectsEXT(device, &objectsInfo);
ASSERT_TRUE(deviceInfo.mtlDevice != nullptr);
ASSERT_TRUE(queueInfo.mtlCommandQueue != nullptr);
}
// Get Metal Buffer
{
VkExportMetalObjectCreateInfoEXT metalBufferCreateInfo = vku::InitStructHelper();
metalBufferCreateInfo.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_BUFFER_BIT_EXT;
VkMemoryAllocateInfo mem_info = vku::InitStructHelper(&metalBufferCreateInfo);
mem_info.allocationSize = 1024;
VkDeviceMemory memory;
const VkResult err = vk::AllocateMemory(device, &mem_info, NULL, &memory);
ASSERT_EQ(VK_SUCCESS, err);
VkExportMetalBufferInfoEXT bufferInfo = vku::InitStructHelper();
bufferInfo.memory = memory;
VkExportMetalObjectsInfoEXT objectsInfo = vku::InitStructHelper(&bufferInfo);
vk::ExportMetalObjectsEXT(device, &objectsInfo);
ASSERT_TRUE(bufferInfo.mtlBuffer != nullptr);
vk::FreeMemory(device, memory, nullptr);
}
// Get Metal Texture and Metal IOSurfaceRef
{
VkExportMetalObjectCreateInfoEXT metalSurfaceInfo = vku::InitStructHelper();
metalSurfaceInfo.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_IOSURFACE_BIT_EXT;
VkExportMetalObjectCreateInfoEXT metalTextureCreateInfo = vku::InitStructHelper(&metalSurfaceInfo);
metalTextureCreateInfo.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_TEXTURE_BIT_EXT;
// Image contents don't matter
VkImageCreateInfo ici = vku::InitStructHelper(&metalTextureCreateInfo);
ici.imageType = VK_IMAGE_TYPE_2D;
ici.format = VK_FORMAT_B8G8R8A8_UNORM;
ici.extent = {32, 32, 1};
ici.mipLevels = 1;
ici.arrayLayers = 1;
ici.samples = VK_SAMPLE_COUNT_1_BIT;
ici.tiling = VK_IMAGE_TILING_LINEAR;
ici.usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
vkt::Image image(*m_device, ici, vkt::set_layout);
VkExportMetalIOSurfaceInfoEXT surfaceInfo = vku::InitStructHelper();
surfaceInfo.image = image;
VkExportMetalTextureInfoEXT textureInfo = vku::InitStructHelper(&surfaceInfo);
textureInfo.image = image;
textureInfo.plane = VK_IMAGE_ASPECT_PLANE_0_BIT; // Image is not multi-planar
VkExportMetalObjectsInfoEXT objectsInfo = vku::InitStructHelper(&textureInfo);
// This tests both texture, surface, and pNext chaining
vk::ExportMetalObjectsEXT(device, &objectsInfo);
ASSERT_TRUE(textureInfo.mtlTexture != nullptr);
ASSERT_TRUE(surfaceInfo.ioSurface != nullptr);
}
// Get Metal Shared Event
{
VkExportMetalObjectCreateInfoEXT metalEventCreateInfo = vku::InitStructHelper();
metalEventCreateInfo.exportObjectType = VK_EXPORT_METAL_OBJECT_TYPE_METAL_SHARED_EVENT_BIT_EXT;
VkEventCreateInfo eventCreateInfo = vku::InitStructHelper(&metalEventCreateInfo);
vkt::Event event(*m_device, eventCreateInfo);
ASSERT_TRUE(event.initialized());
VkExportMetalSharedEventInfoEXT eventInfo = vku::InitStructHelper();
eventInfo.event = event;
VkExportMetalObjectsInfoEXT objectsInfo = vku::InitStructHelper(&eventInfo);
vk::ExportMetalObjectsEXT(device, &objectsInfo);
ASSERT_TRUE(eventInfo.mtlSharedEvent != nullptr);
}
}
#endif // VK_USE_PLATFORM_METAL_EXT
#ifdef VK_USE_PLATFORM_WIN32_KHR
TEST_F(PositiveExternalMemorySync, ExportFromImportedFence) {
TEST_DESCRIPTION("Export from fence with imported payload");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_EXTERNAL_FENCE_WIN32_EXTENSION_NAME);
// Required to pass in various memory flags without querying for corresponding extensions.
AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
IgnoreHandleTypeError(m_errorMonitor);
const auto handle_type = VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT;
{
const auto handle_types = FindSupportedExternalFenceHandleTypes(
Gpu(), VK_EXTERNAL_FENCE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_FENCE_FEATURE_IMPORTABLE_BIT);
if ((handle_types & VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT) == 0) {
GTEST_SKIP() << "VK_EXTERNAL_FENCE_HANDLE_TYPE_OPAQUE_WIN32_BIT is not both exportable and importable";
}
}
VkPhysicalDeviceExternalFenceInfo fence_info = vku::InitStructHelper();
fence_info.handleType = handle_type;
VkExternalFenceProperties fence_properties = vku::InitStructHelper();
vk::GetPhysicalDeviceExternalFenceProperties(Gpu(), &fence_info, &fence_properties);
if ((handle_type & fence_properties.exportFromImportedHandleTypes) == 0) {
GTEST_SKIP() << "can't find handle type that can be exported from imported fence";
}
// create fence and export payload
VkExportFenceCreateInfo export_info = vku::InitStructHelper();
export_info.handleTypes = handle_type; // at first export handle type, then import it
const VkFenceCreateInfo create_info = vku::InitStructHelper(&export_info);
vkt::Fence fence(*m_device, create_info);
HANDLE handle = NULL;
fence.ExportHandle(handle, handle_type);
// create fence and import payload
VkExportFenceCreateInfo export_info2 = vku::InitStructHelper(); // prepare to export from imported fence
export_info2.handleTypes = handle_type;
const VkFenceCreateInfo create_info2 = vku::InitStructHelper(&export_info2);
vkt::Fence import_fence(*m_device, create_info2);
import_fence.ImportHandle(handle, handle_type);
// export from imported fence
HANDLE handle2 = NULL;
import_fence.ExportHandle(handle2, handle_type);
::CloseHandle(handle);
if (handle2 != handle) {
::CloseHandle(handle2);
}
}
TEST_F(PositiveExternalMemorySync, ImportMemoryWin32BufferDifferentDedicated) {
TEST_DESCRIPTION("https://gitlab.khronos.org/vulkan/Vulkan-ValidationLayers/-/issues/35");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
// Required to pass in various memory flags without querying for corresponding extensions.
AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
IgnoreHandleTypeError(m_errorMonitor);
auto buffer_info = vkt::Buffer::CreateInfo(1024, VK_BUFFER_USAGE_TRANSFER_DST_BIT);
const auto handle_types = FindSupportedExternalMemoryHandleTypes(
Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT);
if ((handle_types & VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT) == 0) {
GTEST_SKIP() << "VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT is not both exportable and importable";
}
VkExternalMemoryBufferCreateInfo external_buffer_info = vku::InitStructHelper();
external_buffer_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
buffer_info.pNext = &external_buffer_info;
vkt::Buffer buffer(*m_device, buffer_info, vkt::no_mem);
VkMemoryDedicatedAllocateInfo dedicated_info = vku::InitStructHelper();
dedicated_info.buffer = buffer;
VkExportMemoryAllocateInfo export_info = vku::InitStructHelper(&dedicated_info);
export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
auto alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer.MemoryRequirements(), 0, &export_info);
vkt::DeviceMemory memory_export(*m_device, alloc_info);
VkMemoryGetWin32HandleInfoKHR get_handle_info = vku::InitStructHelper();
get_handle_info.memory = memory_export;
get_handle_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
HANDLE handle = NULL;
vk::GetMemoryWin32HandleKHR(device(), &get_handle_info, &handle);
vkt::Buffer buffer2(*m_device, buffer_info, vkt::no_mem);
dedicated_info.buffer = buffer2;
VkImportMemoryWin32HandleInfoKHR import_info = vku::InitStructHelper(&dedicated_info);
import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
import_info.handle = handle;
alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer2.MemoryRequirements(), 0, &import_info);
vkt::DeviceMemory memory_import(*m_device, alloc_info);
// "For handle types defined as NT handles, the handles returned by vkGetFenceWin32HandleKHR are owned by the application. To
// avoid leaking resources, the application must release ownership of them using the CloseHandle system call when they are no
// longer needed."
::CloseHandle(handle);
}
#endif // VK_USE_PLATFORM_WIN32_KHR
TEST_F(PositiveExternalMemorySync, MultipleExportOpaqueFd) {
TEST_DESCRIPTION("regression from dEQP-VK.api.external.semaphore.opaque_fd.export_multiple_times_temporary");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
// Required to pass in various memory flags without querying for corresponding extensions.
AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
IgnoreHandleTypeError(m_errorMonitor);
const auto handle_types = FindSupportedExternalSemaphoreHandleTypes(Gpu(), VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT);
if ((handle_types & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) == 0) {
GTEST_SKIP() << "VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT is not exportable";
}
VkExportSemaphoreCreateInfo export_info = vku::InitStructHelper();
export_info.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
const VkSemaphoreCreateInfo create_info = vku::InitStructHelper(&export_info);
vkt::Semaphore semaphore(*m_device, create_info);
int handle = 0;
semaphore.ExportHandle(handle, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT);
semaphore.ExportHandle(handle, VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT);
}
TEST_F(PositiveExternalMemorySync, ImportMemoryFdBufferDifferentDedicated) {
TEST_DESCRIPTION("https://gitlab.khronos.org/vulkan/Vulkan-ValidationLayers/-/issues/35");
SetTargetApiVersion(VK_API_VERSION_1_1);
AddRequiredExtensions(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
// Required to pass in various memory flags without querying for corresponding extensions.
AddRequiredExtensions(VK_KHR_MAINTENANCE_5_EXTENSION_NAME);
RETURN_IF_SKIP(Init());
IgnoreHandleTypeError(m_errorMonitor);
VkExternalMemoryBufferCreateInfo external_buffer_info = vku::InitStructHelper();
external_buffer_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
auto buffer_info = vkt::Buffer::CreateInfo(1024, VK_BUFFER_USAGE_TRANSFER_DST_BIT, {}, &external_buffer_info);
if (!FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT)) {
GTEST_SKIP() << "Unable to find exportable handle type";
}
if (!FindSupportedExternalMemoryHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT)) {
GTEST_SKIP() << "Unable to find importable handle type";
}
const auto compatible_types = GetCompatibleHandleTypes(Gpu(), buffer_info, VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT);
if ((VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT & compatible_types) == 0) {
GTEST_SKIP() << "Cannot find handle types that are supported but not compatible with each other";
}
vkt::Buffer buffer(*m_device, buffer_info, vkt::no_mem);
VkMemoryDedicatedAllocateInfo dedicated_info = vku::InitStructHelper();
dedicated_info.image = VK_NULL_HANDLE;
dedicated_info.buffer = buffer;
VkExportMemoryAllocateInfo export_info = vku::InitStructHelper(&dedicated_info);
export_info.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
auto alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer.MemoryRequirements(), 0, &export_info);
vkt::DeviceMemory memory_export(*m_device, alloc_info);
VkMemoryGetFdInfoKHR mgfi = vku::InitStructHelper();
mgfi.memory = memory_export;
mgfi.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
int fd;
vk::GetMemoryFdKHR(device(), &mgfi, &fd);
vkt::Buffer buffer2(*m_device, buffer_info, vkt::no_mem);
dedicated_info.buffer = buffer2;
VkImportMemoryFdInfoKHR import_info = vku::InitStructHelper(&dedicated_info);
import_info.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT;
import_info.fd = fd;
alloc_info = vkt::DeviceMemory::GetResourceAllocInfo(*m_device, buffer2.MemoryRequirements(), 0, &import_info);
vkt::DeviceMemory memory_import(*m_device, alloc_info);
}
TEST_F(PositiveExternalMemorySync, BinarySyncDependsOnExternalTimelineSignal) {
// https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/10211
TEST_DESCRIPTION("Binary semaphore waits for binary signal that depends on timeline signal from imported semaphore");
#ifdef VK_USE_PLATFORM_WIN32_KHR
const auto extension_name = VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME;
const auto handle_type = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT;
#else
const auto extension_name = VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME;
const auto handle_type = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT;
#endif
SetTargetApiVersion(VK_API_VERSION_1_3);
AddRequiredExtensions(extension_name);
AddRequiredFeature(vkt::Feature::synchronization2);
AddRequiredFeature(vkt::Feature::timelineSemaphore);
RETURN_IF_SKIP(Init());
if (!SemaphoreExportImportSupported(Gpu(), VK_SEMAPHORE_TYPE_TIMELINE, handle_type)) {
GTEST_SKIP() << "Semaphore does not support export and import through opaque handle";
}
VkSemaphoreTypeCreateInfo semaphore_type_ci = vku::InitStructHelper();
semaphore_type_ci.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE;
VkExportSemaphoreCreateInfo export_info = vku::InitStructHelper(&semaphore_type_ci);
export_info.handleTypes = handle_type;
VkSemaphoreCreateInfo semaphore_ci = vku::InitStructHelper(&export_info);
vkt::Semaphore semaphore(*m_device, semaphore_ci);
vkt::Semaphore imported_semaphore(*m_device, VK_SEMAPHORE_TYPE_TIMELINE);
vkt::Semaphore binary_semaphore(*m_device);
ExternalHandle handle{};
semaphore.ExportHandle(handle, handle_type);
imported_semaphore.ImportHandle(handle, handle_type);
// Check that this scenario does not generate VUID-vkQueueSubmit2-semaphore-03873.
// In case of regression when the exported signal is not handled correctly, the
// validation may assume that timeline wait does not have resolving signal submitted yet.
m_default_queue->Submit2(vkt::no_cmd, vkt::TimelineSignal(semaphore, 1));
m_default_queue->Submit2(vkt::no_cmd, vkt::TimelineWait(imported_semaphore, 1), vkt::TimelineSignal(imported_semaphore, 2));
m_default_queue->Submit2(vkt::no_cmd, vkt::TimelineWait(semaphore, 2), vkt::Signal(binary_semaphore));
m_default_queue->Submit2(vkt::no_cmd, vkt::Wait(binary_semaphore));
m_device->Wait();
}