| // | |
| // Copyright 2019 The ANGLE Project Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| // | |
| // VulkanHelper.cpp : Helper for allocating & managing vulkan external objects. | |
| #include "test_utils/VulkanHelper.h" | |
| #include <vector> | |
| #include "common/bitset_utils.h" | |
| #include "common/debug.h" | |
| #include "common/system_utils.h" | |
| #include "common/vulkan/vulkan_icd.h" | |
| #include "test_utils/ANGLETest.h" | |
| #include "vulkan/vulkan_core.h" | |
| namespace angle | |
| { | |
| namespace | |
| { | |
| std::vector<VkExtensionProperties> EnumerateInstanceExtensionProperties(const char *layerName) | |
| { | |
| uint32_t instanceExtensionCount; | |
| VkResult result = | |
| vkEnumerateInstanceExtensionProperties(layerName, &instanceExtensionCount, nullptr); | |
| ASSERT(result == VK_SUCCESS); | |
| std::vector<VkExtensionProperties> instanceExtensionProperties(instanceExtensionCount); | |
| result = vkEnumerateInstanceExtensionProperties(layerName, &instanceExtensionCount, | |
| instanceExtensionProperties.data()); | |
| ASSERT(result == VK_SUCCESS); | |
| return instanceExtensionProperties; | |
| } | |
| std::vector<VkPhysicalDevice> EnumeratePhysicalDevices(VkInstance instance) | |
| { | |
| uint32_t physicalDeviceCount; | |
| VkResult result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr); | |
| ASSERT(result == VK_SUCCESS); | |
| std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount); | |
| result = vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, physicalDevices.data()); | |
| return physicalDevices; | |
| } | |
| std::vector<VkExtensionProperties> EnumerateDeviceExtensionProperties( | |
| VkPhysicalDevice physicalDevice, | |
| const char *layerName) | |
| { | |
| uint32_t deviceExtensionCount; | |
| VkResult result = vkEnumerateDeviceExtensionProperties(physicalDevice, layerName, | |
| &deviceExtensionCount, nullptr); | |
| ASSERT(result == VK_SUCCESS); | |
| std::vector<VkExtensionProperties> deviceExtensionProperties(deviceExtensionCount); | |
| result = vkEnumerateDeviceExtensionProperties(physicalDevice, layerName, &deviceExtensionCount, | |
| deviceExtensionProperties.data()); | |
| ASSERT(result == VK_SUCCESS); | |
| return deviceExtensionProperties; | |
| } | |
| std::vector<VkQueueFamilyProperties> GetPhysicalDeviceQueueFamilyProperties( | |
| VkPhysicalDevice physicalDevice) | |
| { | |
| uint32_t queueFamilyPropertyCount; | |
| vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyPropertyCount, nullptr); | |
| std::vector<VkQueueFamilyProperties> physicalDeviceQueueFamilyProperties( | |
| queueFamilyPropertyCount); | |
| vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyPropertyCount, | |
| physicalDeviceQueueFamilyProperties.data()); | |
| return physicalDeviceQueueFamilyProperties; | |
| } | |
| bool HasExtension(const std::vector<VkExtensionProperties> instanceExtensions, | |
| const char *extensionName) | |
| { | |
| for (const auto &extensionProperties : instanceExtensions) | |
| { | |
| if (!strcmp(extensionProperties.extensionName, extensionName)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool HasExtension(const std::vector<const char *> enabledExtensions, const char *extensionName) | |
| { | |
| for (const char *enabledExtension : enabledExtensions) | |
| { | |
| if (!strcmp(enabledExtension, extensionName)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| bool HasExtension(const char *const *enabledExtensions, const char *extensionName) | |
| { | |
| size_t i = 0; | |
| while (enabledExtensions[i]) | |
| { | |
| if (!strcmp(enabledExtensions[i], extensionName)) | |
| return true; | |
| i++; | |
| } | |
| return false; | |
| } | |
| uint32_t FindMemoryType(const VkPhysicalDeviceMemoryProperties &memoryProperties, | |
| uint32_t memoryTypeBits, | |
| VkMemoryPropertyFlags requiredMemoryPropertyFlags) | |
| { | |
| for (size_t memoryIndex : angle::BitSet32<32>(memoryTypeBits)) | |
| { | |
| ASSERT(memoryIndex < memoryProperties.memoryTypeCount); | |
| if ((memoryProperties.memoryTypes[memoryIndex].propertyFlags & | |
| requiredMemoryPropertyFlags) == requiredMemoryPropertyFlags) | |
| { | |
| return static_cast<uint32_t>(memoryIndex); | |
| } | |
| } | |
| return UINT32_MAX; | |
| } | |
| void ImageMemoryBarrier(VkCommandBuffer commandBuffer, | |
| VkImage image, | |
| uint32_t srcQueueFamilyIndex, | |
| uint32_t dstQueueFamilyIndex, | |
| VkImageLayout oldLayout, | |
| VkImageLayout newLayout) | |
| { | |
| const VkImageMemoryBarrier imageMemoryBarriers[] = { | |
| /* [0] = */ {/* .sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | |
| /* .pNext = */ nullptr, | |
| /* .srcAccessMask = */ VK_ACCESS_MEMORY_WRITE_BIT, | |
| /* .dstAccessMask = */ VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | |
| /* .oldLayout = */ oldLayout, | |
| /* .newLayout = */ newLayout, | |
| /* .srcQueueFamilyIndex = */ srcQueueFamilyIndex, | |
| /* .dstQueueFamilyIndex = */ dstQueueFamilyIndex, | |
| /* .image = */ image, | |
| /* .subresourceRange = */ | |
| { | |
| /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT, | |
| /* .basicMiplevel = */ 0, | |
| /* .levelCount = */ 1, | |
| /* .baseArrayLayer = */ 0, | |
| /* .layerCount = */ 1, | |
| }}}; | |
| const uint32_t imageMemoryBarrierCount = std::extent<decltype(imageMemoryBarriers)>(); | |
| constexpr VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; | |
| constexpr VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; | |
| const VkDependencyFlags dependencyFlags = 0; | |
| vkCmdPipelineBarrier(commandBuffer, srcStageMask, dstStageMask, dependencyFlags, 0, nullptr, 0, | |
| nullptr, imageMemoryBarrierCount, imageMemoryBarriers); | |
| } | |
| } // namespace | |
| VulkanHelper::VulkanHelper() {} | |
| VulkanHelper::~VulkanHelper() | |
| { | |
| if (mDevice != VK_NULL_HANDLE) | |
| { | |
| vkDeviceWaitIdle(mDevice); | |
| } | |
| if (mCommandPool != VK_NULL_HANDLE) | |
| { | |
| vkDestroyCommandPool(mDevice, mCommandPool, nullptr); | |
| } | |
| if (!mInitializedFromANGLE) | |
| { | |
| if (mDevice != VK_NULL_HANDLE) | |
| { | |
| vkDestroyDevice(mDevice, nullptr); | |
| mDevice = VK_NULL_HANDLE; | |
| mGraphicsQueue = VK_NULL_HANDLE; | |
| } | |
| if (mInstance != VK_NULL_HANDLE) | |
| { | |
| vkDestroyInstance(mInstance, nullptr); | |
| mInstance = VK_NULL_HANDLE; | |
| } | |
| } | |
| } | |
| void VulkanHelper::initialize(bool useSwiftshader, bool enableValidationLayers) | |
| { | |
| bool enableValidationLayersOverride = enableValidationLayers; | |
| #if !defined(ANGLE_ENABLE_VULKAN_VALIDATION_LAYERS) | |
| enableValidationLayersOverride = false; | |
| #endif | |
| vk::ICD icd = useSwiftshader ? vk::ICD::SwiftShader : vk::ICD::Default; | |
| vk::ScopedVkLoaderEnvironment scopedEnvironment(enableValidationLayersOverride, icd); | |
| ASSERT(mInstance == VK_NULL_HANDLE); | |
| VkResult result = VK_SUCCESS; | |
| #if ANGLE_SHARED_LIBVULKAN | |
| result = volkInitialize(); | |
| ASSERT(result == VK_SUCCESS); | |
| #endif // ANGLE_SHARED_LIBVULKAN | |
| std::vector<VkExtensionProperties> instanceExtensionProperties = | |
| EnumerateInstanceExtensionProperties(nullptr); | |
| std::vector<const char *> requestedInstanceExtensions = { | |
| VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, | |
| VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME, | |
| VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME}; | |
| std::vector<const char *> enabledInstanceExtensions; | |
| for (const char *extensionName : requestedInstanceExtensions) | |
| { | |
| if (HasExtension(instanceExtensionProperties, extensionName)) | |
| { | |
| enabledInstanceExtensions.push_back(extensionName); | |
| } | |
| } | |
| VkApplicationInfo applicationInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_APPLICATION_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .pApplicationName = */ "ANGLE Tests", | |
| /* .applicationVersion = */ 1, | |
| /* .pEngineName = */ nullptr, | |
| /* .engineVersion = */ 0, | |
| /* .apiVersion = */ VK_API_VERSION_1_1, | |
| }; | |
| uint32_t enabledInstanceExtensionCount = | |
| static_cast<uint32_t>(enabledInstanceExtensions.size()); | |
| std::vector<const char *> enabledLayerNames; | |
| if (enableValidationLayersOverride) | |
| { | |
| enabledLayerNames.push_back("VK_LAYER_KHRONOS_validation"); | |
| } | |
| VkInstanceCreateInfo instanceCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ 0, | |
| /* .pApplicationInfo = */ &applicationInfo, | |
| /* .enabledLayerCount = */ static_cast<uint32_t>(enabledLayerNames.size()), | |
| /* .ppEnabledLayerNames = */ enabledLayerNames.data(), | |
| /* .enabledExtensionCount = */ enabledInstanceExtensionCount, | |
| /* .ppEnabledExtensionName = */ enabledInstanceExtensions.data(), | |
| }; | |
| result = vkCreateInstance(&instanceCreateInfo, nullptr, &mInstance); | |
| ASSERT(result == VK_SUCCESS); | |
| ASSERT(mInstance != VK_NULL_HANDLE); | |
| #if ANGLE_SHARED_LIBVULKAN | |
| volkLoadInstance(mInstance); | |
| #endif // ANGLE_SHARED_LIBVULKAN | |
| std::vector<VkPhysicalDevice> physicalDevices = EnumeratePhysicalDevices(mInstance); | |
| ASSERT(physicalDevices.size() > 0); | |
| VkPhysicalDeviceProperties physicalDeviceProperties; | |
| ChoosePhysicalDevice(vkGetPhysicalDeviceProperties, physicalDevices, icd, 0, 0, | |
| &mPhysicalDevice, &physicalDeviceProperties); | |
| vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &mMemoryProperties); | |
| std::vector<VkExtensionProperties> deviceExtensionProperties = | |
| EnumerateDeviceExtensionProperties(mPhysicalDevice, nullptr); | |
| std::vector<const char *> requestedDeviceExtensions = { | |
| VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME, | |
| VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME, | |
| VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME, VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME, | |
| VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME, | |
| VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, | |
| }; | |
| std::vector<const char *> enabledDeviceExtensions; | |
| for (const char *extensionName : requestedDeviceExtensions) | |
| { | |
| if (HasExtension(deviceExtensionProperties, extensionName)) | |
| { | |
| enabledDeviceExtensions.push_back(extensionName); | |
| } | |
| } | |
| std::vector<VkQueueFamilyProperties> queueFamilyProperties = | |
| GetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice); | |
| for (uint32_t i = 0; i < queueFamilyProperties.size(); ++i) | |
| { | |
| if (queueFamilyProperties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) | |
| { | |
| mGraphicsQueueFamilyIndex = i; | |
| } | |
| } | |
| ASSERT(mGraphicsQueueFamilyIndex != UINT32_MAX); | |
| constexpr uint32_t kQueueCreateInfoCount = 1; | |
| constexpr uint32_t kGraphicsQueueCount = 1; | |
| float graphicsQueuePriorities[kGraphicsQueueCount] = {0.f}; | |
| VkDeviceQueueCreateInfo queueCreateInfos[kQueueCreateInfoCount] = { | |
| /* [0] = */ { | |
| /* .sType = */ VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ 0, | |
| /* .queueFamilyIndex = */ mGraphicsQueueFamilyIndex, | |
| /* .queueCount = */ 1, | |
| /* .pQueuePriorities = */ graphicsQueuePriorities, | |
| }, | |
| }; | |
| uint32_t enabledDeviceExtensionCount = static_cast<uint32_t>(enabledDeviceExtensions.size()); | |
| VkDeviceCreateInfo deviceCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ 0, | |
| /* .queueCreateInfoCount = */ kQueueCreateInfoCount, | |
| /* .pQueueCreateInfos = */ queueCreateInfos, | |
| /* .enabledLayerCount = */ 0, | |
| /* .ppEnabledLayerNames = */ nullptr, | |
| /* .enabledExtensionCount = */ enabledDeviceExtensionCount, | |
| /* .ppEnabledExtensionName = */ enabledDeviceExtensions.data(), | |
| /* .pEnabledFeatures = */ nullptr, | |
| }; | |
| result = vkCreateDevice(mPhysicalDevice, &deviceCreateInfo, nullptr, &mDevice); | |
| ASSERT(result == VK_SUCCESS); | |
| ASSERT(mDevice != VK_NULL_HANDLE); | |
| #if ANGLE_SHARED_LIBVULKAN | |
| volkLoadDevice(mDevice); | |
| #endif // ANGLE_SHARED_LIBVULKAN | |
| constexpr uint32_t kGraphicsQueueIndex = 0; | |
| static_assert(kGraphicsQueueIndex < kGraphicsQueueCount, "must be in range"); | |
| vkGetDeviceQueue(mDevice, mGraphicsQueueFamilyIndex, kGraphicsQueueIndex, &mGraphicsQueue); | |
| ASSERT(mGraphicsQueue != VK_NULL_HANDLE); | |
| VkCommandPoolCreateInfo commandPoolCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ 0, | |
| /* .queueFamilyIndex = */ mGraphicsQueueFamilyIndex, | |
| }; | |
| result = vkCreateCommandPool(mDevice, &commandPoolCreateInfo, nullptr, &mCommandPool); | |
| ASSERT(result == VK_SUCCESS); | |
| mHasExternalMemoryFd = | |
| HasExtension(enabledDeviceExtensions, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); | |
| mHasExternalSemaphoreFd = | |
| HasExtension(enabledDeviceExtensions, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME); | |
| mHasExternalMemoryFuchsia = | |
| HasExtension(enabledDeviceExtensions, VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME); | |
| mHasExternalSemaphoreFuchsia = | |
| HasExtension(enabledDeviceExtensions, VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME); | |
| vkGetPhysicalDeviceImageFormatProperties2 = | |
| reinterpret_cast<PFN_vkGetPhysicalDeviceImageFormatProperties2>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetPhysicalDeviceImageFormatProperties2")); | |
| vkGetMemoryFdKHR = reinterpret_cast<PFN_vkGetMemoryFdKHR>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetMemoryFdKHR")); | |
| ASSERT(!mHasExternalMemoryFd || vkGetMemoryFdKHR); | |
| vkGetSemaphoreFdKHR = reinterpret_cast<PFN_vkGetSemaphoreFdKHR>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetSemaphoreFdKHR")); | |
| ASSERT(!mHasExternalSemaphoreFd || vkGetSemaphoreFdKHR); | |
| vkGetPhysicalDeviceExternalSemaphorePropertiesKHR = | |
| reinterpret_cast<PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR")); | |
| vkGetMemoryZirconHandleFUCHSIA = reinterpret_cast<PFN_vkGetMemoryZirconHandleFUCHSIA>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetMemoryZirconHandleFUCHSIA")); | |
| ASSERT(!mHasExternalMemoryFuchsia || vkGetMemoryZirconHandleFUCHSIA); | |
| vkGetSemaphoreZirconHandleFUCHSIA = reinterpret_cast<PFN_vkGetSemaphoreZirconHandleFUCHSIA>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetSemaphoreZirconHandleFUCHSIA")); | |
| ASSERT(!mHasExternalSemaphoreFuchsia || vkGetSemaphoreZirconHandleFUCHSIA); | |
| } | |
| void VulkanHelper::initializeFromANGLE() | |
| { | |
| mInitializedFromANGLE = true; | |
| VkResult vkResult = VK_SUCCESS; | |
| EXPECT_TRUE(IsEGLClientExtensionEnabled("EGL_EXT_device_query")); | |
| EGLDisplay display = eglGetCurrentDisplay(); | |
| EGLAttrib result = 0; | |
| EXPECT_EGL_TRUE(eglQueryDisplayAttribEXT(display, EGL_DEVICE_EXT, &result)); | |
| EGLDeviceEXT device = reinterpret_cast<EGLDeviceEXT>(result); | |
| EXPECT_NE(EGL_NO_DEVICE_EXT, device); | |
| EXPECT_TRUE(IsEGLDeviceExtensionEnabled(device, "EGL_ANGLE_device_vulkan")); | |
| EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(device, EGL_VULKAN_GET_INSTANCE_PROC_ADDR, &result)); | |
| PFN_vkGetInstanceProcAddr getInstanceProcAddr = | |
| reinterpret_cast<PFN_vkGetInstanceProcAddr>(result); | |
| EXPECT_NE(getInstanceProcAddr, nullptr); | |
| #if ANGLE_SHARED_LIBVULKAN | |
| volkInitializeCustom(getInstanceProcAddr); | |
| #endif // ANGLE_SHARED_LIBVULKAN | |
| EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(device, EGL_VULKAN_INSTANCE_ANGLE, &result)); | |
| mInstance = reinterpret_cast<VkInstance>(result); | |
| EXPECT_NE(mInstance, static_cast<VkInstance>(VK_NULL_HANDLE)); | |
| #if ANGLE_SHARED_LIBVULKAN | |
| volkLoadInstance(mInstance); | |
| #endif // ANGLE_SHARED_LIBVULKAN | |
| EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(device, EGL_VULKAN_PHYSICAL_DEVICE_ANGLE, &result)); | |
| mPhysicalDevice = reinterpret_cast<VkPhysicalDevice>(result); | |
| EXPECT_NE(mPhysicalDevice, static_cast<VkPhysicalDevice>(VK_NULL_HANDLE)); | |
| vkGetPhysicalDeviceMemoryProperties(mPhysicalDevice, &mMemoryProperties); | |
| EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(device, EGL_VULKAN_DEVICE_ANGLE, &result)); | |
| mDevice = reinterpret_cast<VkDevice>(result); | |
| EXPECT_NE(mDevice, static_cast<VkDevice>(VK_NULL_HANDLE)); | |
| EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(device, EGL_VULKAN_QUEUE_ANGLE, &result)); | |
| mGraphicsQueue = reinterpret_cast<VkQueue>(result); | |
| EXPECT_NE(mGraphicsQueue, static_cast<VkQueue>(VK_NULL_HANDLE)); | |
| EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(device, EGL_VULKAN_QUEUE_FAMILIY_INDEX_ANGLE, &result)); | |
| mGraphicsQueueFamilyIndex = static_cast<uint32_t>(result); | |
| EXPECT_EGL_TRUE(eglQueryDeviceAttribEXT(device, EGL_VULKAN_DEVICE_EXTENSIONS_ANGLE, &result)); | |
| const char *const *enabledDeviceExtensions = reinterpret_cast<const char *const *>(result); | |
| mHasExternalMemoryFd = | |
| HasExtension(enabledDeviceExtensions, VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME); | |
| mHasExternalSemaphoreFd = | |
| HasExtension(enabledDeviceExtensions, VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME); | |
| mHasExternalMemoryFuchsia = | |
| HasExtension(enabledDeviceExtensions, VK_FUCHSIA_EXTERNAL_MEMORY_EXTENSION_NAME); | |
| mHasExternalSemaphoreFuchsia = | |
| HasExtension(enabledDeviceExtensions, VK_FUCHSIA_EXTERNAL_SEMAPHORE_EXTENSION_NAME); | |
| #if ANGLE_SHARED_LIBVULKAN | |
| volkLoadDevice(mDevice); | |
| #endif // ANGLE_SHARED_LIBVULKAN | |
| VkCommandPoolCreateInfo commandPoolCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ 0, | |
| /* .queueFamilyIndex = */ mGraphicsQueueFamilyIndex, | |
| }; | |
| vkResult = vkCreateCommandPool(mDevice, &commandPoolCreateInfo, nullptr, &mCommandPool); | |
| ASSERT(vkResult == VK_SUCCESS); | |
| vkGetPhysicalDeviceImageFormatProperties2 = | |
| reinterpret_cast<PFN_vkGetPhysicalDeviceImageFormatProperties2>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetPhysicalDeviceImageFormatProperties2")); | |
| ASSERT(vkGetPhysicalDeviceImageFormatProperties2); | |
| vkGetMemoryFdKHR = reinterpret_cast<PFN_vkGetMemoryFdKHR>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetMemoryFdKHR")); | |
| ASSERT(!mHasExternalMemoryFd || vkGetMemoryFdKHR); | |
| vkGetSemaphoreFdKHR = reinterpret_cast<PFN_vkGetSemaphoreFdKHR>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetSemaphoreFdKHR")); | |
| ASSERT(!mHasExternalSemaphoreFd || vkGetSemaphoreFdKHR); | |
| vkGetPhysicalDeviceExternalSemaphorePropertiesKHR = | |
| reinterpret_cast<PFN_vkGetPhysicalDeviceExternalSemaphorePropertiesKHR>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetPhysicalDeviceExternalSemaphorePropertiesKHR")); | |
| vkGetMemoryZirconHandleFUCHSIA = reinterpret_cast<PFN_vkGetMemoryZirconHandleFUCHSIA>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetMemoryZirconHandleFUCHSIA")); | |
| ASSERT(!mHasExternalMemoryFuchsia || vkGetMemoryZirconHandleFUCHSIA); | |
| vkGetSemaphoreZirconHandleFUCHSIA = reinterpret_cast<PFN_vkGetSemaphoreZirconHandleFUCHSIA>( | |
| vkGetInstanceProcAddr(mInstance, "vkGetSemaphoreZirconHandleFUCHSIA")); | |
| ASSERT(!mHasExternalSemaphoreFuchsia || vkGetSemaphoreZirconHandleFUCHSIA); | |
| } | |
| VkResult VulkanHelper::createImage2D(VkFormat format, | |
| VkImageCreateFlags createFlags, | |
| VkImageUsageFlags usageFlags, | |
| VkExtent3D extent, | |
| VkImage *imageOut, | |
| VkDeviceMemory *deviceMemoryOut, | |
| VkDeviceSize *deviceMemorySizeOut, | |
| VkImageCreateInfo *imageCreateInfoOut) | |
| { | |
| VkImageCreateInfo imageCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ createFlags, | |
| /* .imageType = */ VK_IMAGE_TYPE_2D, | |
| /* .format = */ format, | |
| /* .extent = */ extent, | |
| /* .mipLevels = */ 1, | |
| /* .arrayLayers = */ 1, | |
| /* .samples = */ VK_SAMPLE_COUNT_1_BIT, | |
| /* .tiling = */ VK_IMAGE_TILING_OPTIMAL, | |
| /* .usage = */ usageFlags, | |
| /* .sharingMode = */ VK_SHARING_MODE_EXCLUSIVE, | |
| /* .queueFamilyIndexCount = */ 0, | |
| /* .pQueueFamilyIndices = */ nullptr, | |
| /* initialLayout = */ VK_IMAGE_LAYOUT_UNDEFINED, | |
| }; | |
| VkImage image = VK_NULL_HANDLE; | |
| VkResult result = vkCreateImage(mDevice, &imageCreateInfo, nullptr, &image); | |
| if (result != VK_SUCCESS) | |
| { | |
| return result; | |
| } | |
| VkMemoryPropertyFlags requestedMemoryPropertyFlags = 0; | |
| VkMemoryRequirements memoryRequirements; | |
| vkGetImageMemoryRequirements(mDevice, image, &memoryRequirements); | |
| uint32_t memoryTypeIndex = FindMemoryType(mMemoryProperties, memoryRequirements.memoryTypeBits, | |
| requestedMemoryPropertyFlags); | |
| ASSERT(memoryTypeIndex != UINT32_MAX); | |
| VkDeviceSize deviceMemorySize = memoryRequirements.size; | |
| VkMemoryAllocateInfo memoryAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .allocationSize = */ deviceMemorySize, | |
| /* .memoryTypeIndex = */ memoryTypeIndex, | |
| }; | |
| VkDeviceMemory deviceMemory = VK_NULL_HANDLE; | |
| result = vkAllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &deviceMemory); | |
| if (result != VK_SUCCESS) | |
| { | |
| vkDestroyImage(mDevice, image, nullptr); | |
| return result; | |
| } | |
| VkDeviceSize memoryOffset = 0; | |
| result = vkBindImageMemory(mDevice, image, deviceMemory, memoryOffset); | |
| if (result != VK_SUCCESS) | |
| { | |
| vkFreeMemory(mDevice, deviceMemory, nullptr); | |
| vkDestroyImage(mDevice, image, nullptr); | |
| return result; | |
| } | |
| *imageOut = image; | |
| *deviceMemoryOut = deviceMemory; | |
| *deviceMemorySizeOut = deviceMemorySize; | |
| *imageCreateInfoOut = imageCreateInfo; | |
| return VK_SUCCESS; | |
| } | |
| bool VulkanHelper::canCreateImageExternal(VkFormat format, | |
| VkImageType type, | |
| VkImageTiling tiling, | |
| VkImageCreateFlags createFlags, | |
| VkImageUsageFlags usageFlags, | |
| VkExternalMemoryHandleTypeFlagBits handleType) const | |
| { | |
| VkPhysicalDeviceExternalImageFormatInfo externalImageFormatInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .handleType = */ handleType, | |
| }; | |
| VkPhysicalDeviceImageFormatInfo2 imageFormatInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2, | |
| /* .pNext = */ &externalImageFormatInfo, | |
| /* .format = */ format, | |
| /* .type = */ type, | |
| /* .tiling = */ tiling, | |
| /* .usage = */ usageFlags, | |
| /* .flags = */ createFlags, | |
| }; | |
| VkExternalImageFormatProperties externalImageFormatProperties = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES, | |
| /* .pNext = */ nullptr, | |
| }; | |
| VkImageFormatProperties2 imageFormatProperties = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2, | |
| /* .pNext = */ &externalImageFormatProperties}; | |
| VkResult result = vkGetPhysicalDeviceImageFormatProperties2(mPhysicalDevice, &imageFormatInfo, | |
| &imageFormatProperties); | |
| if (result == VK_ERROR_FORMAT_NOT_SUPPORTED) | |
| { | |
| return false; | |
| } | |
| ASSERT(result == VK_SUCCESS); | |
| constexpr VkExternalMemoryFeatureFlags kRequiredFeatures = | |
| VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT; | |
| if ((externalImageFormatProperties.externalMemoryProperties.externalMemoryFeatures & | |
| kRequiredFeatures) != kRequiredFeatures) | |
| { | |
| return false; | |
| } | |
| return true; | |
| } | |
| VkResult VulkanHelper::createImage2DExternal(VkFormat format, | |
| VkImageCreateFlags createFlags, | |
| VkImageUsageFlags usageFlags, | |
| const void *imageCreateInfoPNext, | |
| VkExtent3D extent, | |
| VkExternalMemoryHandleTypeFlags handleTypes, | |
| VkImage *imageOut, | |
| VkDeviceMemory *deviceMemoryOut, | |
| VkDeviceSize *deviceMemorySizeOut) | |
| { | |
| VkExternalMemoryImageCreateInfoKHR externalMemoryImageCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, | |
| /* .pNext = */ imageCreateInfoPNext, | |
| /* .handleTypes = */ handleTypes, | |
| }; | |
| VkImageCreateInfo imageCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | |
| /* .pNext = */ &externalMemoryImageCreateInfo, | |
| /* .flags = */ createFlags, | |
| /* .imageType = */ VK_IMAGE_TYPE_2D, | |
| /* .format = */ format, | |
| /* .extent = */ extent, | |
| /* .mipLevels = */ 1, | |
| /* .arrayLayers = */ 1, | |
| /* .samples = */ VK_SAMPLE_COUNT_1_BIT, | |
| /* .tiling = */ VK_IMAGE_TILING_OPTIMAL, | |
| /* .usage = */ usageFlags, | |
| /* .sharingMode = */ VK_SHARING_MODE_EXCLUSIVE, | |
| /* .queueFamilyIndexCount = */ 0, | |
| /* .pQueueFamilyIndices = */ nullptr, | |
| /* initialLayout = */ VK_IMAGE_LAYOUT_UNDEFINED, | |
| }; | |
| VkImage image = VK_NULL_HANDLE; | |
| VkResult result = vkCreateImage(mDevice, &imageCreateInfo, nullptr, &image); | |
| if (result != VK_SUCCESS) | |
| { | |
| return result; | |
| } | |
| VkMemoryPropertyFlags requestedMemoryPropertyFlags = 0; | |
| VkMemoryRequirements memoryRequirements; | |
| vkGetImageMemoryRequirements(mDevice, image, &memoryRequirements); | |
| uint32_t memoryTypeIndex = FindMemoryType(mMemoryProperties, memoryRequirements.memoryTypeBits, | |
| requestedMemoryPropertyFlags); | |
| ASSERT(memoryTypeIndex != UINT32_MAX); | |
| VkDeviceSize deviceMemorySize = memoryRequirements.size; | |
| VkExportMemoryAllocateInfo exportMemoryAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .handleTypes = */ handleTypes, | |
| }; | |
| VkMemoryDedicatedAllocateInfoKHR memoryDedicatedAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, | |
| /* .pNext = */ &exportMemoryAllocateInfo, | |
| /* .image = */ image, | |
| }; | |
| VkMemoryAllocateInfo memoryAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | |
| /* .pNext = */ &memoryDedicatedAllocateInfo, | |
| /* .allocationSize = */ deviceMemorySize, | |
| /* .memoryTypeIndex = */ memoryTypeIndex, | |
| }; | |
| VkDeviceMemory deviceMemory = VK_NULL_HANDLE; | |
| result = vkAllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &deviceMemory); | |
| if (result != VK_SUCCESS) | |
| { | |
| vkDestroyImage(mDevice, image, nullptr); | |
| return result; | |
| } | |
| VkDeviceSize memoryOffset = 0; | |
| result = vkBindImageMemory(mDevice, image, deviceMemory, memoryOffset); | |
| if (result != VK_SUCCESS) | |
| { | |
| vkFreeMemory(mDevice, deviceMemory, nullptr); | |
| vkDestroyImage(mDevice, image, nullptr); | |
| return result; | |
| } | |
| *imageOut = image; | |
| *deviceMemoryOut = deviceMemory; | |
| *deviceMemorySizeOut = deviceMemorySize; | |
| return VK_SUCCESS; | |
| } | |
| bool VulkanHelper::canCreateImageOpaqueFd(VkFormat format, | |
| VkImageType type, | |
| VkImageTiling tiling, | |
| VkImageCreateFlags createFlags, | |
| VkImageUsageFlags usageFlags) const | |
| { | |
| if (!mHasExternalMemoryFd) | |
| { | |
| return false; | |
| } | |
| return canCreateImageExternal(format, type, tiling, createFlags, usageFlags, | |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT); | |
| } | |
| VkResult VulkanHelper::createImage2DOpaqueFd(VkFormat format, | |
| VkImageCreateFlags createFlags, | |
| VkImageUsageFlags usageFlags, | |
| const void *imageCreateInfoPNext, | |
| VkExtent3D extent, | |
| VkImage *imageOut, | |
| VkDeviceMemory *deviceMemoryOut, | |
| VkDeviceSize *deviceMemorySizeOut) | |
| { | |
| return createImage2DExternal(format, createFlags, usageFlags, imageCreateInfoPNext, extent, | |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, imageOut, | |
| deviceMemoryOut, deviceMemorySizeOut); | |
| } | |
| VkResult VulkanHelper::exportMemoryOpaqueFd(VkDeviceMemory deviceMemory, int *fd) | |
| { | |
| VkMemoryGetFdInfoKHR memoryGetFdInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR, | |
| /* .pNext = */ nullptr, | |
| /* .memory = */ deviceMemory, | |
| /* .handleType = */ VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, | |
| }; | |
| return vkGetMemoryFdKHR(mDevice, &memoryGetFdInfo, fd); | |
| } | |
| bool VulkanHelper::canCreateImageZirconVmo(VkFormat format, | |
| VkImageType type, | |
| VkImageTiling tiling, | |
| VkImageCreateFlags createFlags, | |
| VkImageUsageFlags usageFlags) const | |
| { | |
| if (!mHasExternalMemoryFuchsia) | |
| { | |
| return false; | |
| } | |
| return canCreateImageExternal(format, type, tiling, createFlags, usageFlags, | |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA); | |
| } | |
| VkResult VulkanHelper::createImage2DZirconVmo(VkFormat format, | |
| VkImageCreateFlags createFlags, | |
| VkImageUsageFlags usageFlags, | |
| const void *imageCreateInfoPNext, | |
| VkExtent3D extent, | |
| VkImage *imageOut, | |
| VkDeviceMemory *deviceMemoryOut, | |
| VkDeviceSize *deviceMemorySizeOut) | |
| { | |
| return createImage2DExternal(format, createFlags, usageFlags, imageCreateInfoPNext, extent, | |
| VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA, imageOut, | |
| deviceMemoryOut, deviceMemorySizeOut); | |
| } | |
| VkResult VulkanHelper::exportMemoryZirconVmo(VkDeviceMemory deviceMemory, zx_handle_t *vmo) | |
| { | |
| VkMemoryGetZirconHandleInfoFUCHSIA memoryGetZirconHandleInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_GET_ZIRCON_HANDLE_INFO_FUCHSIA, | |
| /* .pNext = */ nullptr, | |
| /* .memory = */ deviceMemory, | |
| /* .handleType = */ VK_EXTERNAL_MEMORY_HANDLE_TYPE_ZIRCON_VMO_BIT_FUCHSIA, | |
| }; | |
| return vkGetMemoryZirconHandleFUCHSIA(mDevice, &memoryGetZirconHandleInfo, vmo); | |
| } | |
| bool VulkanHelper::canCreateSemaphoreOpaqueFd() const | |
| { | |
| if (!mHasExternalSemaphoreFd || !vkGetPhysicalDeviceExternalSemaphorePropertiesKHR) | |
| { | |
| return false; | |
| } | |
| VkPhysicalDeviceExternalSemaphoreInfo externalSemaphoreInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, | |
| }; | |
| VkExternalSemaphoreProperties externalSemaphoreProperties = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, | |
| }; | |
| vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(mPhysicalDevice, &externalSemaphoreInfo, | |
| &externalSemaphoreProperties); | |
| constexpr VkExternalSemaphoreFeatureFlags kRequiredFeatures = | |
| VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT; | |
| if ((externalSemaphoreProperties.externalSemaphoreFeatures & kRequiredFeatures) != | |
| kRequiredFeatures) | |
| { | |
| return false; | |
| } | |
| return true; | |
| } | |
| VkResult VulkanHelper::createSemaphoreOpaqueFd(VkSemaphore *semaphore) | |
| { | |
| VkExportSemaphoreCreateInfo exportSemaphoreCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .handleTypes = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, | |
| }; | |
| VkSemaphoreCreateInfo semaphoreCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, | |
| /* .pNext = */ &exportSemaphoreCreateInfo, | |
| /* .flags = */ 0, | |
| }; | |
| return vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, semaphore); | |
| } | |
| VkResult VulkanHelper::exportSemaphoreOpaqueFd(VkSemaphore semaphore, int *fd) | |
| { | |
| VkSemaphoreGetFdInfoKHR semaphoreGetFdInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR, | |
| /* .pNext = */ nullptr, | |
| /* .semaphore = */ semaphore, | |
| /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, | |
| }; | |
| return vkGetSemaphoreFdKHR(mDevice, &semaphoreGetFdInfo, fd); | |
| } | |
| bool VulkanHelper::canCreateSemaphoreZirconEvent() const | |
| { | |
| if (!mHasExternalSemaphoreFuchsia || !vkGetPhysicalDeviceExternalSemaphorePropertiesKHR) | |
| { | |
| return false; | |
| } | |
| VkPhysicalDeviceExternalSemaphoreInfo externalSemaphoreInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA, | |
| }; | |
| VkExternalSemaphoreProperties externalSemaphoreProperties = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_EXTERNAL_SEMAPHORE_PROPERTIES, | |
| }; | |
| vkGetPhysicalDeviceExternalSemaphorePropertiesKHR(mPhysicalDevice, &externalSemaphoreInfo, | |
| &externalSemaphoreProperties); | |
| constexpr VkExternalSemaphoreFeatureFlags kRequiredFeatures = | |
| VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT | VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT; | |
| if ((externalSemaphoreProperties.externalSemaphoreFeatures & kRequiredFeatures) != | |
| kRequiredFeatures) | |
| { | |
| return false; | |
| } | |
| return true; | |
| } | |
| VkResult VulkanHelper::createSemaphoreZirconEvent(VkSemaphore *semaphore) | |
| { | |
| VkExportSemaphoreCreateInfo exportSemaphoreCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .handleTypes = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA, | |
| }; | |
| VkSemaphoreCreateInfo semaphoreCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, | |
| /* .pNext = */ &exportSemaphoreCreateInfo, | |
| /* .flags = */ 0, | |
| }; | |
| return vkCreateSemaphore(mDevice, &semaphoreCreateInfo, nullptr, semaphore); | |
| } | |
| VkResult VulkanHelper::exportSemaphoreZirconEvent(VkSemaphore semaphore, zx_handle_t *event) | |
| { | |
| VkSemaphoreGetZirconHandleInfoFUCHSIA semaphoreGetZirconHandleInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_SEMAPHORE_GET_ZIRCON_HANDLE_INFO_FUCHSIA, | |
| /* .pNext = */ nullptr, | |
| /* .semaphore = */ semaphore, | |
| /* .handleType = */ VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_ZIRCON_EVENT_BIT_FUCHSIA, | |
| }; | |
| return vkGetSemaphoreZirconHandleFUCHSIA(mDevice, &semaphoreGetZirconHandleInfo, event); | |
| } | |
| void VulkanHelper::releaseImageAndSignalSemaphore(VkImage image, | |
| VkImageLayout oldLayout, | |
| VkImageLayout newLayout, | |
| VkSemaphore semaphore) | |
| { | |
| VkResult result; | |
| VkCommandBuffer commandBuffers[] = {VK_NULL_HANDLE}; | |
| constexpr uint32_t commandBufferCount = std::extent<decltype(commandBuffers)>(); | |
| VkCommandBufferAllocateInfo commandBufferAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .commandPool = */ mCommandPool, | |
| /* .level = */ VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
| /* .commandBufferCount = */ commandBufferCount, | |
| }; | |
| result = vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, commandBuffers); | |
| ASSERT(result == VK_SUCCESS); | |
| VkCommandBufferBeginInfo commandBufferBeginInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, | |
| /* .pInheritanceInfo = */ nullptr, | |
| }; | |
| result = vkBeginCommandBuffer(commandBuffers[0], &commandBufferBeginInfo); | |
| ASSERT(result == VK_SUCCESS); | |
| ImageMemoryBarrier(commandBuffers[0], image, mGraphicsQueueFamilyIndex, | |
| VK_QUEUE_FAMILY_EXTERNAL, oldLayout, newLayout); | |
| result = vkEndCommandBuffer(commandBuffers[0]); | |
| ASSERT(result == VK_SUCCESS); | |
| const VkSemaphore signalSemaphores[] = { | |
| semaphore, | |
| }; | |
| constexpr uint32_t signalSemaphoreCount = std::extent<decltype(signalSemaphores)>(); | |
| const VkSubmitInfo submits[] = { | |
| /* [0] = */ { | |
| /* .sType */ VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .waitSemaphoreCount = */ 0, | |
| /* .pWaitSemaphores = */ nullptr, | |
| /* .pWaitDstStageMask = */ nullptr, | |
| /* .commandBufferCount = */ commandBufferCount, | |
| /* .pCommandBuffers = */ commandBuffers, | |
| /* .signalSemaphoreCount = */ signalSemaphoreCount, | |
| /* .pSignalSemaphores = */ signalSemaphores, | |
| }, | |
| }; | |
| constexpr uint32_t submitCount = std::extent<decltype(submits)>(); | |
| const VkFence fence = VK_NULL_HANDLE; | |
| result = vkQueueSubmit(mGraphicsQueue, submitCount, submits, fence); | |
| ASSERT(result == VK_SUCCESS); | |
| } | |
| void VulkanHelper::waitSemaphoreAndAcquireImage(VkImage image, | |
| VkImageLayout oldLayout, | |
| VkImageLayout newLayout, | |
| VkSemaphore semaphore) | |
| { | |
| VkResult result; | |
| VkCommandBuffer commandBuffers[] = {VK_NULL_HANDLE}; | |
| constexpr uint32_t commandBufferCount = std::extent<decltype(commandBuffers)>(); | |
| VkCommandBufferAllocateInfo commandBufferAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .commandPool = */ mCommandPool, | |
| /* .level = */ VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
| /* .commandBufferCount = */ commandBufferCount, | |
| }; | |
| result = vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, commandBuffers); | |
| ASSERT(result == VK_SUCCESS); | |
| VkCommandBufferBeginInfo commandBufferBeginInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, | |
| /* .pInheritanceInfo = */ nullptr, | |
| }; | |
| result = vkBeginCommandBuffer(commandBuffers[0], &commandBufferBeginInfo); | |
| ASSERT(result == VK_SUCCESS); | |
| ImageMemoryBarrier(commandBuffers[0], image, VK_QUEUE_FAMILY_EXTERNAL, | |
| mGraphicsQueueFamilyIndex, oldLayout, newLayout); | |
| result = vkEndCommandBuffer(commandBuffers[0]); | |
| ASSERT(result == VK_SUCCESS); | |
| const VkSemaphore waitSemaphores[] = { | |
| semaphore, | |
| }; | |
| const VkPipelineStageFlags waitDstStageMasks[] = { | |
| VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | |
| }; | |
| constexpr uint32_t waitSemaphoreCount = std::extent<decltype(waitSemaphores)>(); | |
| constexpr uint32_t waitDstStageMaskCount = std::extent<decltype(waitDstStageMasks)>(); | |
| static_assert(waitSemaphoreCount == waitDstStageMaskCount, | |
| "waitSemaphores and waitDstStageMasks must be the same length"); | |
| const VkSubmitInfo submits[] = { | |
| /* [0] = */ { | |
| /* .sType */ VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .waitSemaphoreCount = */ waitSemaphoreCount, | |
| /* .pWaitSemaphores = */ waitSemaphores, | |
| /* .pWaitDstStageMask = */ waitDstStageMasks, | |
| /* .commandBufferCount = */ commandBufferCount, | |
| /* .pCommandBuffers = */ commandBuffers, | |
| /* .signalSemaphoreCount = */ 0, | |
| /* .pSignalSemaphores = */ nullptr, | |
| }, | |
| }; | |
| constexpr uint32_t submitCount = std::extent<decltype(submits)>(); | |
| const VkFence fence = VK_NULL_HANDLE; | |
| result = vkQueueSubmit(mGraphicsQueue, submitCount, submits, fence); | |
| ASSERT(result == VK_SUCCESS); | |
| } | |
| void VulkanHelper::readPixels(VkImage srcImage, | |
| VkImageLayout srcImageLayout, | |
| VkFormat srcImageFormat, | |
| VkOffset3D imageOffset, | |
| VkExtent3D imageExtent, | |
| void *pixels, | |
| size_t pixelsSize) | |
| { | |
| ASSERT(srcImageFormat == VK_FORMAT_B8G8R8A8_UNORM || | |
| srcImageFormat == VK_FORMAT_R8G8B8A8_UNORM); | |
| ASSERT(imageExtent.depth == 1); | |
| ASSERT(pixelsSize == 4 * imageExtent.width * imageExtent.height); | |
| VkBufferCreateInfo bufferCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ 0, | |
| /* .size = */ pixelsSize, | |
| /* .usage = */ VK_BUFFER_USAGE_TRANSFER_DST_BIT, | |
| /* .sharingMode = */ VK_SHARING_MODE_EXCLUSIVE, | |
| /* .queueFamilyIndexCount = */ 0, | |
| /* .pQueueFamilyIndices = */ nullptr, | |
| }; | |
| VkBuffer stagingBuffer = VK_NULL_HANDLE; | |
| VkResult result = vkCreateBuffer(mDevice, &bufferCreateInfo, nullptr, &stagingBuffer); | |
| ASSERT(result == VK_SUCCESS); | |
| VkMemoryPropertyFlags requestedMemoryPropertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; | |
| VkMemoryRequirements memoryRequirements; | |
| vkGetBufferMemoryRequirements(mDevice, stagingBuffer, &memoryRequirements); | |
| uint32_t memoryTypeIndex = FindMemoryType(mMemoryProperties, memoryRequirements.memoryTypeBits, | |
| requestedMemoryPropertyFlags); | |
| ASSERT(memoryTypeIndex != UINT32_MAX); | |
| VkDeviceSize deviceMemorySize = memoryRequirements.size; | |
| VkMemoryDedicatedAllocateInfoKHR memoryDedicatedAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, | |
| /* .pNext = */ nullptr, | |
| /* .image = */ VK_NULL_HANDLE, | |
| /* .buffer = */ stagingBuffer, | |
| }; | |
| VkMemoryAllocateInfo memoryAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | |
| /* .pNext = */ &memoryDedicatedAllocateInfo, | |
| /* .allocationSize = */ deviceMemorySize, | |
| /* .memoryTypeIndex = */ memoryTypeIndex, | |
| }; | |
| VkDeviceMemory deviceMemory = VK_NULL_HANDLE; | |
| result = vkAllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &deviceMemory); | |
| ASSERT(result == VK_SUCCESS); | |
| result = vkBindBufferMemory(mDevice, stagingBuffer, deviceMemory, 0 /* memoryOffset */); | |
| ASSERT(result == VK_SUCCESS); | |
| VkCommandBuffer commandBuffers[] = {VK_NULL_HANDLE}; | |
| constexpr uint32_t commandBufferCount = std::extent<decltype(commandBuffers)>(); | |
| VkCommandBufferAllocateInfo commandBufferAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .commandPool = */ mCommandPool, | |
| /* .level = */ VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
| /* .commandBufferCount = */ commandBufferCount, | |
| }; | |
| result = vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, commandBuffers); | |
| ASSERT(result == VK_SUCCESS); | |
| VkCommandBufferBeginInfo commandBufferBeginInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, | |
| /* .pInheritanceInfo = */ nullptr, | |
| }; | |
| result = vkBeginCommandBuffer(commandBuffers[0], &commandBufferBeginInfo); | |
| ASSERT(result == VK_SUCCESS); | |
| VkBufferImageCopy bufferImageCopies[] = { | |
| /* [0] = */ { | |
| /* .bufferOffset = */ 0, | |
| /* .bufferRowLength = */ 0, | |
| /* .bufferImageHeight = */ 0, | |
| /* .imageSubresources = */ | |
| { | |
| /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT, | |
| /* .mipLevel = */ 0, | |
| /* .baseArrayLayer = */ 0, | |
| /* .layerCount = */ 1, | |
| }, | |
| /* .imageOffset = */ imageOffset, | |
| /* .imageExtent = */ imageExtent, | |
| }, | |
| }; | |
| constexpr uint32_t bufferImageCopyCount = std::extent<decltype(bufferImageCopies)>(); | |
| if (srcImageLayout != VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL) | |
| { | |
| VkImageMemoryBarrier imageMemoryBarriers = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | |
| /* .pNext = */ nullptr, | |
| /* .srcAccessMask = */ VK_ACCESS_TRANSFER_WRITE_BIT, | |
| /* .dstAccessMask = */ VK_ACCESS_TRANSFER_READ_BIT, | |
| /* .oldLayout = */ srcImageLayout, | |
| /* .newLayout = */ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | |
| /* .srcQueueFamilyIndex = */ mGraphicsQueueFamilyIndex, | |
| /* .dstQueueFamilyIndex = */ mGraphicsQueueFamilyIndex, | |
| /* .image = */ srcImage, | |
| /* .subresourceRange = */ | |
| { | |
| /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT, | |
| /* .baseMipLevel = */ 0, | |
| /* .levelCount = */ 1, | |
| /* .baseArrayLayer = */ 0, | |
| /* .layerCount = */ 1, | |
| }, | |
| }; | |
| vkCmdPipelineBarrier(commandBuffers[0], VK_PIPELINE_STAGE_TRANSFER_BIT, | |
| VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, | |
| &imageMemoryBarriers); | |
| srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; | |
| } | |
| vkCmdCopyImageToBuffer(commandBuffers[0], srcImage, srcImageLayout, stagingBuffer, | |
| bufferImageCopyCount, bufferImageCopies); | |
| VkMemoryBarrier memoryBarriers[] = { | |
| /* [0] = */ {/* .sType = */ VK_STRUCTURE_TYPE_MEMORY_BARRIER, | |
| /* .pNext = */ nullptr, | |
| /* .srcAccessMask = */ VK_ACCESS_MEMORY_WRITE_BIT, | |
| /* .dstAccessMask = */ VK_ACCESS_HOST_READ_BIT}, | |
| }; | |
| constexpr uint32_t memoryBarrierCount = std::extent<decltype(memoryBarriers)>(); | |
| vkCmdPipelineBarrier(commandBuffers[0], VK_PIPELINE_STAGE_TRANSFER_BIT, | |
| VK_PIPELINE_STAGE_HOST_BIT, 0 /* dependencyFlags */, memoryBarrierCount, | |
| memoryBarriers, 0, nullptr, 0, nullptr); | |
| result = vkEndCommandBuffer(commandBuffers[0]); | |
| ASSERT(result == VK_SUCCESS); | |
| const VkSubmitInfo submits[] = { | |
| /* [0] = */ { | |
| /* .sType */ VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .waitSemaphoreCount = */ 0, | |
| /* .pWaitSemaphores = */ nullptr, | |
| /* .pWaitDstStageMask = */ nullptr, | |
| /* .commandBufferCount = */ commandBufferCount, | |
| /* .pCommandBuffers = */ commandBuffers, | |
| /* .signalSemaphoreCount = */ 0, | |
| /* .pSignalSemaphores = */ nullptr, | |
| }, | |
| }; | |
| constexpr uint32_t submitCount = std::extent<decltype(submits)>(); | |
| const VkFence fence = VK_NULL_HANDLE; | |
| result = vkQueueSubmit(mGraphicsQueue, submitCount, submits, fence); | |
| ASSERT(result == VK_SUCCESS); | |
| result = vkQueueWaitIdle(mGraphicsQueue); | |
| ASSERT(result == VK_SUCCESS); | |
| vkFreeCommandBuffers(mDevice, mCommandPool, commandBufferCount, commandBuffers); | |
| void *stagingMemory = nullptr; | |
| result = vkMapMemory(mDevice, deviceMemory, 0 /* offset */, deviceMemorySize, 0 /* flags */, | |
| &stagingMemory); | |
| ASSERT(result == VK_SUCCESS); | |
| VkMappedMemoryRange memoryRanges[] = { | |
| /* [0] = */ { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, | |
| /* .pNext = */ nullptr, | |
| /* .memory = */ deviceMemory, | |
| /* .offset = */ 0, | |
| /* .size = */ deviceMemorySize, | |
| }, | |
| }; | |
| constexpr uint32_t memoryRangeCount = std::extent<decltype(memoryRanges)>(); | |
| result = vkInvalidateMappedMemoryRanges(mDevice, memoryRangeCount, memoryRanges); | |
| ASSERT(result == VK_SUCCESS); | |
| memcpy(pixels, stagingMemory, pixelsSize); | |
| vkDestroyBuffer(mDevice, stagingBuffer, nullptr); | |
| vkUnmapMemory(mDevice, deviceMemory); | |
| vkFreeMemory(mDevice, deviceMemory, nullptr); | |
| } | |
| void VulkanHelper::writePixels(VkImage dstImage, | |
| VkImageLayout imageLayout, | |
| VkFormat imageFormat, | |
| VkOffset3D imageOffset, | |
| VkExtent3D imageExtent, | |
| const void *pixels, | |
| size_t pixelsSize) | |
| { | |
| ASSERT(imageFormat == VK_FORMAT_B8G8R8A8_UNORM || imageFormat == VK_FORMAT_R8G8B8A8_UNORM); | |
| ASSERT(imageExtent.depth == 1); | |
| ASSERT(pixelsSize == 4 * imageExtent.width * imageExtent.height); | |
| VkBufferCreateInfo bufferCreateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ 0, | |
| /* .size = */ pixelsSize, | |
| /* .usage = */ VK_BUFFER_USAGE_TRANSFER_SRC_BIT, | |
| /* .sharingMode = */ VK_SHARING_MODE_EXCLUSIVE, | |
| /* .queueFamilyIndexCount = */ 0, | |
| /* .pQueueFamilyIndices = */ nullptr, | |
| }; | |
| VkBuffer stagingBuffer = VK_NULL_HANDLE; | |
| VkResult result = vkCreateBuffer(mDevice, &bufferCreateInfo, nullptr, &stagingBuffer); | |
| ASSERT(result == VK_SUCCESS); | |
| VkMemoryPropertyFlags requestedMemoryPropertyFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; | |
| VkMemoryRequirements memoryRequirements; | |
| vkGetBufferMemoryRequirements(mDevice, stagingBuffer, &memoryRequirements); | |
| uint32_t memoryTypeIndex = FindMemoryType(mMemoryProperties, memoryRequirements.memoryTypeBits, | |
| requestedMemoryPropertyFlags); | |
| ASSERT(memoryTypeIndex != UINT32_MAX); | |
| VkDeviceSize deviceMemorySize = memoryRequirements.size; | |
| VkMemoryDedicatedAllocateInfoKHR memoryDedicatedAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR, | |
| /* .pNext = */ nullptr, | |
| /* .image = */ VK_NULL_HANDLE, | |
| /* .buffer = */ stagingBuffer, | |
| }; | |
| VkMemoryAllocateInfo memoryAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | |
| /* .pNext = */ &memoryDedicatedAllocateInfo, | |
| /* .allocationSize = */ deviceMemorySize, | |
| /* .memoryTypeIndex = */ memoryTypeIndex, | |
| }; | |
| VkDeviceMemory deviceMemory = VK_NULL_HANDLE; | |
| result = vkAllocateMemory(mDevice, &memoryAllocateInfo, nullptr, &deviceMemory); | |
| ASSERT(result == VK_SUCCESS); | |
| result = vkBindBufferMemory(mDevice, stagingBuffer, deviceMemory, 0 /* memoryOffset */); | |
| ASSERT(result == VK_SUCCESS); | |
| void *stagingMemory = nullptr; | |
| result = vkMapMemory(mDevice, deviceMemory, 0 /* offset */, deviceMemorySize, 0 /* flags */, | |
| &stagingMemory); | |
| ASSERT(result == VK_SUCCESS); | |
| VkMappedMemoryRange memoryRanges[] = { | |
| /* [0] = */ { | |
| /* .sType = */ VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, | |
| /* .pNext = */ nullptr, | |
| /* .memory = */ deviceMemory, | |
| /* .offset = */ 0, | |
| /* .size = */ deviceMemorySize, | |
| }, | |
| }; | |
| constexpr uint32_t memoryRangeCount = std::extent<decltype(memoryRanges)>(); | |
| result = vkInvalidateMappedMemoryRanges(mDevice, memoryRangeCount, memoryRanges); | |
| ASSERT(result == VK_SUCCESS); | |
| memcpy(stagingMemory, pixels, pixelsSize); | |
| vkUnmapMemory(mDevice, deviceMemory); | |
| VkCommandBuffer commandBuffers[] = {VK_NULL_HANDLE}; | |
| constexpr uint32_t commandBufferCount = std::extent<decltype(commandBuffers)>(); | |
| VkCommandBufferAllocateInfo commandBufferAllocateInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .commandPool = */ mCommandPool, | |
| /* .level = */ VK_COMMAND_BUFFER_LEVEL_PRIMARY, | |
| /* .commandBufferCount = */ commandBufferCount, | |
| }; | |
| result = vkAllocateCommandBuffers(mDevice, &commandBufferAllocateInfo, commandBuffers); | |
| ASSERT(result == VK_SUCCESS); | |
| VkCommandBufferBeginInfo commandBufferBeginInfo = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .flags = */ VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, | |
| /* .pInheritanceInfo = */ nullptr, | |
| }; | |
| result = vkBeginCommandBuffer(commandBuffers[0], &commandBufferBeginInfo); | |
| ASSERT(result == VK_SUCCESS); | |
| // Memory barrier for pipeline from Host-Write to Transfer-Read. | |
| VkMemoryBarrier memoryBarriers[] = { | |
| /* [0] = */ {/* .sType = */ VK_STRUCTURE_TYPE_MEMORY_BARRIER, | |
| /* .pNext = */ nullptr, | |
| /* .srcAccessMask = */ VK_ACCESS_HOST_WRITE_BIT, | |
| /* .dstAccessMask = */ VK_ACCESS_TRANSFER_READ_BIT}, | |
| }; | |
| constexpr uint32_t memoryBarrierCount = std::extent<decltype(memoryBarriers)>(); | |
| vkCmdPipelineBarrier(commandBuffers[0], VK_PIPELINE_STAGE_HOST_BIT, | |
| VK_PIPELINE_STAGE_TRANSFER_BIT, 0 /* dependencyFlags */, | |
| memoryBarrierCount, memoryBarriers, 0, nullptr, 0, nullptr); | |
| // Memory-barrier for image to Transfer-Write. | |
| if (imageLayout != VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) | |
| { | |
| VkImageMemoryBarrier imageMemoryBarriers = { | |
| /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | |
| /* .pNext = */ nullptr, | |
| /* .srcAccessMask = */ VK_ACCESS_NONE, | |
| /* .dstAccessMask = */ VK_ACCESS_TRANSFER_WRITE_BIT, | |
| /* .oldLayout = */ imageLayout, | |
| /* .newLayout = */ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, | |
| /* .srcQueueFamilyIndex = */ mGraphicsQueueFamilyIndex, | |
| /* .dstQueueFamilyIndex = */ mGraphicsQueueFamilyIndex, | |
| /* .image = */ dstImage, | |
| /* .subresourceRange = */ | |
| { | |
| /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT, | |
| /* .baseMipLevel = */ 0, | |
| /* .levelCount = */ 1, | |
| /* .baseArrayLayer = */ 0, | |
| /* .layerCount = */ 1, | |
| }, | |
| }; | |
| vkCmdPipelineBarrier(commandBuffers[0], VK_PIPELINE_STAGE_TRANSFER_BIT, | |
| VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, | |
| &imageMemoryBarriers); | |
| imageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; | |
| } | |
| // Issue the buffer to image copy. | |
| VkBufferImageCopy bufferImageCopies[] = { | |
| /* [0] = */ { | |
| /* .bufferOffset = */ 0, | |
| /* .bufferRowLength = */ 0, | |
| /* .bufferImageHeight = */ 0, | |
| /* .imageSubresources = */ | |
| { | |
| /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT, | |
| /* .mipLevel = */ 0, | |
| /* .baseArrayLayer = */ 0, | |
| /* .layerCount = */ 1, | |
| }, | |
| /* .imageOffset = */ imageOffset, | |
| /* .imageExtent = */ imageExtent, | |
| }, | |
| }; | |
| constexpr uint32_t bufferImageCopyCount = std::extent<decltype(bufferImageCopies)>(); | |
| vkCmdCopyBufferToImage(commandBuffers[0], stagingBuffer, dstImage, imageLayout, | |
| bufferImageCopyCount, bufferImageCopies); | |
| result = vkEndCommandBuffer(commandBuffers[0]); | |
| ASSERT(result == VK_SUCCESS); | |
| const VkSubmitInfo submits[] = { | |
| /* [0] = */ { | |
| /* .sType */ VK_STRUCTURE_TYPE_SUBMIT_INFO, | |
| /* .pNext = */ nullptr, | |
| /* .waitSemaphoreCount = */ 0, | |
| /* .pWaitSemaphores = */ nullptr, | |
| /* .pWaitDstStageMask = */ nullptr, | |
| /* .commandBufferCount = */ commandBufferCount, | |
| /* .pCommandBuffers = */ commandBuffers, | |
| /* .signalSemaphoreCount = */ 0, | |
| /* .pSignalSemaphores = */ nullptr, | |
| }, | |
| }; | |
| constexpr uint32_t submitCount = std::extent<decltype(submits)>(); | |
| const VkFence fence = VK_NULL_HANDLE; | |
| result = vkQueueSubmit(mGraphicsQueue, submitCount, submits, fence); | |
| ASSERT(result == VK_SUCCESS); | |
| result = vkQueueWaitIdle(mGraphicsQueue); | |
| ASSERT(result == VK_SUCCESS); | |
| vkFreeCommandBuffers(mDevice, mCommandPool, commandBufferCount, commandBuffers); | |
| vkDestroyBuffer(mDevice, stagingBuffer, nullptr); | |
| vkFreeMemory(mDevice, deviceMemory, nullptr); | |
| } | |
| } // namespace angle |