blob: 95f428df6fd9864616fd0c5a017894ed299c3594 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <array>
#include <thread>
#include <vector>
#include <gtest/gtest.h>
#include <vulkan/vulkan.h>
#include "platform_semaphore.h"
#define PRINT_STDERR(format, ...) \
fprintf(stderr, "%s:%d " format "\n", __FILE__, __LINE__, ##__VA_ARGS__)
namespace {
class VulkanTest {
public:
bool Initialize();
static bool Exec(VulkanTest* t1, VulkanTest* t2, bool temporary);
static bool ExecUsingQueue(VulkanTest* t1, VulkanTest* t2, bool temporary);
private:
bool InitVulkan();
bool InitImage();
bool is_initialized_ = false;
PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR
vkGetPhysicalDeviceExternalSemaphorePropertiesKHR_;
PFN_vkImportSemaphoreZirconHandleFUCHSIA vkImportSemaphoreZirconHandleFUCHSIA_;
PFN_vkGetSemaphoreZirconHandleFUCHSIA vkGetSemaphoreZirconHandleFUCHSIA_;
VkPhysicalDevice vk_physical_device_;
VkDevice vk_device_;
VkQueue vk_queue_;
static constexpr uint32_t kSemaphoreCount = 2;
std::vector<VkSemaphore> vk_semaphore_;
};
bool VulkanTest::Initialize() {
if (is_initialized_)
return false;
if (!InitVulkan()) {
PRINT_STDERR("failed to initialize Vulkan");
return false;
}
is_initialized_ = true;
return true;
}
bool VulkanTest::InitVulkan() {
VkResult result;
uint32_t extension_count;
result = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count, nullptr);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkEnumerateInstanceExtensionProperties returned %d", result);
return false;
}
std::vector<VkExtensionProperties> extension_properties(extension_count);
result = vkEnumerateInstanceExtensionProperties(nullptr, &extension_count,
extension_properties.data());
if (result != VK_SUCCESS) {
PRINT_STDERR("vkEnumerateInstanceExtensionProperties returned %d", result);
return false;
}
std::array<const char*, 2> instance_extensions{
VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME};
std::array<const char*, 2> device_extensions{VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME,
VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME};
uint32_t found_count = 0;
for (auto& prop : extension_properties) {
for (uint32_t i = 0; i < instance_extensions.size(); i++) {
if ((strcmp(prop.extensionName, instance_extensions[i]) == 0))
found_count++;
}
}
if (found_count != instance_extensions.size()) {
PRINT_STDERR("failed to find instance extensions");
return false;
}
// Setup validation layer.
uint32_t layer_count;
result = vkEnumerateInstanceLayerProperties(&layer_count, nullptr);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkEnumerateInstanceLayerProperties returned %d", result);
return false;
}
std::vector<VkLayerProperties> layer_properties(layer_count);
result = vkEnumerateInstanceLayerProperties(&layer_count, layer_properties.data());
bool found_khr_validation = false;
bool found_lunarg_validation = false;
for (const auto& property : layer_properties) {
found_khr_validation =
found_khr_validation || (strcmp(property.layerName, "VK_LAYER_KHRONOS_validation") == 0);
found_lunarg_validation =
found_lunarg_validation ||
(strcmp(property.layerName, "VK_LAYER_LUNARG_standard_validation") == 0);
}
std::vector<const char*> layers;
// Vulkan loader is needed for loading layers.
if (found_khr_validation) {
layers.push_back("VK_LAYER_KHRONOS_validation");
} else if (found_lunarg_validation) {
layers.push_back("VK_LAYER_LUNARG_standard_validation");
}
VkInstanceCreateInfo create_info{
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // VkStructureType sType;
nullptr, // const void* pNext;
0, // VkInstanceCreateFlags flags;
nullptr, // const VkApplicationInfo* pApplicationInfo;
static_cast<uint32_t>(layers.size()), // uint32_t enabledLayerCount;
layers.data(), // const char* const* ppEnabledLayerNames;
static_cast<uint32_t>(instance_extensions.size()),
instance_extensions.data(),
};
VkAllocationCallbacks* allocation_callbacks = nullptr;
VkInstance instance;
if ((result = vkCreateInstance(&create_info, allocation_callbacks, &instance)) != VK_SUCCESS) {
PRINT_STDERR("vkCreateInstance failed %d", result);
return false;
}
uint32_t physical_device_count = 0;
if ((result = vkEnumeratePhysicalDevices(instance, &physical_device_count, nullptr)) !=
VK_SUCCESS) {
PRINT_STDERR("vkEnumeratePhysicalDevices failed %d", result);
return false;
}
if (physical_device_count < 1) {
PRINT_STDERR("unexpected physical_device_count %d", physical_device_count);
return false;
}
std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
if ((result = vkEnumeratePhysicalDevices(instance, &physical_device_count,
physical_devices.data())) != VK_SUCCESS) {
PRINT_STDERR("vkEnumeratePhysicalDevices failed %d", result);
return false;
}
uint32_t queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[0], &queue_family_count, nullptr);
if (queue_family_count < 1) {
PRINT_STDERR("invalid queue_family_count %d", queue_family_count);
return false;
}
std::vector<VkQueueFamilyProperties> queue_family_properties(queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[0], &queue_family_count,
queue_family_properties.data());
int32_t queue_family_index = -1;
for (uint32_t i = 0; i < queue_family_count; i++) {
if (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
queue_family_index = i;
break;
}
}
if (queue_family_index < 0) {
PRINT_STDERR("couldn't find an appropriate queue");
return false;
}
result =
vkEnumerateDeviceExtensionProperties(physical_devices[0], nullptr, &extension_count, nullptr);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkEnumerateDeviceExtensionProperties returned %d", result);
return false;
}
extension_properties.resize(extension_count);
result = vkEnumerateDeviceExtensionProperties(physical_devices[0], nullptr, &extension_count,
extension_properties.data());
if (result != VK_SUCCESS) {
PRINT_STDERR("vkEnumerateDeviceExtensionProperties returned %d", result);
return false;
}
found_count = 0;
for (const auto& prop : extension_properties) {
for (uint32_t i = 0; i < device_extensions.size(); i++) {
if ((strcmp(prop.extensionName, device_extensions[i]) == 0))
found_count++;
}
}
if (found_count != device_extensions.size()) {
PRINT_STDERR("failed to find device extensions");
return false;
}
// Create the device
float queue_priorities[1] = {0.0};
VkDeviceQueueCreateInfo queue_create_info = {.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueFamilyIndex = 0,
.queueCount = 1,
.pQueuePriorities = queue_priorities};
VkDeviceCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queue_create_info,
.enabledLayerCount = 0,
.ppEnabledLayerNames = nullptr,
.enabledExtensionCount = static_cast<uint32_t>(device_extensions.size()),
.ppEnabledExtensionNames = device_extensions.data(),
.pEnabledFeatures = nullptr};
VkDevice vkdevice;
if ((result = vkCreateDevice(physical_devices[0], &createInfo, nullptr /* allocationcallbacks */,
&vkdevice)) != VK_SUCCESS) {
PRINT_STDERR("vkCreateDevice failed: %d", result);
return false;
}
vk_physical_device_ = physical_devices[0];
vk_device_ = vkdevice;
vkGetDeviceQueue(vkdevice, queue_family_index, 0, &vk_queue_);
// Get extension function pointers
vkGetPhysicalDeviceExternalSemaphorePropertiesKHR_ =
reinterpret_cast<PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR>(
vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR"));
if (!vkGetPhysicalDeviceExternalSemaphorePropertiesKHR_) {
PRINT_STDERR("couldn't find vkGetPhysicalDeviceExternalSemaphorePropertiesKHR");
return false;
}
vkImportSemaphoreZirconHandleFUCHSIA_ =
reinterpret_cast<PFN_vkImportSemaphoreZirconHandleFUCHSIA>(
vkGetDeviceProcAddr(vk_device_, "vkImportSemaphoreZirconHandleFUCHSIA"));
if (!vkImportSemaphoreZirconHandleFUCHSIA_) {
PRINT_STDERR("couldn't find vkImportSemaphoreZirconHandleFUCHSIA");
return false;
}
vkGetSemaphoreZirconHandleFUCHSIA_ = reinterpret_cast<PFN_vkGetSemaphoreZirconHandleFUCHSIA>(
vkGetDeviceProcAddr(vk_device_, "vkGetSemaphoreZirconHandleFUCHSIA"));
if (!vkGetSemaphoreZirconHandleFUCHSIA_) {
PRINT_STDERR("couldn't find vkGetSemaphoreZirconHandleFUCHSIA");
return false;
}
VkExternalSemaphorePropertiesKHR external_semaphore_properties = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES_KHR,
};
VkPhysicalDeviceExternalSemaphoreInfo external_semaphore_info = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO_KHR,
.pNext = nullptr,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
};
vkGetPhysicalDeviceExternalSemaphorePropertiesKHR_(vk_physical_device_, &external_semaphore_info,
&external_semaphore_properties);
EXPECT_EQ(external_semaphore_properties.compatibleHandleTypes,
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA);
EXPECT_EQ(external_semaphore_properties.externalSemaphoreFeatures,
0u | VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT_KHR |
VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT_KHR);
// Create semaphores for export
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
VkExportSemaphoreCreateInfo export_create_info = {
.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO_KHR,
.pNext = nullptr,
.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
};
VkSemaphoreCreateInfo create_info = {
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &export_create_info,
.flags = 0,
};
VkSemaphore semaphore;
result = vkCreateSemaphore(vk_device_, &create_info, nullptr, &semaphore);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkCreateSemaphore returned %d", result);
return false;
}
vk_semaphore_.push_back(semaphore);
}
return true;
}
bool VulkanTest::Exec(VulkanTest* t1, VulkanTest* t2, bool temporary) {
VkResult result;
std::vector<uint32_t> handle(kSemaphoreCount);
// Export semaphores
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
VkSemaphoreGetZirconHandleInfoFUCHSIA info{
.sType = VK_STRUCTURE_TYPE_TEMP_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA,
.pNext = nullptr,
.semaphore = t1->vk_semaphore_[i],
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
};
result = t1->vkGetSemaphoreZirconHandleFUCHSIA_(t1->vk_device_, &info, &handle[i]);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkGetSemaphoreZirconHandleFUCHSIA returned %d", result);
return false;
}
}
std::vector<std::unique_ptr<magma::PlatformSemaphore>> exported;
// Import semaphores
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
uint32_t flags = temporary ? VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR : 0;
exported.emplace_back(magma::PlatformSemaphore::Import(handle[i]));
uint32_t import_handle;
EXPECT_TRUE(exported.back()->duplicate_handle(&import_handle));
VkImportSemaphoreZirconHandleInfoFUCHSIA import_info = {
.sType = VK_STRUCTURE_TYPE_TEMP_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA,
.pNext = nullptr,
.semaphore = t2->vk_semaphore_[i],
.flags = flags,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
.handle = import_handle};
result = t1->vkImportSemaphoreZirconHandleFUCHSIA_(t2->vk_device_, &import_info);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkImportSemaphoreZirconHandleFUCHSIA failed: %d", result);
return false;
}
}
// Test semaphores
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
auto& platform_semaphore_export = exported[i];
// Export the imported semaphores
VkSemaphoreGetZirconHandleInfoFUCHSIA info{
.sType = VK_STRUCTURE_TYPE_TEMP_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA,
.pNext = nullptr,
.semaphore = t2->vk_semaphore_[i],
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
};
result = t1->vkGetSemaphoreZirconHandleFUCHSIA_(t2->vk_device_, &info, &handle[i]);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkGetSemaphoreZirconHandleFUCHSIA_ returned %d", result);
return false;
}
std::shared_ptr<magma::PlatformSemaphore> platform_semaphore_import =
magma::PlatformSemaphore::Import(handle[i]);
EXPECT_EQ(platform_semaphore_export->id(), platform_semaphore_import->id());
platform_semaphore_export->Reset();
std::thread thread(
[platform_semaphore_import] { EXPECT_TRUE(platform_semaphore_import->Wait(2000)); });
platform_semaphore_export->Signal();
thread.join();
}
// Destroy semaphores
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
vkDestroySemaphore(t1->vk_device_, t1->vk_semaphore_[i], nullptr);
vkDestroySemaphore(t2->vk_device_, t2->vk_semaphore_[i], nullptr);
}
return true;
}
bool VulkanTest::ExecUsingQueue(VulkanTest* t1, VulkanTest* t2, bool temporary) {
VkResult result;
std::vector<uint32_t> handle(kSemaphoreCount);
// Export semaphores
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
VkSemaphoreGetZirconHandleInfoFUCHSIA info{
.sType = VK_STRUCTURE_TYPE_TEMP_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA,
.pNext = nullptr,
.semaphore = t1->vk_semaphore_[i],
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
};
result = t1->vkGetSemaphoreZirconHandleFUCHSIA_(t1->vk_device_, &info, &handle[i]);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkGetSemaphoreZirconHandleFUCHSIA_ returned %d", result);
return false;
}
}
// Import semaphores
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
uint32_t flags = temporary ? VK_SEMAPHORE_IMPORT_TEMPORARY_BIT_KHR : 0;
VkImportSemaphoreZirconHandleInfoFUCHSIA import_info = {
.sType = VK_STRUCTURE_TYPE_TEMP_IMPORT_SEMAPHORE_ZIRCON_HANDLE_INFO_FUCHSIA,
.pNext = nullptr,
.semaphore = t2->vk_semaphore_[i],
.flags = flags,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_TEMP_ZIRCON_EVENT_BIT_FUCHSIA,
.handle = handle[i]};
result = t1->vkImportSemaphoreZirconHandleFUCHSIA_(t2->vk_device_, &import_info);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkImportSemaphoreZirconHandleFUCHSIA_ failed: %d", result);
return false;
}
}
VkSubmitInfo submit_info1 = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &t1->vk_semaphore_[0]};
result = vkQueueSubmit(t1->vk_queue_, 1, &submit_info1, VK_NULL_HANDLE);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkQueueSubmit failed: %d", result);
return false;
}
VkPipelineStageFlags stage_flags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
VkSubmitInfo submit_info2 = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &t2->vk_semaphore_[0],
.pWaitDstStageMask = &stage_flags,
.signalSemaphoreCount = 1,
.pSignalSemaphores = &t2->vk_semaphore_[1]};
result = vkQueueSubmit(t2->vk_queue_, 1, &submit_info2, VK_NULL_HANDLE);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkQueueSubmit failed: %d", result);
return false;
}
VkSubmitInfo submit_info3 = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &t1->vk_semaphore_[1],
.pWaitDstStageMask = &stage_flags};
vkQueueSubmit(t1->vk_queue_, 1, &submit_info3, VK_NULL_HANDLE);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkQueueSubmit failed: %d", result);
return false;
}
result = vkQueueWaitIdle(t1->vk_queue_);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkQueueWaitIdle failed: %d", result);
return false;
}
result = vkQueueWaitIdle(t2->vk_queue_);
if (result != VK_SUCCESS) {
PRINT_STDERR("vkQueueWaitIdle failed: %d", result);
return false;
}
// Destroy semaphores
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
vkDestroySemaphore(t1->vk_device_, t1->vk_semaphore_[i], nullptr);
vkDestroySemaphore(t2->vk_device_, t2->vk_semaphore_[i], nullptr);
}
return true;
}
TEST(VulkanExtension, ExternalSemaphoreFuchsia) {
VulkanTest t1, t2;
ASSERT_TRUE(t1.Initialize());
ASSERT_TRUE(t2.Initialize());
EXPECT_TRUE(VulkanTest::Exec(&t1, &t2, false));
}
TEST(VulkanExtension, TemporaryExternalSemaphoreFuchsia) {
VulkanTest t1, t2;
ASSERT_TRUE(t1.Initialize());
ASSERT_TRUE(t2.Initialize());
EXPECT_TRUE(VulkanTest::Exec(&t1, &t2, true));
}
TEST(VulkanExtension, QueueExternalSemaphoreFuchsia) {
VulkanTest t1, t2;
ASSERT_TRUE(t1.Initialize());
ASSERT_TRUE(t2.Initialize());
EXPECT_TRUE(VulkanTest::ExecUsingQueue(&t1, &t2, false));
}
TEST(VulkanExtension, QueueTemporaryExternalSemaphoreFuchsia) {
VulkanTest t1, t2;
ASSERT_TRUE(t1.Initialize());
ASSERT_TRUE(t2.Initialize());
EXPECT_TRUE(VulkanTest::ExecUsingQueue(&t1, &t2, true));
}
} // namespace