| 1 | // Copyright (C) 2019 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
| 3 | |
| 4 | // NOTE: Some of the code below is adapted from the public domain code at https://vulkan-tutorial.com/ |
| 5 | |
| 6 | #define GL_GLEXT_PROTOTYPES |
| 7 | |
| 8 | #include "vulkanwrapper.h" |
| 9 | |
| 10 | #include <QImage> |
| 11 | #include <QVarLengthArray> |
| 12 | #include <QOpenGLContext> |
| 13 | #include <QtGui/qopengl.h> |
| 14 | #include <QtOpenGL/private/qvkconvenience_p.h> |
| 15 | |
| 16 | #include <set> |
| 17 | |
| 18 | #include <unistd.h> |
| 19 | |
| 20 | #include <QDebug> |
| 21 | |
| 22 | QT_BEGIN_NAMESPACE |
| 23 | |
| 24 | static constexpr bool = false; |
| 25 | |
| 26 | #define DECL_VK_FUNCTION(name) \ |
| 27 | PFN_ ## name name = nullptr; |
| 28 | |
| 29 | #define IMPL_VK_FUNCTION(name) \ |
| 30 | name = reinterpret_cast<PFN_ ## name>(f_glGetVkProcAddrNV(#name)); \ |
| 31 | if (!name) { \ |
| 32 | qCritical() << "ERROR in Vulkan proc lookup. Could not find " #name; \ |
| 33 | } |
| 34 | |
| 35 | struct QueueFamilyIndices { |
| 36 | int graphicsFamily = -1; |
| 37 | int presentFamily = -1; |
| 38 | |
| 39 | bool isComplete() { |
| 40 | return graphicsFamily >= 0 && presentFamily >= 0; |
| 41 | } |
| 42 | }; |
| 43 | |
| 44 | class VulkanWrapperPrivate |
| 45 | { |
| 46 | public: |
| 47 | explicit VulkanWrapperPrivate(QOpenGLContext *glContext); |
| 48 | |
| 49 | VulkanImageWrapper *createTextureImage(const QImage &img); |
| 50 | VulkanImageWrapper *createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, VkFormat vkFormat); |
| 51 | |
| 52 | void freeTextureImage(VulkanImageWrapper *imageWrapper); |
| 53 | |
| 54 | private: |
| 55 | DECL_VK_FUNCTION(vkAllocateCommandBuffers); |
| 56 | DECL_VK_FUNCTION(vkAllocateMemory); |
| 57 | DECL_VK_FUNCTION(vkBeginCommandBuffer); |
| 58 | DECL_VK_FUNCTION(vkBindImageMemory); |
| 59 | DECL_VK_FUNCTION(vkCmdCopyBufferToImage); |
| 60 | DECL_VK_FUNCTION(vkCmdPipelineBarrier); |
| 61 | DECL_VK_FUNCTION(vkCreateImage); |
| 62 | DECL_VK_FUNCTION(vkDestroyImage); |
| 63 | DECL_VK_FUNCTION(vkDestroyBuffer); |
| 64 | DECL_VK_FUNCTION(vkEndCommandBuffer); |
| 65 | DECL_VK_FUNCTION(vkFreeCommandBuffers); |
| 66 | DECL_VK_FUNCTION(vkFreeMemory); |
| 67 | DECL_VK_FUNCTION(vkGetImageMemoryRequirements); |
| 68 | DECL_VK_FUNCTION(vkGetPhysicalDeviceMemoryProperties); |
| 69 | DECL_VK_FUNCTION(vkMapMemory); |
| 70 | DECL_VK_FUNCTION(vkQueueSubmit); |
| 71 | DECL_VK_FUNCTION(vkQueueWaitIdle); |
| 72 | DECL_VK_FUNCTION(vkUnmapMemory); |
| 73 | DECL_VK_FUNCTION(vkCreateBuffer); |
| 74 | DECL_VK_FUNCTION(vkGetBufferMemoryRequirements); |
| 75 | DECL_VK_FUNCTION(vkBindBufferMemory); |
| 76 | |
| 77 | DECL_VK_FUNCTION(vkCreateInstance); |
| 78 | DECL_VK_FUNCTION(vkEnumeratePhysicalDevices); |
| 79 | DECL_VK_FUNCTION(vkGetPhysicalDeviceProperties); |
| 80 | DECL_VK_FUNCTION(vkCreateDevice); |
| 81 | DECL_VK_FUNCTION(vkGetPhysicalDeviceFormatProperties); |
| 82 | |
| 83 | DECL_VK_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties); |
| 84 | DECL_VK_FUNCTION(vkCreateCommandPool); |
| 85 | |
| 86 | DECL_VK_FUNCTION(vkGetDeviceQueue); |
| 87 | DECL_VK_FUNCTION(vkGetImageMemoryRequirements2KHR); |
| 88 | DECL_VK_FUNCTION(vkGetMemoryFdKHR); |
| 89 | |
| 90 | //DECL_VK_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR); |
| 91 | |
| 92 | void initFunctions(PFNGLGETVKPROCADDRNVPROC f_glGetVkProcAddrNV) { |
| 93 | IMPL_VK_FUNCTION(vkAllocateCommandBuffers); |
| 94 | IMPL_VK_FUNCTION(vkAllocateMemory); |
| 95 | IMPL_VK_FUNCTION(vkBeginCommandBuffer); |
| 96 | IMPL_VK_FUNCTION(vkBindImageMemory); |
| 97 | IMPL_VK_FUNCTION(vkCmdCopyBufferToImage); |
| 98 | IMPL_VK_FUNCTION(vkCmdPipelineBarrier); |
| 99 | IMPL_VK_FUNCTION(vkCreateImage); |
| 100 | IMPL_VK_FUNCTION(vkDestroyImage); |
| 101 | IMPL_VK_FUNCTION(vkDestroyBuffer); |
| 102 | IMPL_VK_FUNCTION(vkEndCommandBuffer); |
| 103 | IMPL_VK_FUNCTION(vkFreeCommandBuffers); |
| 104 | IMPL_VK_FUNCTION(vkFreeMemory); |
| 105 | IMPL_VK_FUNCTION(vkGetImageMemoryRequirements); |
| 106 | IMPL_VK_FUNCTION(vkGetPhysicalDeviceMemoryProperties); |
| 107 | IMPL_VK_FUNCTION(vkMapMemory); |
| 108 | IMPL_VK_FUNCTION(vkQueueSubmit); |
| 109 | IMPL_VK_FUNCTION(vkQueueWaitIdle); |
| 110 | IMPL_VK_FUNCTION(vkUnmapMemory); |
| 111 | IMPL_VK_FUNCTION(vkCreateBuffer); |
| 112 | IMPL_VK_FUNCTION(vkGetBufferMemoryRequirements); |
| 113 | IMPL_VK_FUNCTION(vkBindBufferMemory); |
| 114 | |
| 115 | IMPL_VK_FUNCTION(vkCreateInstance); |
| 116 | IMPL_VK_FUNCTION(vkEnumeratePhysicalDevices); |
| 117 | IMPL_VK_FUNCTION(vkGetPhysicalDeviceProperties); |
| 118 | IMPL_VK_FUNCTION(vkCreateDevice); |
| 119 | IMPL_VK_FUNCTION(vkGetPhysicalDeviceFormatProperties); |
| 120 | |
| 121 | IMPL_VK_FUNCTION(vkGetPhysicalDeviceQueueFamilyProperties); |
| 122 | IMPL_VK_FUNCTION(vkCreateCommandPool); |
| 123 | |
| 124 | IMPL_VK_FUNCTION(vkGetDeviceQueue); |
| 125 | IMPL_VK_FUNCTION(vkGetImageMemoryRequirements2KHR); |
| 126 | IMPL_VK_FUNCTION(vkGetMemoryFdKHR); |
| 127 | |
| 128 | //IMPL_VK_FUNCTION(vkGetPhysicalDeviceSurfaceSupportKHR); |
| 129 | } |
| 130 | |
| 131 | int findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties); |
| 132 | |
| 133 | VulkanImageWrapper *createImage(VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, const QSize &size, int memSize); |
| 134 | bool transitionImageLayout(VkImage image, VkFormat /*format*/, VkImageLayout oldLayout, VkImageLayout newLayout); |
| 135 | bool createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory); |
| 136 | VkCommandBuffer beginSingleTimeCommands(); |
| 137 | void endSingleTimeCommands(VkCommandBuffer commandBuffer); |
| 138 | void copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height); |
| 139 | void createCommandPool(); |
| 140 | QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); |
| 141 | bool createLogicalDevice(); |
| 142 | |
| 143 | private: |
| 144 | VkInstance m_instance = VK_NULL_HANDLE; |
| 145 | VkPhysicalDevice m_physicalDevice = VK_NULL_HANDLE; |
| 146 | VkDevice m_device = VK_NULL_HANDLE; |
| 147 | VkCommandPool m_commandPool = VK_NULL_HANDLE; |
| 148 | |
| 149 | VkQueue m_graphicsQueue = VK_NULL_HANDLE; |
| 150 | |
| 151 | bool m_initFailed = false; |
| 152 | }; |
| 153 | |
| 154 | struct VulkanImageWrapper |
| 155 | { |
| 156 | VkImage textureImage = VK_NULL_HANDLE; |
| 157 | int imgMemSize = -1; |
| 158 | QSize imgSize; |
| 159 | int imgFd = -1; |
| 160 | VkDeviceMemory textureImageMemory = VK_NULL_HANDLE; |
| 161 | }; |
| 162 | |
| 163 | int VulkanWrapperPrivate::findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) |
| 164 | { |
| 165 | VkPhysicalDeviceMemoryProperties memProperties; |
| 166 | vkGetPhysicalDeviceMemoryProperties(m_physicalDevice, &memProperties); |
| 167 | |
| 168 | for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) { |
| 169 | if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) { |
| 170 | return i; |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | qCritical(msg: "VulkanWrapper: failed to find suitable memory type!" ); |
| 175 | return -1; |
| 176 | } |
| 177 | |
| 178 | |
| 179 | VulkanImageWrapper *VulkanWrapperPrivate::createImage(VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, const QSize &size, int memSize) |
| 180 | { |
| 181 | VkImageCreateInfo imageInfo = {}; |
| 182 | imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; |
| 183 | imageInfo.imageType = VK_IMAGE_TYPE_2D; |
| 184 | imageInfo.extent.width = size.width(); |
| 185 | imageInfo.extent.height = size.height(); |
| 186 | imageInfo.extent.depth = 1; |
| 187 | imageInfo.mipLevels = 1; |
| 188 | imageInfo.arrayLayers = 1; |
| 189 | imageInfo.format = format; |
| 190 | imageInfo.tiling = tiling; |
| 191 | imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| 192 | imageInfo.usage = usage; |
| 193 | imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; |
| 194 | imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| 195 | |
| 196 | VkImage image = VK_NULL_HANDLE; |
| 197 | |
| 198 | if (vkCreateImage(m_device, &imageInfo, nullptr, &image) != VK_SUCCESS) { |
| 199 | qCritical(msg: "VulkanWrapper: failed to create image!" ); |
| 200 | return nullptr; |
| 201 | } |
| 202 | |
| 203 | std::unique_ptr imageWrapper = std::make_unique<VulkanImageWrapper>(); |
| 204 | imageWrapper->textureImage = image; |
| 205 | imageWrapper->imgMemSize = memSize; |
| 206 | imageWrapper->imgSize = size; |
| 207 | |
| 208 | VkMemoryRequirements memRequirements; |
| 209 | vkGetImageMemoryRequirements(m_device, image, &memRequirements); |
| 210 | |
| 211 | VkExportMemoryAllocateInfoKHR exportAllocInfo = {}; |
| 212 | exportAllocInfo.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR; |
| 213 | exportAllocInfo.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; |
| 214 | |
| 215 | VkMemoryAllocateInfo allocInfo = {}; |
| 216 | allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
| 217 | allocInfo.allocationSize = memRequirements.size; |
| 218 | int memoryType = findMemoryType(typeFilter: memRequirements.memoryTypeBits, properties); |
| 219 | if (memoryType < 0) |
| 220 | return nullptr; |
| 221 | allocInfo.memoryTypeIndex = memoryType; |
| 222 | allocInfo.pNext = &exportAllocInfo; |
| 223 | |
| 224 | if (vkAllocateMemory(m_device, &allocInfo, nullptr, &imageWrapper->textureImageMemory) != VK_SUCCESS) { |
| 225 | qCritical(msg: "VulkanWrapper: failed to allocate image memory!" ); |
| 226 | return nullptr; |
| 227 | } |
| 228 | |
| 229 | int res = vkBindImageMemory(m_device, image, imageWrapper->textureImageMemory, 0); |
| 230 | Q_UNUSED(res); |
| 231 | if (vwExtraDebug) qDebug() << "vkBindImageMemory res" << res; |
| 232 | |
| 233 | VkMemoryGetFdInfoKHR memoryFdInfo = {}; |
| 234 | memoryFdInfo.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR; |
| 235 | memoryFdInfo.memory = imageWrapper->textureImageMemory; |
| 236 | memoryFdInfo.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR; |
| 237 | |
| 238 | res = vkGetMemoryFdKHR(m_device, &memoryFdInfo, &imageWrapper->imgFd); |
| 239 | if (vwExtraDebug) qDebug() << "vkGetMemoryFdKHR res" << res << "fd" << imageWrapper->imgFd; |
| 240 | |
| 241 | return imageWrapper.release(); |
| 242 | } |
| 243 | |
| 244 | |
| 245 | bool VulkanWrapperPrivate::transitionImageLayout(VkImage image, VkFormat /*format*/, VkImageLayout oldLayout, VkImageLayout newLayout) |
| 246 | { |
| 247 | VkCommandBuffer commandBuffer = beginSingleTimeCommands(); |
| 248 | |
| 249 | VkImageMemoryBarrier barrier = {}; |
| 250 | barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; |
| 251 | barrier.oldLayout = oldLayout; |
| 252 | barrier.newLayout = newLayout; |
| 253 | barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| 254 | barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; |
| 255 | barrier.image = image; |
| 256 | barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| 257 | barrier.subresourceRange.baseMipLevel = 0; |
| 258 | barrier.subresourceRange.levelCount = 1; |
| 259 | barrier.subresourceRange.baseArrayLayer = 0; |
| 260 | barrier.subresourceRange.layerCount = 1; |
| 261 | |
| 262 | VkPipelineStageFlags sourceStage; |
| 263 | VkPipelineStageFlags destinationStage; |
| 264 | |
| 265 | if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED && newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { |
| 266 | barrier.srcAccessMask = 0; |
| 267 | barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; |
| 268 | |
| 269 | sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; |
| 270 | destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT; |
| 271 | } else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { |
| 272 | barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; |
| 273 | barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; |
| 274 | |
| 275 | sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT; |
| 276 | destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; |
| 277 | } else { |
| 278 | qCritical(msg: "VulkanWrapper: unsupported layout transition!" ); |
| 279 | return false; |
| 280 | } |
| 281 | |
| 282 | vkCmdPipelineBarrier( |
| 283 | commandBuffer, |
| 284 | sourceStage, destinationStage, |
| 285 | 0, |
| 286 | 0, nullptr, |
| 287 | 0, nullptr, |
| 288 | 1, &barrier |
| 289 | ); |
| 290 | |
| 291 | endSingleTimeCommands(commandBuffer); |
| 292 | return true; |
| 293 | } |
| 294 | |
| 295 | bool VulkanWrapperPrivate::createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory) |
| 296 | { |
| 297 | VkBufferCreateInfo bufferInfo = {}; |
| 298 | bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; |
| 299 | bufferInfo.size = size; |
| 300 | bufferInfo.usage = usage; |
| 301 | bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| 302 | |
| 303 | if (vkCreateBuffer(m_device, &bufferInfo, nullptr, &buffer) != VK_SUCCESS) { |
| 304 | qCritical(msg: "VulkanWrapper: failed to create buffer!" ); |
| 305 | return false; |
| 306 | } |
| 307 | |
| 308 | VkMemoryRequirements memRequirements; |
| 309 | vkGetBufferMemoryRequirements(m_device, buffer, &memRequirements); |
| 310 | |
| 311 | VkMemoryAllocateInfo allocInfo = {}; |
| 312 | allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; |
| 313 | allocInfo.allocationSize = memRequirements.size; |
| 314 | allocInfo.memoryTypeIndex = findMemoryType(typeFilter: memRequirements.memoryTypeBits, properties); |
| 315 | |
| 316 | if (vkAllocateMemory(m_device, &allocInfo, nullptr, &bufferMemory) != VK_SUCCESS) { |
| 317 | qCritical(msg: "VulkanWrapper: failed to allocate buffer memory!" ); |
| 318 | return false; |
| 319 | } |
| 320 | |
| 321 | vkBindBufferMemory(m_device, buffer, bufferMemory, 0); |
| 322 | return true; |
| 323 | } |
| 324 | |
| 325 | |
| 326 | VkCommandBuffer VulkanWrapperPrivate::beginSingleTimeCommands() |
| 327 | { |
| 328 | VkCommandBufferAllocateInfo allocInfo = {}; |
| 329 | allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; |
| 330 | allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
| 331 | allocInfo.commandPool = m_commandPool; |
| 332 | allocInfo.commandBufferCount = 1; |
| 333 | |
| 334 | if (vwExtraDebug) qDebug() << "allocating..." ; |
| 335 | |
| 336 | VkCommandBuffer commandBuffer; |
| 337 | int res = vkAllocateCommandBuffers(m_device, &allocInfo, &commandBuffer); |
| 338 | Q_UNUSED(res); |
| 339 | if (vwExtraDebug) qDebug() << "vkAllocateCommandBuffers res" << res; |
| 340 | |
| 341 | VkCommandBufferBeginInfo beginInfo = {}; |
| 342 | beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; |
| 343 | beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; |
| 344 | |
| 345 | res = vkBeginCommandBuffer(commandBuffer, &beginInfo); |
| 346 | if (vwExtraDebug) qDebug() << "BEGIN res" << res; |
| 347 | |
| 348 | return commandBuffer; |
| 349 | } |
| 350 | |
| 351 | void VulkanWrapperPrivate::endSingleTimeCommands(VkCommandBuffer commandBuffer) |
| 352 | { |
| 353 | int res = vkEndCommandBuffer(commandBuffer); |
| 354 | Q_UNUSED(res); |
| 355 | if (vwExtraDebug) qDebug() << "END res" << res; |
| 356 | |
| 357 | VkSubmitInfo submitInfo = {}; |
| 358 | submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
| 359 | submitInfo.commandBufferCount = 1; |
| 360 | submitInfo.pCommandBuffers = &commandBuffer; |
| 361 | |
| 362 | vkQueueSubmit(m_graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE); |
| 363 | vkQueueWaitIdle(m_graphicsQueue); |
| 364 | |
| 365 | vkFreeCommandBuffers(m_device, m_commandPool, 1, &commandBuffer); |
| 366 | } |
| 367 | |
| 368 | void VulkanWrapperPrivate::copyBufferToImage(VkBuffer buffer, VkImage image, uint32_t width, uint32_t height) |
| 369 | { |
| 370 | VkCommandBuffer commandBuffer = beginSingleTimeCommands(); |
| 371 | |
| 372 | VkBufferImageCopy region = {}; |
| 373 | region.bufferOffset = 0; |
| 374 | region.bufferRowLength = 0; |
| 375 | region.bufferImageHeight = 0; |
| 376 | region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| 377 | region.imageSubresource.mipLevel = 0; |
| 378 | region.imageSubresource.baseArrayLayer = 0; |
| 379 | region.imageSubresource.layerCount = 1; |
| 380 | region.imageOffset = {.x: 0, .y: 0, .z: 0}; |
| 381 | region.imageExtent = { |
| 382 | .width: width, |
| 383 | .height: height, |
| 384 | .depth: 1 |
| 385 | }; |
| 386 | |
| 387 | vkCmdCopyBufferToImage(commandBuffer, buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); |
| 388 | |
| 389 | endSingleTimeCommands(commandBuffer); |
| 390 | } |
| 391 | |
| 392 | void VulkanWrapperPrivate::createCommandPool() |
| 393 | { |
| 394 | QueueFamilyIndices queueFamilyIndices = findQueueFamilies(device: m_physicalDevice); |
| 395 | |
| 396 | VkCommandPoolCreateInfo poolInfo = {}; |
| 397 | poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; |
| 398 | poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily; |
| 399 | |
| 400 | if (vkCreateCommandPool(m_device, &poolInfo, nullptr, &m_commandPool) != VK_SUCCESS) { |
| 401 | m_initFailed = true; |
| 402 | qCritical(msg: "VulkanWrapperPrivate: could not create command pool" ); |
| 403 | } |
| 404 | } |
| 405 | |
| 406 | QueueFamilyIndices VulkanWrapperPrivate::findQueueFamilies(VkPhysicalDevice device) |
| 407 | { |
| 408 | QueueFamilyIndices indices; |
| 409 | |
| 410 | uint32_t queueFamilyCount = 0; |
| 411 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, nullptr); |
| 412 | if (vwExtraDebug) qDebug() << "queueFamilyCount" << queueFamilyCount; |
| 413 | |
| 414 | |
| 415 | std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); |
| 416 | vkGetPhysicalDeviceQueueFamilyProperties(device, &queueFamilyCount, queueFamilies.data()); |
| 417 | |
| 418 | #ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG |
| 419 | for (const auto& queueFamily : queueFamilies) { |
| 420 | qDebug() << "....q" << "count" << queueFamily.queueCount << queueFamily.timestampValidBits << hex << queueFamily.queueFlags; |
| 421 | } |
| 422 | #endif |
| 423 | |
| 424 | int i = 0; |
| 425 | for (const auto& queueFamily : queueFamilies) { |
| 426 | if (queueFamily.queueCount > 0 && queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) { |
| 427 | indices.graphicsFamily = i; |
| 428 | break; |
| 429 | } |
| 430 | i++; |
| 431 | } |
| 432 | |
| 433 | return indices; |
| 434 | } |
| 435 | |
| 436 | bool VulkanWrapperPrivate::createLogicalDevice() |
| 437 | { |
| 438 | QueueFamilyIndices indices = findQueueFamilies(device: m_physicalDevice); |
| 439 | |
| 440 | std::vector<VkDeviceQueueCreateInfo> queueCreateInfos; |
| 441 | std::set<int> uniqueQueueFamilies = {indices.graphicsFamily}; //////, indices.presentFamily}; |
| 442 | |
| 443 | float queuePriority = 1.0f; |
| 444 | for (int queueFamily : uniqueQueueFamilies) { |
| 445 | VkDeviceQueueCreateInfo queueCreateInfo = {}; |
| 446 | queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
| 447 | queueCreateInfo.queueFamilyIndex = queueFamily; |
| 448 | queueCreateInfo.queueCount = 1; |
| 449 | queueCreateInfo.pQueuePriorities = &queuePriority; |
| 450 | queueCreateInfos.push_back(x: queueCreateInfo); |
| 451 | } |
| 452 | |
| 453 | VkPhysicalDeviceFeatures deviceFeatures = {}; |
| 454 | |
| 455 | VkDeviceCreateInfo createInfo = {}; |
| 456 | createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; |
| 457 | |
| 458 | createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size()); |
| 459 | createInfo.pQueueCreateInfos = queueCreateInfos.data(); |
| 460 | |
| 461 | createInfo.pEnabledFeatures = &deviceFeatures; |
| 462 | |
| 463 | if (vkCreateDevice(m_physicalDevice, &createInfo, nullptr, &m_device) != VK_SUCCESS) { |
| 464 | qCritical(msg: "VulkanWrapper: failed to create logical device!" ); |
| 465 | return false; |
| 466 | } |
| 467 | |
| 468 | vkGetDeviceQueue(m_device, indices.graphicsFamily, 0, &m_graphicsQueue); |
| 469 | return true; |
| 470 | } |
| 471 | |
| 472 | VulkanImageWrapper *VulkanWrapperPrivate::createTextureImage(const QImage &img) |
| 473 | { |
| 474 | return createTextureImageFromData(pixels: img.constBits(), bufferSize: img.sizeInBytes(), size: img.size(), vkFormat: VK_FORMAT_R8G8B8A8_UNORM); |
| 475 | } |
| 476 | |
| 477 | VulkanImageWrapper *VulkanWrapperPrivate::createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, VkFormat vkFormat) |
| 478 | { |
| 479 | if (m_initFailed) |
| 480 | return nullptr; |
| 481 | |
| 482 | int texWidth = size.width(); |
| 483 | int texHeight = size.height(); |
| 484 | bool ok; |
| 485 | if (vwExtraDebug) qDebug(msg: "image load %p %dx%d" , pixels, texWidth, texHeight); |
| 486 | if (!pixels) { |
| 487 | qCritical(msg: "VulkanWrapper: failed to load texture image!" ); |
| 488 | return nullptr; |
| 489 | } |
| 490 | |
| 491 | VkBuffer stagingBuffer; |
| 492 | VkDeviceMemory stagingBufferMemory; |
| 493 | ok = createBuffer(size: bufferSize, usage: VK_BUFFER_USAGE_TRANSFER_SRC_BIT, properties: VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, buffer&: stagingBuffer, bufferMemory&: stagingBufferMemory); |
| 494 | |
| 495 | if (!ok) |
| 496 | return nullptr; |
| 497 | |
| 498 | void* data; |
| 499 | vkMapMemory(m_device, stagingBufferMemory, 0, bufferSize, 0, &data); |
| 500 | if (vwExtraDebug) qDebug() << "mapped" << data << bufferSize; |
| 501 | memcpy(dest: data, src: pixels, n: static_cast<size_t>(bufferSize)); |
| 502 | vkUnmapMemory(m_device, stagingBufferMemory); |
| 503 | |
| 504 | if (vwExtraDebug) qDebug() << "creating image..." ; |
| 505 | |
| 506 | std::unique_ptr<VulkanImageWrapper> imageWrapper(createImage(format: vkFormat, tiling: VK_IMAGE_TILING_OPTIMAL, usage: VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, properties: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, size, memSize: bufferSize)); |
| 507 | if (!imageWrapper) |
| 508 | return nullptr; |
| 509 | |
| 510 | if (vwExtraDebug) qDebug() << "transition..." ; |
| 511 | |
| 512 | const VkImage textureImage = imageWrapper->textureImage; |
| 513 | |
| 514 | ok = transitionImageLayout(image: textureImage, vkFormat, oldLayout: VK_IMAGE_LAYOUT_UNDEFINED, newLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); |
| 515 | |
| 516 | if (!ok) |
| 517 | return nullptr; |
| 518 | |
| 519 | if (vwExtraDebug) qDebug() << "copyBufferToImage..." ; |
| 520 | copyBufferToImage(buffer: stagingBuffer, image: textureImage, width: static_cast<uint32_t>(texWidth), height: static_cast<uint32_t>(texHeight)); |
| 521 | transitionImageLayout(image: textureImage, vkFormat, oldLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, newLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); |
| 522 | |
| 523 | vkDestroyBuffer(m_device, stagingBuffer, nullptr); |
| 524 | vkFreeMemory(m_device, stagingBufferMemory, nullptr); |
| 525 | |
| 526 | return imageWrapper.release(); |
| 527 | } |
| 528 | |
| 529 | void VulkanWrapperPrivate::freeTextureImage(VulkanImageWrapper *imageWrapper) |
| 530 | { |
| 531 | if (!imageWrapper) |
| 532 | return; |
| 533 | |
| 534 | //"To avoid leaking resources, the application must release ownership of the file descriptor using the close system call" |
| 535 | ::close(fd: imageWrapper->imgFd); |
| 536 | |
| 537 | // clean up the image memory |
| 538 | vkDestroyImage(m_device, imageWrapper->textureImage, nullptr); |
| 539 | vkFreeMemory(m_device, imageWrapper->textureImageMemory, nullptr); |
| 540 | } |
| 541 | |
| 542 | VulkanWrapperPrivate::VulkanWrapperPrivate(QOpenGLContext *glContext) |
| 543 | { |
| 544 | if (vwExtraDebug) qDebug(msg: "Creating Vulkan instance" ); |
| 545 | VkApplicationInfo applicationInfo = {}; |
| 546 | applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; |
| 547 | applicationInfo.pNext = nullptr; |
| 548 | applicationInfo.pApplicationName = nullptr; |
| 549 | applicationInfo.applicationVersion = 0; |
| 550 | applicationInfo.pEngineName = nullptr; |
| 551 | applicationInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); |
| 552 | applicationInfo.apiVersion = VK_MAKE_VERSION(1, 0, 5); |
| 553 | |
| 554 | VkInstanceCreateInfo instanceCreateInfo = {}; |
| 555 | instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
| 556 | instanceCreateInfo.pNext = nullptr; |
| 557 | instanceCreateInfo.flags = 0; |
| 558 | instanceCreateInfo.pApplicationInfo = &applicationInfo; |
| 559 | instanceCreateInfo.enabledLayerCount = 0; |
| 560 | instanceCreateInfo.ppEnabledLayerNames = nullptr; |
| 561 | instanceCreateInfo.enabledExtensionCount = 0; |
| 562 | instanceCreateInfo.ppEnabledExtensionNames = nullptr; |
| 563 | |
| 564 | auto f_glGetVkProcAddrNV = reinterpret_cast<PFNGLGETVKPROCADDRNVPROC>(glContext->getProcAddress(procName: "glGetVkProcAddrNV" )); |
| 565 | |
| 566 | if (!f_glGetVkProcAddrNV) { |
| 567 | qCritical(msg: "VulkanWrapper: Could not find Vulkan/GL interop function glGetVkProcAddrNV" ); |
| 568 | m_initFailed = true; |
| 569 | return; |
| 570 | } |
| 571 | |
| 572 | initFunctions(f_glGetVkProcAddrNV); |
| 573 | |
| 574 | VkResult instanceCreationResult = vkCreateInstance(&instanceCreateInfo, nullptr, &m_instance); |
| 575 | |
| 576 | if (vwExtraDebug) qDebug() << "result" << instanceCreationResult; |
| 577 | |
| 578 | if (instanceCreationResult != VK_SUCCESS) { |
| 579 | qCritical() << "VulkanWrapper: Failed to create Vulkan instance: Error " |
| 580 | << instanceCreationResult; |
| 581 | m_initFailed = true; |
| 582 | return; |
| 583 | } |
| 584 | |
| 585 | uint32_t devCount; |
| 586 | |
| 587 | auto res = vkEnumeratePhysicalDevices(m_instance, &devCount, nullptr); |
| 588 | if (vwExtraDebug) qDebug() << "vkEnumeratePhysicalDevices res =" << res << "count =" << devCount; |
| 589 | |
| 590 | QVarLengthArray<VkPhysicalDevice, 5> dev(devCount); |
| 591 | |
| 592 | res = vkEnumeratePhysicalDevices(m_instance, &devCount, dev.data()); |
| 593 | if (vwExtraDebug) qDebug() << "...devs res =" << res << "count =" << devCount; |
| 594 | |
| 595 | #ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG |
| 596 | VkPhysicalDeviceProperties props; |
| 597 | |
| 598 | vkGetPhysicalDeviceProperties(dev[0], &props); |
| 599 | |
| 600 | qDebug() << "Properties " << hex |
| 601 | << "apiVersion" << props.apiVersion |
| 602 | << "driverVersion" << props.driverVersion |
| 603 | << "vendorID" << props.vendorID |
| 604 | << "deviceID" << props.deviceID |
| 605 | << "deviceType" << props.deviceType |
| 606 | << "deviceName" << props.deviceName; |
| 607 | #endif |
| 608 | |
| 609 | m_physicalDevice = dev[0]; //TODO handle the case of multiple GPUs where only some support Vulkan |
| 610 | |
| 611 | bool ok = createLogicalDevice(); |
| 612 | if (!ok) { |
| 613 | qCritical(msg: "VulkanWrapperPrivate: could not create logical device" ); |
| 614 | m_initFailed = true; |
| 615 | return; |
| 616 | } |
| 617 | |
| 618 | VkPhysicalDeviceMemoryProperties memProps; |
| 619 | |
| 620 | |
| 621 | vkGetPhysicalDeviceMemoryProperties(dev[0], &memProps); |
| 622 | |
| 623 | #ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG |
| 624 | qDebug() << "Physical memory properties:\n" << "types:" << memProps.memoryTypeCount << "heaps:" << memProps.memoryHeapCount; |
| 625 | for (uint i = 0; i < memProps.memoryTypeCount; ++i) |
| 626 | qDebug() << " " << i << "heap" << memProps.memoryTypes[i].heapIndex << "flags" << hex << memProps.memoryTypes[i].propertyFlags; |
| 627 | |
| 628 | for (uint i = 0; i < memProps.memoryHeapCount; ++i) |
| 629 | qDebug() << " " << i << "size" << memProps.memoryHeaps[i].size << "flags" << hex << memProps.memoryHeaps[i].flags; |
| 630 | #endif |
| 631 | |
| 632 | int gpuMemoryType = -1; |
| 633 | |
| 634 | for (uint i = 0; i < memProps.memoryTypeCount; ++i) { |
| 635 | if (memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { |
| 636 | gpuMemoryType = i; |
| 637 | break; |
| 638 | } |
| 639 | } |
| 640 | |
| 641 | if (gpuMemoryType < 0) { |
| 642 | qCritical(msg: "VulkanWrapper: Could not find GPU memory!" ); |
| 643 | m_initFailed = true; |
| 644 | return; |
| 645 | } |
| 646 | |
| 647 | #ifdef VULKAN_SERVER_BUFFER_EXTRA_DEBUG |
| 648 | qDebug() << "GPU memory type:" << gpuMemoryType << "heap:" << memProps.memoryTypes[gpuMemoryType].heapIndex; |
| 649 | |
| 650 | for (int f = 0; f <= VK_FORMAT_ASTC_12x12_SRGB_BLOCK; f++) |
| 651 | { |
| 652 | VkFormatProperties formatProps; |
| 653 | vkGetPhysicalDeviceFormatProperties(dev[0], VkFormat(f), &formatProps); |
| 654 | qDebug() << "format" << f << "features" << hex << formatProps.linearTilingFeatures << formatProps.optimalTilingFeatures << formatProps.bufferFeatures; |
| 655 | } |
| 656 | #endif |
| 657 | createCommandPool(); |
| 658 | } |
| 659 | |
| 660 | |
| 661 | VulkanWrapper::VulkanWrapper(QOpenGLContext *glContext) |
| 662 | : d_ptr(new VulkanWrapperPrivate(glContext)) |
| 663 | { |
| 664 | } |
| 665 | |
| 666 | VulkanImageWrapper *VulkanWrapper::createTextureImage(const QImage &img) |
| 667 | { |
| 668 | return d_ptr->createTextureImage(img); |
| 669 | } |
| 670 | |
| 671 | VulkanImageWrapper *VulkanWrapper::createTextureImageFromData(const uchar *pixels, uint bufferSize, const QSize &size, uint glInternalFormat) |
| 672 | { |
| 673 | VkFormat vkFormat = VkFormat(QVkConvenience::vkFormatFromGlFormat(glFormat: glInternalFormat)); |
| 674 | if (vkFormat == VK_FORMAT_UNDEFINED) |
| 675 | return nullptr; |
| 676 | |
| 677 | return d_ptr->createTextureImageFromData(pixels, bufferSize, size, vkFormat); |
| 678 | } |
| 679 | |
| 680 | int VulkanWrapper::getImageInfo(const VulkanImageWrapper *imgWrapper, int *memSize, int *w, int *h) |
| 681 | { |
| 682 | if (memSize) |
| 683 | *memSize = imgWrapper->imgMemSize; |
| 684 | if (w) |
| 685 | *w = imgWrapper->imgSize.width(); |
| 686 | if (h) |
| 687 | *h = imgWrapper->imgSize.height(); |
| 688 | return imgWrapper->imgFd; |
| 689 | } |
| 690 | |
| 691 | void VulkanWrapper::freeTextureImage(VulkanImageWrapper *imageWrapper) |
| 692 | { |
| 693 | d_ptr->freeTextureImage(imageWrapper); |
| 694 | } |
| 695 | |
| 696 | QT_END_NAMESPACE |
| 697 | |