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 | |