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
22QT_BEGIN_NAMESPACE
23
24static constexpr bool vwExtraDebug = 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
35struct QueueFamilyIndices {
36 int graphicsFamily = -1;
37 int presentFamily = -1;
38
39 bool isComplete() {
40 return graphicsFamily >= 0 && presentFamily >= 0;
41 }
42};
43
44class VulkanWrapperPrivate
45{
46public:
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
54private:
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
143private:
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
154struct 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
163int 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
179VulkanImageWrapper *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
245bool 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
295bool 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
326VkCommandBuffer 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
351void 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
368void 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, &region);
388
389 endSingleTimeCommands(commandBuffer);
390}
391
392void 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
406QueueFamilyIndices 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
436bool 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
472VulkanImageWrapper *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
477VulkanImageWrapper *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
529void 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
542VulkanWrapperPrivate::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
661VulkanWrapper::VulkanWrapper(QOpenGLContext *glContext)
662 : d_ptr(new VulkanWrapperPrivate(glContext))
663{
664}
665
666VulkanImageWrapper *VulkanWrapper::createTextureImage(const QImage &img)
667{
668 return d_ptr->createTextureImage(img);
669}
670
671VulkanImageWrapper *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
680int 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
691void VulkanWrapper::freeTextureImage(VulkanImageWrapper *imageWrapper)
692{
693 d_ptr->freeTextureImage(imageWrapper);
694}
695
696QT_END_NAMESPACE
697

source code of qtwayland/src/hardwareintegration/compositor/vulkan-server/vulkanwrapper.cpp