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