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