Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2019 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the Qt Gui module
7**
8** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include "qrhivulkan_p_p.h"
38#include "qrhivulkanext_p.h"
39
40#define VMA_IMPLEMENTATION
41#define VMA_STATIC_VULKAN_FUNCTIONS 0
42#define VMA_RECORDING_ENABLED 0
43#define VMA_DEDICATED_ALLOCATION 0
44#ifdef QT_DEBUG
45#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1
46#endif
47#include "vk_mem_alloc.h"
48
49#include <qmath.h>
50#include <QVulkanFunctions>
51#include <QVulkanWindow>
52
53QT_BEGIN_NAMESPACE
54
55/*
56 Vulkan 1.0 backend. Provides a double-buffered swapchain that throttles the
57 rendering thread to vsync. Textures and "static" buffers are device local,
58 and a separate, host visible staging buffer is used to upload data to them.
59 "Dynamic" buffers are in host visible memory and are duplicated (since there
60 can be 2 frames in flight). This is handled transparently to the application.
61*/
62
63/*!
64 \class QRhiVulkanInitParams
65 \inmodule QtRhi
66 \brief Vulkan specific initialization parameters.
67
68 A Vulkan-based QRhi needs at minimum a valid QVulkanInstance. It is up to
69 the user to ensure this is available and initialized. This is typically
70 done in main() similarly to the following:
71
72 \badcode
73 int main(int argc, char **argv)
74 {
75 ...
76
77 QVulkanInstance inst;
78 #ifndef Q_OS_ANDROID
79 inst.setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
80 #else
81 inst.setLayers(QByteArrayList()
82 << "VK_LAYER_GOOGLE_threading"
83 << "VK_LAYER_LUNARG_parameter_validation"
84 << "VK_LAYER_LUNARG_object_tracker"
85 << "VK_LAYER_LUNARG_core_validation"
86 << "VK_LAYER_LUNARG_image"
87 << "VK_LAYER_LUNARG_swapchain"
88 << "VK_LAYER_GOOGLE_unique_objects");
89 #endif
90 inst.setExtensions(QByteArrayList()
91 << "VK_KHR_get_physical_device_properties2");
92 if (!inst.create())
93 qFatal("Vulkan not available");
94
95 ...
96 }
97 \endcode
98
99 The example here has two optional aspects: it enables the
100 \l{https://github.com/KhronosGroup/Vulkan-ValidationLayers}{Vulkan
101 validation layers}, when they are available, and also enables the
102 VK_KHR_get_physical_device_properties2 extension (part of Vulkan 1.1), when
103 available. The former is useful during the development phase (remember that
104 QVulkanInstance conveniently redirects messages and warnings to qDebug).
105 Avoid enabling it in production builds, however. The latter is important in
106 order to make QRhi::CustomInstanceStepRate available with Vulkan since
107 VK_EXT_vertex_attribute_divisor (part of Vulkan 1.1) depends on it. It can
108 be omitted when instanced drawing with a non-one step rate is not used.
109
110 Once this is done, a Vulkan-based QRhi can be created by passing the
111 instance and a QWindow with its surface type set to
112 QSurface::VulkanSurface:
113
114 \badcode
115 QRhiVulkanInitParams params;
116 params.inst = vulkanInstance;
117 params.window = window;
118 rhi = QRhi::create(QRhi::Vulkan, &params);
119 \endcode
120
121 The window is optional and can be omitted. This is not recommended however
122 because there is then no way to ensure presenting is supported while
123 choosing a graphics queue.
124
125 \note Even when a window is specified, QRhiSwapChain objects can be created
126 for other windows as well, as long as they all have their
127 QWindow::surfaceType() set to QSurface::VulkanSurface.
128
129 \section2 Working with existing Vulkan devices
130
131 When interoperating with another graphics engine, it may be necessary to
132 get a QRhi instance that uses the same Vulkan device. This can be achieved
133 by passing a pointer to a QRhiVulkanNativeHandles to QRhi::create().
134
135 The physical device and device object must then be set to a non-null value.
136 In addition, either the graphics queue family index or the graphics queue
137 object itself is required. Prefer the former, whenever possible since
138 deducing the index is not possible afterwards. Optionally, an existing
139 command pool object can be specified as well, and, also optionally,
140 vmemAllocator can be used to share the same
141 \l{https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator}{Vulkan
142 memory allocator} between two QRhi instances.
143
144 The QRhi does not take ownership of any of the external objects.
145 */
146
147/*!
148 \class QRhiVulkanNativeHandles
149 \inmodule QtRhi
150 \brief Collects device, queue, and other Vulkan objects that are used by the QRhi.
151
152 \note Ownership of the Vulkan objects is never transferred.
153 */
154
155/*!
156 \class QRhiVulkanTextureNativeHandles
157 \inmodule QtRhi
158 \brief Holds the Vulkan image object that is backing a QRhiTexture.
159
160 Importing and exporting Vulkan image objects that back a QRhiTexture when
161 running with the Vulkan backend is supported via this class. Ownership of
162 the Vulkan object is never transferred.
163
164 \note Memory allocation details are not exposed. This is intentional since
165 memory is typically suballocated from a bigger chunk of VkDeviceMemory, and
166 exposing the allocator details is not desirable for now.
167 */
168
169/*!
170 \class QRhiVulkanCommandBufferNativeHandles
171 \inmodule QtRhi
172 \brief Holds the Vulkan command buffer object that is backing a QRhiCommandBuffer.
173
174 \note The Vulkan command buffer object is only guaranteed to be valid, and
175 in recording state, while recording a frame. That is, between a
176 \l{QRhi::beginFrame()}{beginFrame()} - \l{QRhi::endFrame()}{endFrame()} or
177 \l{QRhi::beginOffscreenFrame()}{beginOffscreenFrame()} -
178 \l{QRhi::endOffsrceenFrame()}{endOffscreenFrame()} pair.
179 */
180
181static inline VkDeviceSize aligned(VkDeviceSize v, VkDeviceSize byteAlign)
182{
183 return (v + byteAlign - 1) & ~(byteAlign - 1);
184}
185
186static QVulkanInstance *globalVulkanInstance;
187
188static void VKAPI_PTR wrap_vkGetPhysicalDeviceProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceProperties* pProperties)
189{
190 globalVulkanInstance->functions()->vkGetPhysicalDeviceProperties(physicalDevice, pProperties);
191}
192
193static void VKAPI_PTR wrap_vkGetPhysicalDeviceMemoryProperties(VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties)
194{
195 globalVulkanInstance->functions()->vkGetPhysicalDeviceMemoryProperties(physicalDevice, pMemoryProperties);
196}
197
198static VkResult VKAPI_PTR wrap_vkAllocateMemory(VkDevice device, const VkMemoryAllocateInfo* pAllocateInfo, const VkAllocationCallbacks* pAllocator, VkDeviceMemory* pMemory)
199{
200 return globalVulkanInstance->deviceFunctions(device)->vkAllocateMemory(device, pAllocateInfo, pAllocator, pMemory);
201}
202
203void VKAPI_PTR wrap_vkFreeMemory(VkDevice device, VkDeviceMemory memory, const VkAllocationCallbacks* pAllocator)
204{
205 globalVulkanInstance->deviceFunctions(device)->vkFreeMemory(device, memory, pAllocator);
206}
207
208VkResult VKAPI_PTR wrap_vkMapMemory(VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void** ppData)
209{
210 return globalVulkanInstance->deviceFunctions(device)->vkMapMemory(device, memory, offset, size, flags, ppData);
211}
212
213void VKAPI_PTR wrap_vkUnmapMemory(VkDevice device, VkDeviceMemory memory)
214{
215 globalVulkanInstance->deviceFunctions(device)->vkUnmapMemory(device, memory);
216}
217
218VkResult VKAPI_PTR wrap_vkFlushMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
219{
220 return globalVulkanInstance->deviceFunctions(device)->vkFlushMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
221}
222
223VkResult VKAPI_PTR wrap_vkInvalidateMappedMemoryRanges(VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange* pMemoryRanges)
224{
225 return globalVulkanInstance->deviceFunctions(device)->vkInvalidateMappedMemoryRanges(device, memoryRangeCount, pMemoryRanges);
226}
227
228VkResult VKAPI_PTR wrap_vkBindBufferMemory(VkDevice device, VkBuffer buffer, VkDeviceMemory memory, VkDeviceSize memoryOffset)
229{
230 return globalVulkanInstance->deviceFunctions(device)->vkBindBufferMemory(device, buffer, memory, memoryOffset);
231}
232
233VkResult VKAPI_PTR wrap_vkBindImageMemory(VkDevice device, VkImage image, VkDeviceMemory memory, VkDeviceSize memoryOffset)
234{
235 return globalVulkanInstance->deviceFunctions(device)->vkBindImageMemory(device, image, memory, memoryOffset);
236}
237
238void VKAPI_PTR wrap_vkGetBufferMemoryRequirements(VkDevice device, VkBuffer buffer, VkMemoryRequirements* pMemoryRequirements)
239{
240 globalVulkanInstance->deviceFunctions(device)->vkGetBufferMemoryRequirements(device, buffer, pMemoryRequirements);
241}
242
243void VKAPI_PTR wrap_vkGetImageMemoryRequirements(VkDevice device, VkImage image, VkMemoryRequirements* pMemoryRequirements)
244{
245 globalVulkanInstance->deviceFunctions(device)->vkGetImageMemoryRequirements(device, image, pMemoryRequirements);
246}
247
248VkResult VKAPI_PTR wrap_vkCreateBuffer(VkDevice device, const VkBufferCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkBuffer* pBuffer)
249{
250 return globalVulkanInstance->deviceFunctions(device)->vkCreateBuffer(device, pCreateInfo, pAllocator, pBuffer);
251}
252
253void VKAPI_PTR wrap_vkDestroyBuffer(VkDevice device, VkBuffer buffer, const VkAllocationCallbacks* pAllocator)
254{
255 globalVulkanInstance->deviceFunctions(device)->vkDestroyBuffer(device, buffer, pAllocator);
256}
257
258VkResult VKAPI_PTR wrap_vkCreateImage(VkDevice device, const VkImageCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkImage* pImage)
259{
260 return globalVulkanInstance->deviceFunctions(device)->vkCreateImage(device, pCreateInfo, pAllocator, pImage);
261}
262
263void VKAPI_PTR wrap_vkDestroyImage(VkDevice device, VkImage image, const VkAllocationCallbacks* pAllocator)
264{
265 globalVulkanInstance->deviceFunctions(device)->vkDestroyImage(device, image, pAllocator);
266}
267
268static inline VmaAllocation toVmaAllocation(QVkAlloc a)
269{
270 return reinterpret_cast<VmaAllocation>(a);
271}
272
273static inline VmaAllocator toVmaAllocator(QVkAllocator a)
274{
275 return reinterpret_cast<VmaAllocator>(a);
276}
277
278QRhiVulkan::QRhiVulkan(QRhiVulkanInitParams *params, QRhiVulkanNativeHandles *importDevice)
279 : ofr(this)
280{
281 inst = params->inst;
282 maybeWindow = params->window; // may be null
283
284 importedDevice = importDevice != nullptr;
285 if (importedDevice) {
286 physDev = importDevice->physDev;
287 dev = importDevice->dev;
288 if (physDev && dev) {
289 gfxQueueFamilyIdx = importDevice->gfxQueueFamilyIdx;
290 gfxQueue = importDevice->gfxQueue;
291 if (importDevice->cmdPool) {
292 importedCmdPool = true;
293 cmdPool = importDevice->cmdPool;
294 }
295 if (importDevice->vmemAllocator) {
296 importedAllocator = true;
297 allocator = importDevice->vmemAllocator;
298 }
299 } else {
300 qWarning("No (physical) Vulkan device is given, cannot import");
301 importedDevice = false;
302 }
303 }
304}
305
306static bool qvk_debug_filter(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object,
307 size_t location, int32_t messageCode, const char *pLayerPrefix, const char *pMessage)
308{
309 Q_UNUSED(flags);
310 Q_UNUSED(objectType);
311 Q_UNUSED(object);
312 Q_UNUSED(location);
313 Q_UNUSED(messageCode);
314 Q_UNUSED(pLayerPrefix);
315
316 // Filter out certain misleading validation layer messages, as per
317 // VulkanMemoryAllocator documentation.
318 if (strstr(pMessage, "Mapping an image with layout")
319 && strstr(pMessage, "can result in undefined behavior if this memory is used by the device"))
320 {
321 return true;
322 }
323
324 return false;
325}
326
327bool QRhiVulkan::create(QRhi::Flags flags)
328{
329 Q_UNUSED(flags);
330 Q_ASSERT(inst);
331
332 globalVulkanInstance = inst; // assume this will not change during the lifetime of the entire application
333
334 f = inst->functions();
335
336 QVector<VkQueueFamilyProperties> queueFamilyProps;
337 auto queryQueueFamilyProps = [this, &queueFamilyProps] {
338 uint32_t queueCount = 0;
339 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, nullptr);
340 queueFamilyProps.resize(queueCount);
341 f->vkGetPhysicalDeviceQueueFamilyProperties(physDev, &queueCount, queueFamilyProps.data());
342 };
343
344 if (!importedDevice) {
345 uint32_t physDevCount = 0;
346 f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, nullptr);
347 if (!physDevCount) {
348 qWarning("No physical devices");
349 return false;
350 }
351 QVarLengthArray<VkPhysicalDevice, 4> physDevs(physDevCount);
352 VkResult err = f->vkEnumeratePhysicalDevices(inst->vkInstance(), &physDevCount, physDevs.data());
353 if (err != VK_SUCCESS || !physDevCount) {
354 qWarning("Failed to enumerate physical devices: %d", err);
355 return false;
356 }
357 int physDevIndex = -1;
358 int requestedPhysDevIndex = -1;
359 if (qEnvironmentVariableIsSet("QT_VK_PHYSICAL_DEVICE_INDEX"))
360 requestedPhysDevIndex = qEnvironmentVariableIntValue("QT_VK_PHYSICAL_DEVICE_INDEX");
361 for (uint32_t i = 0; i < physDevCount; ++i) {
362 f->vkGetPhysicalDeviceProperties(physDevs[i], &physDevProperties);
363 qDebug("Physical device %d: '%s' %d.%d.%d", i,
364 physDevProperties.deviceName,
365 VK_VERSION_MAJOR(physDevProperties.driverVersion),
366 VK_VERSION_MINOR(physDevProperties.driverVersion),
367 VK_VERSION_PATCH(physDevProperties.driverVersion));
368 if (physDevIndex < 0 && (requestedPhysDevIndex < 0 || requestedPhysDevIndex == int(i))) {
369 physDevIndex = i;
370 qDebug(" using this physical device");
371 }
372 }
373 if (physDevIndex < 0) {
374 qWarning("No matching physical device");
375 return false;
376 }
377 physDev = physDevs[physDevIndex];
378
379 queryQueueFamilyProps();
380
381 gfxQueue = VK_NULL_HANDLE;
382
383 // We only support combined graphics+present queues. When it comes to
384 // compute, only combined graphics+compute queue is used, compute gets
385 // disabled otherwise.
386 gfxQueueFamilyIdx = -1;
387 int computelessGfxQueueCandidateIdx = -1;
388 for (int i = 0; i < queueFamilyProps.count(); ++i) {
389 qDebug("queue family %d: flags=0x%x count=%d", i, queueFamilyProps[i].queueFlags, queueFamilyProps[i].queueCount);
390 if (gfxQueueFamilyIdx == -1
391 && (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
392 && (!maybeWindow || inst->supportsPresent(physDev, i, maybeWindow)))
393 {
394 if (queueFamilyProps[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
395 gfxQueueFamilyIdx = i;
396 else if (computelessGfxQueueCandidateIdx == -1)
397 computelessGfxQueueCandidateIdx = i;
398 }
399 }
400 if (gfxQueueFamilyIdx == -1) {
401 if (computelessGfxQueueCandidateIdx != -1) {
402 gfxQueueFamilyIdx = computelessGfxQueueCandidateIdx;
403 } else {
404 qWarning("No graphics (or no graphics+present) queue family found");
405 return false;
406 }
407 }
408
409 VkDeviceQueueCreateInfo queueInfo[2];
410 const float prio[] = { 0 };
411 memset(queueInfo, 0, sizeof(queueInfo));
412 queueInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
413 queueInfo[0].queueFamilyIndex = gfxQueueFamilyIdx;
414 queueInfo[0].queueCount = 1;
415 queueInfo[0].pQueuePriorities = prio;
416
417 QVector<const char *> devLayers;
418 if (inst->layers().contains("VK_LAYER_LUNARG_standard_validation"))
419 devLayers.append("VK_LAYER_LUNARG_standard_validation");
420
421 uint32_t devExtCount = 0;
422 f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, nullptr);
423 QVector<VkExtensionProperties> devExts(devExtCount);
424 f->vkEnumerateDeviceExtensionProperties(physDev, nullptr, &devExtCount, devExts.data());
425 qDebug("%d device extensions available", devExts.count());
426
427 QVector<const char *> requestedDevExts;
428 requestedDevExts.append("VK_KHR_swapchain");
429
430 debugMarkersAvailable = false;
431 vertexAttribDivisorAvailable = false;
432 for (const VkExtensionProperties &ext : devExts) {
433 if (!strcmp(ext.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
434 requestedDevExts.append(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
435 debugMarkersAvailable = true;
436 } else if (!strcmp(ext.extensionName, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
437 if (inst->extensions().contains(QByteArrayLiteral("VK_KHR_get_physical_device_properties2"))) {
438 requestedDevExts.append(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME);
439 vertexAttribDivisorAvailable = true;
440 }
441 }
442 }
443
444 VkDeviceCreateInfo devInfo;
445 memset(&devInfo, 0, sizeof(devInfo));
446 devInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
447 devInfo.queueCreateInfoCount = 1;
448 devInfo.pQueueCreateInfos = queueInfo;
449 devInfo.enabledLayerCount = devLayers.count();
450 devInfo.ppEnabledLayerNames = devLayers.constData();
451 devInfo.enabledExtensionCount = requestedDevExts.count();
452 devInfo.ppEnabledExtensionNames = requestedDevExts.constData();
453
454 err = f->vkCreateDevice(physDev, &devInfo, nullptr, &dev);
455 if (err != VK_SUCCESS) {
456 qWarning("Failed to create device: %d", err);
457 return false;
458 }
459 }
460
461 df = inst->deviceFunctions(dev);
462
463 if (!importedCmdPool) {
464 VkCommandPoolCreateInfo poolInfo;
465 memset(&poolInfo, 0, sizeof(poolInfo));
466 poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
467 poolInfo.queueFamilyIndex = gfxQueueFamilyIdx;
468 VkResult err = df->vkCreateCommandPool(dev, &poolInfo, nullptr, &cmdPool);
469 if (err != VK_SUCCESS) {
470 qWarning("Failed to create command pool: %d", err);
471 return false;
472 }
473 }
474
475 if (gfxQueueFamilyIdx != -1) {
476 if (!gfxQueue)
477 df->vkGetDeviceQueue(dev, gfxQueueFamilyIdx, 0, &gfxQueue);
478
479 if (queueFamilyProps.isEmpty())
480 queryQueueFamilyProps();
481
482 hasCompute = (queueFamilyProps[gfxQueueFamilyIdx].queueFlags & VK_QUEUE_COMPUTE_BIT) != 0;
483 timestampValidBits = queueFamilyProps[gfxQueueFamilyIdx].timestampValidBits;
484 }
485
486 f->vkGetPhysicalDeviceProperties(physDev, &physDevProperties);
487 ubufAlign = physDevProperties.limits.minUniformBufferOffsetAlignment;
488 // helps little with an optimal offset of 1 (on some drivers) when the spec
489 // elsewhere states that the minimum bufferOffset is 4...
490 texbufAlign = qMax<VkDeviceSize>(4, physDevProperties.limits.optimalBufferCopyOffsetAlignment);
491
492 f->vkGetPhysicalDeviceFeatures(physDev, &physDevFeatures);
493 hasWideLines = physDevFeatures.wideLines;
494
495 if (!importedAllocator) {
496 VmaVulkanFunctions afuncs;
497 afuncs.vkGetPhysicalDeviceProperties = wrap_vkGetPhysicalDeviceProperties;
498 afuncs.vkGetPhysicalDeviceMemoryProperties = wrap_vkGetPhysicalDeviceMemoryProperties;
499 afuncs.vkAllocateMemory = wrap_vkAllocateMemory;
500 afuncs.vkFreeMemory = wrap_vkFreeMemory;
501 afuncs.vkMapMemory = wrap_vkMapMemory;
502 afuncs.vkUnmapMemory = wrap_vkUnmapMemory;
503 afuncs.vkFlushMappedMemoryRanges = wrap_vkFlushMappedMemoryRanges;
504 afuncs.vkInvalidateMappedMemoryRanges = wrap_vkInvalidateMappedMemoryRanges;
505 afuncs.vkBindBufferMemory = wrap_vkBindBufferMemory;
506 afuncs.vkBindImageMemory = wrap_vkBindImageMemory;
507 afuncs.vkGetBufferMemoryRequirements = wrap_vkGetBufferMemoryRequirements;
508 afuncs.vkGetImageMemoryRequirements = wrap_vkGetImageMemoryRequirements;
509 afuncs.vkCreateBuffer = wrap_vkCreateBuffer;
510 afuncs.vkDestroyBuffer = wrap_vkDestroyBuffer;
511 afuncs.vkCreateImage = wrap_vkCreateImage;
512 afuncs.vkDestroyImage = wrap_vkDestroyImage;
513
514 VmaAllocatorCreateInfo allocatorInfo;
515 memset(&allocatorInfo, 0, sizeof(allocatorInfo));
516 allocatorInfo.physicalDevice = physDev;
517 allocatorInfo.device = dev;
518 allocatorInfo.pVulkanFunctions = &afuncs;
519 VmaAllocator vmaallocator;
520 VkResult err = vmaCreateAllocator(&allocatorInfo, &vmaallocator);
521 if (err != VK_SUCCESS) {
522 qWarning("Failed to create allocator: %d", err);
523 return false;
524 }
525 allocator = vmaallocator;
526 }
527
528 inst->installDebugOutputFilter(qvk_debug_filter);
529
530 VkDescriptorPool pool;
531 VkResult err = createDescriptorPool(&pool);
532 if (err == VK_SUCCESS)
533 descriptorPools.append(pool);
534 else
535 qWarning("Failed to create initial descriptor pool: %d", err);
536
537 VkQueryPoolCreateInfo timestampQueryPoolInfo;
538 memset(&timestampQueryPoolInfo, 0, sizeof(timestampQueryPoolInfo));
539 timestampQueryPoolInfo.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
540 timestampQueryPoolInfo.queryType = VK_QUERY_TYPE_TIMESTAMP;
541 timestampQueryPoolInfo.queryCount = QVK_MAX_ACTIVE_TIMESTAMP_PAIRS * 2;
542 err = df->vkCreateQueryPool(dev, &timestampQueryPoolInfo, nullptr, &timestampQueryPool);
543 if (err != VK_SUCCESS) {
544 qWarning("Failed to create timestamp query pool: %d", err);
545 return false;
546 }
547 timestampQueryPoolMap.resize(QVK_MAX_ACTIVE_TIMESTAMP_PAIRS); // 1 bit per pair
548 timestampQueryPoolMap.fill(false);
549
550 if (debugMarkersAvailable) {
551 vkCmdDebugMarkerBegin = reinterpret_cast<PFN_vkCmdDebugMarkerBeginEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerBeginEXT"));
552 vkCmdDebugMarkerEnd = reinterpret_cast<PFN_vkCmdDebugMarkerEndEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerEndEXT"));
553 vkCmdDebugMarkerInsert = reinterpret_cast<PFN_vkCmdDebugMarkerInsertEXT>(f->vkGetDeviceProcAddr(dev, "vkCmdDebugMarkerInsertEXT"));
554 vkDebugMarkerSetObjectName = reinterpret_cast<PFN_vkDebugMarkerSetObjectNameEXT>(f->vkGetDeviceProcAddr(dev, "vkDebugMarkerSetObjectNameEXT"));
555 }
556
557 nativeHandlesStruct.physDev = physDev;
558 nativeHandlesStruct.dev = dev;
559 nativeHandlesStruct.gfxQueueFamilyIdx = gfxQueueFamilyIdx;
560 nativeHandlesStruct.gfxQueue = gfxQueue;
561 nativeHandlesStruct.cmdPool = cmdPool;
562 nativeHandlesStruct.vmemAllocator = allocator;
563
564 return true;
565}
566
567void QRhiVulkan::destroy()
568{
569 if (!df)
570 return;
571
572 df->vkDeviceWaitIdle(dev);
573
574 executeDeferredReleases(true);
575 finishActiveReadbacks(true);
576
577 if (ofr.cmdFence) {
578 df->vkDestroyFence(dev, ofr.cmdFence, nullptr);
579 ofr.cmdFence = VK_NULL_HANDLE;
580 }
581
582 if (ofr.cbWrapper.cb) {
583 df->vkFreeCommandBuffers(dev, cmdPool, 1, &ofr.cbWrapper.cb);
584 ofr.cbWrapper.cb = VK_NULL_HANDLE;
585 }
586
587 if (pipelineCache) {
588 df->vkDestroyPipelineCache(dev, pipelineCache, nullptr);
589 pipelineCache = VK_NULL_HANDLE;
590 }
591
592 for (const DescriptorPoolData &pool : descriptorPools)
593 df->vkDestroyDescriptorPool(dev, pool.pool, nullptr);
594
595 descriptorPools.clear();
596
597 if (timestampQueryPool) {
598 df->vkDestroyQueryPool(dev, timestampQueryPool, nullptr);
599 timestampQueryPool = VK_NULL_HANDLE;
600 }
601
602 if (!importedAllocator && allocator) {
603 vmaDestroyAllocator(toVmaAllocator(allocator));
604 allocator = nullptr;
605 }
606
607 if (!importedCmdPool && cmdPool) {
608 df->vkDestroyCommandPool(dev, cmdPool, nullptr);
609 cmdPool = VK_NULL_HANDLE;
610 }
611
612 if (!importedDevice && dev) {
613 df->vkDestroyDevice(dev, nullptr);
614 inst->resetDeviceFunctions(dev);
615 dev = VK_NULL_HANDLE;
616 }
617
618 f = nullptr;
619 df = nullptr;
620}
621
622VkResult QRhiVulkan::createDescriptorPool(VkDescriptorPool *pool)
623{
624 VkDescriptorPoolSize descPoolSizes[] = {
625 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, QVK_UNIFORM_BUFFERS_PER_POOL },
626 { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, QVK_UNIFORM_BUFFERS_PER_POOL },
627 { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, QVK_COMBINED_IMAGE_SAMPLERS_PER_POOL },
628 { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, QVK_STORAGE_BUFFERS_PER_POOL },
629 { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, QVK_STORAGE_IMAGES_PER_POOL }
630 };
631 VkDescriptorPoolCreateInfo descPoolInfo;
632 memset(&descPoolInfo, 0, sizeof(descPoolInfo));
633 descPoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
634 // Do not enable vkFreeDescriptorSets - sets are never freed on their own
635 // (good so no trouble with fragmentation), they just deref their pool
636 // which is then reset at some point (or not).
637 descPoolInfo.flags = 0;
638 descPoolInfo.maxSets = QVK_DESC_SETS_PER_POOL;
639 descPoolInfo.poolSizeCount = sizeof(descPoolSizes) / sizeof(descPoolSizes[0]);
640 descPoolInfo.pPoolSizes = descPoolSizes;
641 return df->vkCreateDescriptorPool(dev, &descPoolInfo, nullptr, pool);
642}
643
644bool QRhiVulkan::allocateDescriptorSet(VkDescriptorSetAllocateInfo *allocInfo, VkDescriptorSet *result, int *resultPoolIndex)
645{
646 auto tryAllocate = [this, allocInfo, result](int poolIndex) {
647 allocInfo->descriptorPool = descriptorPools[poolIndex].pool;
648 VkResult r = df->vkAllocateDescriptorSets(dev, allocInfo, result);
649 if (r == VK_SUCCESS)
650 descriptorPools[poolIndex].refCount += 1;
651 return r;
652 };
653
654 int lastPoolIdx = descriptorPools.count() - 1;
655 for (int i = lastPoolIdx; i >= 0; --i) {
656 if (descriptorPools[i].refCount == 0) {
657 df->vkResetDescriptorPool(dev, descriptorPools[i].pool, 0);
658 descriptorPools[i].allocedDescSets = 0;
659 }
660 if (descriptorPools[i].allocedDescSets + allocInfo->descriptorSetCount <= QVK_DESC_SETS_PER_POOL) {
661 VkResult err = tryAllocate(i);
662 if (err == VK_SUCCESS) {
663 descriptorPools[i].allocedDescSets += allocInfo->descriptorSetCount;
664 *resultPoolIndex = i;
665 return true;
666 }
667 }
668 }
669
670 VkDescriptorPool newPool;
671 VkResult poolErr = createDescriptorPool(&newPool);
672 if (poolErr == VK_SUCCESS) {
673 descriptorPools.append(newPool);
674 lastPoolIdx = descriptorPools.count() - 1;
675 VkResult err = tryAllocate(lastPoolIdx);
676 if (err != VK_SUCCESS) {
677 qWarning("Failed to allocate descriptor set from new pool too, giving up: %d", err);
678 return false;
679 }
680 descriptorPools[lastPoolIdx].allocedDescSets += allocInfo->descriptorSetCount;
681 *resultPoolIndex = lastPoolIdx;
682 return true;
683 } else {
684 qWarning("Failed to allocate new descriptor pool: %d", poolErr);
685 return false;
686 }
687}
688
689static inline VkFormat toVkTextureFormat(QRhiTexture::Format format, QRhiTexture::Flags flags)
690{
691 const bool srgb = flags.testFlag(QRhiTexture::sRGB);
692 switch (format) {
693 case QRhiTexture::RGBA8:
694 return srgb ? VK_FORMAT_R8G8B8A8_SRGB : VK_FORMAT_R8G8B8A8_UNORM;
695 case QRhiTexture::BGRA8:
696 return srgb ? VK_FORMAT_B8G8R8A8_SRGB : VK_FORMAT_B8G8R8A8_UNORM;
697 case QRhiTexture::R8:
698 return srgb ? VK_FORMAT_R8_SRGB : VK_FORMAT_R8_UNORM;
699 case QRhiTexture::R16:
700 return VK_FORMAT_R16_UNORM;
701 case QRhiTexture::RED_OR_ALPHA8:
702 return VK_FORMAT_R8_UNORM;
703
704 case QRhiTexture::RGBA16F:
705 return VK_FORMAT_R16G16B16A16_SFLOAT;
706 case QRhiTexture::RGBA32F:
707 return VK_FORMAT_R32G32B32A32_SFLOAT;
708
709 case QRhiTexture::D16:
710 return VK_FORMAT_D16_UNORM;
711 case QRhiTexture::D32F:
712 return VK_FORMAT_D32_SFLOAT;
713
714 case QRhiTexture::BC1:
715 return srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK : VK_FORMAT_BC1_RGB_UNORM_BLOCK;
716 case QRhiTexture::BC2:
717 return srgb ? VK_FORMAT_BC2_SRGB_BLOCK : VK_FORMAT_BC2_UNORM_BLOCK;
718 case QRhiTexture::BC3:
719 return srgb ? VK_FORMAT_BC3_SRGB_BLOCK : VK_FORMAT_BC3_UNORM_BLOCK;
720 case QRhiTexture::BC4:
721 return VK_FORMAT_BC4_UNORM_BLOCK;
722 case QRhiTexture::BC5:
723 return VK_FORMAT_BC5_UNORM_BLOCK;
724 case QRhiTexture::BC6H:
725 return VK_FORMAT_BC6H_UFLOAT_BLOCK;
726 case QRhiTexture::BC7:
727 return srgb ? VK_FORMAT_BC7_SRGB_BLOCK : VK_FORMAT_BC7_UNORM_BLOCK;
728
729 case QRhiTexture::ETC2_RGB8:
730 return srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
731 case QRhiTexture::ETC2_RGB8A1:
732 return srgb ? VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
733 case QRhiTexture::ETC2_RGBA8:
734 return srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
735
736 case QRhiTexture::ASTC_4x4:
737 return srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK : VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
738 case QRhiTexture::ASTC_5x4:
739 return srgb ? VK_FORMAT_ASTC_5x4_SRGB_BLOCK : VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
740 case QRhiTexture::ASTC_5x5:
741 return srgb ? VK_FORMAT_ASTC_5x5_SRGB_BLOCK : VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
742 case QRhiTexture::ASTC_6x5:
743 return srgb ? VK_FORMAT_ASTC_6x5_SRGB_BLOCK : VK_FORMAT_ASTC_6x5_UNORM_BLOCK;
744 case QRhiTexture::ASTC_6x6:
745 return srgb ? VK_FORMAT_ASTC_6x6_SRGB_BLOCK : VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
746 case QRhiTexture::ASTC_8x5:
747 return srgb ? VK_FORMAT_ASTC_8x5_SRGB_BLOCK : VK_FORMAT_ASTC_8x5_UNORM_BLOCK;
748 case QRhiTexture::ASTC_8x6:
749 return srgb ? VK_FORMAT_ASTC_8x6_SRGB_BLOCK : VK_FORMAT_ASTC_8x6_UNORM_BLOCK;
750 case QRhiTexture::ASTC_8x8:
751 return srgb ? VK_FORMAT_ASTC_8x8_SRGB_BLOCK : VK_FORMAT_ASTC_8x8_UNORM_BLOCK;
752 case QRhiTexture::ASTC_10x5:
753 return srgb ? VK_FORMAT_ASTC_10x5_SRGB_BLOCK : VK_FORMAT_ASTC_10x5_UNORM_BLOCK;
754 case QRhiTexture::ASTC_10x6:
755 return srgb ? VK_FORMAT_ASTC_10x6_SRGB_BLOCK : VK_FORMAT_ASTC_10x6_UNORM_BLOCK;
756 case QRhiTexture::ASTC_10x8:
757 return srgb ? VK_FORMAT_ASTC_10x8_SRGB_BLOCK : VK_FORMAT_ASTC_10x8_UNORM_BLOCK;
758 case QRhiTexture::ASTC_10x10:
759 return srgb ? VK_FORMAT_ASTC_10x10_SRGB_BLOCK : VK_FORMAT_ASTC_10x10_UNORM_BLOCK;
760 case QRhiTexture::ASTC_12x10:
761 return srgb ? VK_FORMAT_ASTC_12x10_SRGB_BLOCK : VK_FORMAT_ASTC_12x10_UNORM_BLOCK;
762 case QRhiTexture::ASTC_12x12:
763 return srgb ? VK_FORMAT_ASTC_12x12_SRGB_BLOCK : VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
764
765 default:
766 Q_UNREACHABLE();
767 return VK_FORMAT_R8G8B8A8_UNORM;
768 }
769}
770
771static inline QRhiTexture::Format colorTextureFormatFromVkFormat(VkFormat format, QRhiTexture::Flags *flags)
772{
773 switch (format) {
774 case VK_FORMAT_R8G8B8A8_UNORM:
775 return QRhiTexture::RGBA8;
776 case VK_FORMAT_R8G8B8A8_SRGB:
777 if (flags)
778 (*flags) |= QRhiTexture::sRGB;
779 return QRhiTexture::RGBA8;
780 case VK_FORMAT_B8G8R8A8_UNORM:
781 return QRhiTexture::BGRA8;
782 case VK_FORMAT_B8G8R8A8_SRGB:
783 if (flags)
784 (*flags) |= QRhiTexture::sRGB;
785 return QRhiTexture::BGRA8;
786 case VK_FORMAT_R8_UNORM:
787 return QRhiTexture::R8;
788 case VK_FORMAT_R8_SRGB:
789 if (flags)
790 (*flags) |= QRhiTexture::sRGB;
791 return QRhiTexture::R8;
792 case VK_FORMAT_R16_UNORM:
793 return QRhiTexture::R16;
794 default: // this cannot assert, must warn and return unknown
795 qWarning("VkFormat %d is not a recognized uncompressed color format", format);
796 break;
797 }
798 return QRhiTexture::UnknownFormat;
799}
800
801static inline bool isDepthTextureFormat(QRhiTexture::Format format)
802{
803 switch (format) {
804 case QRhiTexture::Format::D16:
805 Q_FALLTHROUGH();
806 case QRhiTexture::Format::D32F:
807 return true;
808
809 default:
810 return false;
811 }
812}
813
814// Transient images ("render buffers") backed by lazily allocated memory are
815// managed manually without going through vk_mem_alloc since it does not offer
816// any support for such images. This should be ok since in practice there
817// should be very few of such images.
818
819uint32_t QRhiVulkan::chooseTransientImageMemType(VkImage img, uint32_t startIndex)
820{
821 VkPhysicalDeviceMemoryProperties physDevMemProps;
822 f->vkGetPhysicalDeviceMemoryProperties(physDev, &physDevMemProps);
823
824 VkMemoryRequirements memReq;
825 df->vkGetImageMemoryRequirements(dev, img, &memReq);
826 uint32_t memTypeIndex = uint32_t(-1);
827
828 if (memReq.memoryTypeBits) {
829 // Find a device local + lazily allocated, or at least device local memtype.
830 const VkMemoryType *memType = physDevMemProps.memoryTypes;
831 bool foundDevLocal = false;
832 for (uint32_t i = startIndex; i < physDevMemProps.memoryTypeCount; ++i) {
833 if (memReq.memoryTypeBits & (1 << i)) {
834 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {
835 if (!foundDevLocal) {
836 foundDevLocal = true;
837 memTypeIndex = i;
838 }
839 if (memType[i].propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
840 memTypeIndex = i;
841 break;
842 }
843 }
844 }
845 }
846 }
847
848 return memTypeIndex;
849}
850
851bool QRhiVulkan::createTransientImage(VkFormat format,
852 const QSize &pixelSize,
853 VkImageUsageFlags usage,
854 VkImageAspectFlags aspectMask,
855 VkSampleCountFlagBits samples,
856 VkDeviceMemory *mem,
857 VkImage *images,
858 VkImageView *views,
859 int count)
860{
861 VkMemoryRequirements memReq;
862 VkResult err;
863
864 for (int i = 0; i < count; ++i) {
865 VkImageCreateInfo imgInfo;
866 memset(&imgInfo, 0, sizeof(imgInfo));
867 imgInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
868 imgInfo.imageType = VK_IMAGE_TYPE_2D;
869 imgInfo.format = format;
870 imgInfo.extent.width = pixelSize.width();
871 imgInfo.extent.height = pixelSize.height();
872 imgInfo.extent.depth = 1;
873 imgInfo.mipLevels = imgInfo.arrayLayers = 1;
874 imgInfo.samples = samples;
875 imgInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
876 imgInfo.usage = usage | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
877 imgInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
878
879 err = df->vkCreateImage(dev, &imgInfo, nullptr, images + i);
880 if (err != VK_SUCCESS) {
881 qWarning("Failed to create image: %d", err);
882 return false;
883 }
884
885 // Assume the reqs are the same since the images are same in every way.
886 // Still, call GetImageMemReq for every image, in order to prevent the
887 // validation layer from complaining.
888 df->vkGetImageMemoryRequirements(dev, images[i], &memReq);
889 }
890
891 VkMemoryAllocateInfo memInfo;
892 memset(&memInfo, 0, sizeof(memInfo));
893 memInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
894 memInfo.allocationSize = aligned(memReq.size, memReq.alignment) * count;
895
896 uint32_t startIndex = 0;
897 do {
898 memInfo.memoryTypeIndex = chooseTransientImageMemType(images[0], startIndex);
899 if (memInfo.memoryTypeIndex == uint32_t(-1)) {
900 qWarning("No suitable memory type found");
901 return false;
902 }
903 startIndex = memInfo.memoryTypeIndex + 1;
904 err = df->vkAllocateMemory(dev, &memInfo, nullptr, mem);
905 if (err != VK_SUCCESS && err != VK_ERROR_OUT_OF_DEVICE_MEMORY) {
906 qWarning("Failed to allocate image memory: %d", err);
907 return false;
908 }
909 } while (err != VK_SUCCESS);
910
911 VkDeviceSize ofs = 0;
912 for (int i = 0; i < count; ++i) {
913 err = df->vkBindImageMemory(dev, images[i], *mem, ofs);
914 if (err != VK_SUCCESS) {
915 qWarning("Failed to bind image memory: %d", err);
916 return false;
917 }
918 ofs += aligned(memReq.size, memReq.alignment);
919
920 VkImageViewCreateInfo imgViewInfo;
921 memset(&imgViewInfo, 0, sizeof(imgViewInfo));
922 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
923 imgViewInfo.image = images[i];
924 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
925 imgViewInfo.format = format;
926 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
927 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
928 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
929 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
930 imgViewInfo.subresourceRange.aspectMask = aspectMask;
931 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
932
933 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, views + i);
934 if (err != VK_SUCCESS) {
935 qWarning("Failed to create image view: %d", err);
936 return false;
937 }
938 }
939
940 return true;
941}
942
943VkFormat QRhiVulkan::optimalDepthStencilFormat()
944{
945 if (optimalDsFormat != VK_FORMAT_UNDEFINED)
946 return optimalDsFormat;
947
948 const VkFormat dsFormatCandidates[] = {
949 VK_FORMAT_D24_UNORM_S8_UINT,
950 VK_FORMAT_D32_SFLOAT_S8_UINT,
951 VK_FORMAT_D16_UNORM_S8_UINT
952 };
953 const int dsFormatCandidateCount = sizeof(dsFormatCandidates) / sizeof(VkFormat);
954 int dsFormatIdx = 0;
955 while (dsFormatIdx < dsFormatCandidateCount) {
956 optimalDsFormat = dsFormatCandidates[dsFormatIdx];
957 VkFormatProperties fmtProp;
958 f->vkGetPhysicalDeviceFormatProperties(physDev, optimalDsFormat, &fmtProp);
959 if (fmtProp.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
960 break;
961 ++dsFormatIdx;
962 }
963 if (dsFormatIdx == dsFormatCandidateCount)
964 qWarning("Failed to find an optimal depth-stencil format");
965
966 return optimalDsFormat;
967}
968
969bool QRhiVulkan::createDefaultRenderPass(VkRenderPass *rp, bool hasDepthStencil, VkSampleCountFlagBits samples, VkFormat colorFormat)
970{
971 VkAttachmentDescription attDesc[3];
972 memset(attDesc, 0, sizeof(attDesc));
973
974 // attachment list layout is color (1), ds (0-1), resolve (0-1)
975
976 attDesc[0].format = colorFormat;
977 attDesc[0].samples = samples;
978 attDesc[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
979 attDesc[0].storeOp = samples > VK_SAMPLE_COUNT_1_BIT ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
980 attDesc[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
981 attDesc[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
982 attDesc[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
983 attDesc[0].finalLayout = samples > VK_SAMPLE_COUNT_1_BIT ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
984
985 // clear on load + no store + lazy alloc + transient image should play
986 // nicely with tiled GPUs (no physical backing necessary for ds buffer)
987 attDesc[1].format = optimalDepthStencilFormat();
988 attDesc[1].samples = samples;
989 attDesc[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
990 attDesc[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
991 attDesc[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
992 attDesc[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
993 attDesc[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
994 attDesc[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
995
996 if (samples > VK_SAMPLE_COUNT_1_BIT) {
997 attDesc[2].format = colorFormat;
998 attDesc[2].samples = VK_SAMPLE_COUNT_1_BIT;
999 attDesc[2].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
1000 attDesc[2].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1001 attDesc[2].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1002 attDesc[2].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1003 attDesc[2].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1004 attDesc[2].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1005 }
1006
1007 VkAttachmentReference colorRef = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1008 VkAttachmentReference dsRef = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
1009 VkAttachmentReference resolveRef = { 2, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1010
1011 VkSubpassDescription subpassDesc;
1012 memset(&subpassDesc, 0, sizeof(subpassDesc));
1013 subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1014 subpassDesc.colorAttachmentCount = 1;
1015 subpassDesc.pColorAttachments = &colorRef;
1016 subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr;
1017
1018 // Replace the first implicit dep (TOP_OF_PIPE / ALL_COMMANDS) with our own.
1019 VkSubpassDependency subpassDep;
1020 memset(&subpassDep, 0, sizeof(subpassDep));
1021 subpassDep.srcSubpass = VK_SUBPASS_EXTERNAL;
1022 subpassDep.dstSubpass = 0;
1023 subpassDep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1024 subpassDep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1025 subpassDep.srcAccessMask = 0;
1026 subpassDep.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1027
1028 VkRenderPassCreateInfo rpInfo;
1029 memset(&rpInfo, 0, sizeof(rpInfo));
1030 rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1031 rpInfo.attachmentCount = 1;
1032 rpInfo.pAttachments = attDesc;
1033 rpInfo.subpassCount = 1;
1034 rpInfo.pSubpasses = &subpassDesc;
1035 rpInfo.dependencyCount = 1;
1036 rpInfo.pDependencies = &subpassDep;
1037
1038 if (hasDepthStencil)
1039 rpInfo.attachmentCount += 1;
1040
1041 if (samples > VK_SAMPLE_COUNT_1_BIT) {
1042 rpInfo.attachmentCount += 1;
1043 subpassDesc.pResolveAttachments = &resolveRef;
1044 }
1045
1046 VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp);
1047 if (err != VK_SUCCESS) {
1048 qWarning("Failed to create renderpass: %d", err);
1049 return false;
1050 }
1051
1052 return true;
1053}
1054
1055bool QRhiVulkan::createOffscreenRenderPass(VkRenderPass *rp,
1056 const QVector<QRhiColorAttachment> &colorAttachments,
1057 bool preserveColor,
1058 bool preserveDs,
1059 QRhiRenderBuffer *depthStencilBuffer,
1060 QRhiTexture *depthTexture)
1061{
1062 QVarLengthArray<VkAttachmentDescription, 8> attDescs;
1063 QVarLengthArray<VkAttachmentReference, 8> colorRefs;
1064 QVarLengthArray<VkAttachmentReference, 8> resolveRefs;
1065 const int colorAttCount = colorAttachments.count();
1066
1067 // attachment list layout is color (0-8), ds (0-1), resolve (0-8)
1068
1069 for (int i = 0; i < colorAttCount; ++i) {
1070 QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture());
1071 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer());
1072 Q_ASSERT(texD || rbD);
1073 const VkFormat vkformat = texD ? texD->vkformat : rbD->vkformat;
1074 const VkSampleCountFlagBits samples = texD ? texD->samples : rbD->samples;
1075
1076 VkAttachmentDescription attDesc;
1077 memset(&attDesc, 0, sizeof(attDesc));
1078 attDesc.format = vkformat;
1079 attDesc.samples = samples;
1080 attDesc.loadOp = preserveColor ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
1081 attDesc.storeOp = colorAttachments[i].resolveTexture() ? VK_ATTACHMENT_STORE_OP_DONT_CARE : VK_ATTACHMENT_STORE_OP_STORE;
1082 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1083 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1084 // this has to interact correctly with activateTextureRenderTarget(), hence leaving in COLOR_ATT
1085 attDesc.initialLayout = preserveColor ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
1086 attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1087 attDescs.append(attDesc);
1088
1089 const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1090 colorRefs.append(ref);
1091 }
1092
1093 const bool hasDepthStencil = depthStencilBuffer || depthTexture;
1094 if (hasDepthStencil) {
1095 const VkFormat dsFormat = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->vkformat
1096 : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->vkformat;
1097 const VkSampleCountFlagBits samples = depthTexture ? QRHI_RES(QVkTexture, depthTexture)->samples
1098 : QRHI_RES(QVkRenderBuffer, depthStencilBuffer)->samples;
1099 const VkAttachmentLoadOp loadOp = preserveDs ? VK_ATTACHMENT_LOAD_OP_LOAD : VK_ATTACHMENT_LOAD_OP_CLEAR;
1100 const VkAttachmentStoreOp storeOp = depthTexture ? VK_ATTACHMENT_STORE_OP_STORE : VK_ATTACHMENT_STORE_OP_DONT_CARE;
1101 VkAttachmentDescription attDesc;
1102 memset(&attDesc, 0, sizeof(attDesc));
1103 attDesc.format = dsFormat;
1104 attDesc.samples = samples;
1105 attDesc.loadOp = loadOp;
1106 attDesc.storeOp = storeOp;
1107 attDesc.stencilLoadOp = loadOp;
1108 attDesc.stencilStoreOp = storeOp;
1109 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1110 attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
1111 attDescs.append(attDesc);
1112 }
1113 VkAttachmentReference dsRef = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
1114
1115 for (int i = 0; i < colorAttCount; ++i) {
1116 if (colorAttachments[i].resolveTexture()) {
1117 QVkTexture *rtexD = QRHI_RES(QVkTexture, colorAttachments[i].resolveTexture());
1118 if (rtexD->samples > VK_SAMPLE_COUNT_1_BIT)
1119 qWarning("Resolving into a multisample texture is not supported");
1120
1121 VkAttachmentDescription attDesc;
1122 memset(&attDesc, 0, sizeof(attDesc));
1123 attDesc.format = rtexD->vkformat;
1124 attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
1125 attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // ignored
1126 attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
1127 attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
1128 attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
1129 attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1130 attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
1131 attDescs.append(attDesc);
1132
1133 const VkAttachmentReference ref = { uint32_t(attDescs.count() - 1), VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1134 resolveRefs.append(ref);
1135 } else {
1136 const VkAttachmentReference ref = { VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
1137 resolveRefs.append(ref);
1138 }
1139 }
1140
1141 VkSubpassDescription subpassDesc;
1142 memset(&subpassDesc, 0, sizeof(subpassDesc));
1143 subpassDesc.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
1144 subpassDesc.colorAttachmentCount = colorRefs.count();
1145 Q_ASSERT(colorRefs.count() == resolveRefs.count());
1146 subpassDesc.pColorAttachments = !colorRefs.isEmpty() ? colorRefs.constData() : nullptr;
1147 subpassDesc.pDepthStencilAttachment = hasDepthStencil ? &dsRef : nullptr;
1148 subpassDesc.pResolveAttachments = !resolveRefs.isEmpty() ? resolveRefs.constData() : nullptr;
1149
1150 VkRenderPassCreateInfo rpInfo;
1151 memset(&rpInfo, 0, sizeof(rpInfo));
1152 rpInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
1153 rpInfo.attachmentCount = attDescs.count();
1154 rpInfo.pAttachments = attDescs.constData();
1155 rpInfo.subpassCount = 1;
1156 rpInfo.pSubpasses = &subpassDesc;
1157 // don't yet know the correct initial/final access and stage stuff for the
1158 // implicit deps at this point, so leave it to the resource tracking to
1159 // generate barriers
1160
1161 VkResult err = df->vkCreateRenderPass(dev, &rpInfo, nullptr, rp);
1162 if (err != VK_SUCCESS) {
1163 qWarning("Failed to create renderpass: %d", err);
1164 return false;
1165 }
1166
1167 return true;
1168}
1169
1170bool QRhiVulkan::recreateSwapChain(QRhiSwapChain *swapChain)
1171{
1172 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
1173 if (swapChainD->pixelSize.isEmpty()) {
1174 qWarning("Surface size is 0, cannot create swapchain");
1175 return false;
1176 }
1177
1178 df->vkDeviceWaitIdle(dev);
1179
1180 if (!vkCreateSwapchainKHR) {
1181 vkCreateSwapchainKHR = reinterpret_cast<PFN_vkCreateSwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkCreateSwapchainKHR"));
1182 vkDestroySwapchainKHR = reinterpret_cast<PFN_vkDestroySwapchainKHR>(f->vkGetDeviceProcAddr(dev, "vkDestroySwapchainKHR"));
1183 vkGetSwapchainImagesKHR = reinterpret_cast<PFN_vkGetSwapchainImagesKHR>(f->vkGetDeviceProcAddr(dev, "vkGetSwapchainImagesKHR"));
1184 vkAcquireNextImageKHR = reinterpret_cast<PFN_vkAcquireNextImageKHR>(f->vkGetDeviceProcAddr(dev, "vkAcquireNextImageKHR"));
1185 vkQueuePresentKHR = reinterpret_cast<PFN_vkQueuePresentKHR>(f->vkGetDeviceProcAddr(dev, "vkQueuePresentKHR"));
1186 if (!vkCreateSwapchainKHR || !vkDestroySwapchainKHR || !vkGetSwapchainImagesKHR || !vkAcquireNextImageKHR || !vkQueuePresentKHR) {
1187 qWarning("Swapchain functions not available");
1188 return false;
1189 }
1190 }
1191
1192 VkSurfaceCapabilitiesKHR surfaceCaps;
1193 vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physDev, swapChainD->surface, &surfaceCaps);
1194 quint32 reqBufferCount;
1195 if (swapChainD->m_flags.testFlag(QRhiSwapChain::MinimalBufferCount)) {
1196 reqBufferCount = qMax<quint32>(2, surfaceCaps.minImageCount);
1197 } else {
1198 const quint32 maxBuffers = QVkSwapChain::MAX_BUFFER_COUNT;
1199 if (surfaceCaps.maxImageCount)
1200 reqBufferCount = qMax(qMin(surfaceCaps.maxImageCount, maxBuffers), surfaceCaps.minImageCount);
1201 else
1202 reqBufferCount = qMax<quint32>(2, surfaceCaps.minImageCount);
1203 }
1204
1205 VkSurfaceTransformFlagBitsKHR preTransform =
1206 (surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
1207 ? VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR
1208 : surfaceCaps.currentTransform;
1209
1210 VkCompositeAlphaFlagBitsKHR compositeAlpha =
1211 (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
1212 ? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
1213 : VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
1214
1215 if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasPreMulAlpha)
1216 && (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR))
1217 {
1218 compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
1219 }
1220
1221 if (swapChainD->m_flags.testFlag(QRhiSwapChain::SurfaceHasNonPreMulAlpha)
1222 && (surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR))
1223 {
1224 compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
1225 }
1226
1227 VkImageUsageFlags usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
1228 swapChainD->supportsReadback = (surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
1229 if (swapChainD->supportsReadback && swapChainD->m_flags.testFlag(QRhiSwapChain::UsedAsTransferSource))
1230 usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
1231
1232 VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
1233 if (swapChainD->m_flags.testFlag(QRhiSwapChain::NoVSync)) {
1234 if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_MAILBOX_KHR))
1235 presentMode = VK_PRESENT_MODE_MAILBOX_KHR;
1236 else if (swapChainD->supportedPresentationModes.contains(VK_PRESENT_MODE_IMMEDIATE_KHR))
1237 presentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
1238 }
1239
1240 // If the surface is different than before, then passing in the old
1241 // swapchain associated with the old surface can fail the swapchain
1242 // creation. (for example, Android loses the surface when backgrounding and
1243 // restoring applications, and it also enforces failing swapchain creation
1244 // with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR if the old swapchain is provided)
1245 const bool reuseExisting = swapChainD->sc && swapChainD->lastConnectedSurface == swapChainD->surface;
1246
1247 qDebug("Creating %s swapchain of %u buffers, size %dx%d, presentation mode %d",
1248 reuseExisting ? "recycled" : "new",
1249 reqBufferCount, swapChainD->pixelSize.width(), swapChainD->pixelSize.height(), presentMode);
1250
1251 VkSwapchainCreateInfoKHR swapChainInfo;
1252 memset(&swapChainInfo, 0, sizeof(swapChainInfo));
1253 swapChainInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
1254 swapChainInfo.surface = swapChainD->surface;
1255 swapChainInfo.minImageCount = reqBufferCount;
1256 swapChainInfo.imageFormat = swapChainD->colorFormat;
1257 swapChainInfo.imageColorSpace = swapChainD->colorSpace;
1258 swapChainInfo.imageExtent = VkExtent2D { uint32_t(swapChainD->pixelSize.width()), uint32_t(swapChainD->pixelSize.height()) };
1259 swapChainInfo.imageArrayLayers = 1;
1260 swapChainInfo.imageUsage = usage;
1261 swapChainInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
1262 swapChainInfo.preTransform = preTransform;
1263 swapChainInfo.compositeAlpha = compositeAlpha;
1264 swapChainInfo.presentMode = presentMode;
1265 swapChainInfo.clipped = true;
1266 swapChainInfo.oldSwapchain = reuseExisting ? swapChainD->sc : VK_NULL_HANDLE;
1267
1268 VkSwapchainKHR newSwapChain;
1269 VkResult err = vkCreateSwapchainKHR(dev, &swapChainInfo, nullptr, &newSwapChain);
1270 if (err != VK_SUCCESS) {
1271 qWarning("Failed to create swapchain: %d", err);
1272 return false;
1273 }
1274
1275 if (swapChainD->sc)
1276 releaseSwapChainResources(swapChain);
1277
1278 swapChainD->sc = newSwapChain;
1279 swapChainD->lastConnectedSurface = swapChainD->surface;
1280
1281 quint32 actualSwapChainBufferCount = 0;
1282 err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, nullptr);
1283 if (err != VK_SUCCESS || actualSwapChainBufferCount < 2) {
1284 qWarning("Failed to get swapchain images: %d (count=%u)", err, actualSwapChainBufferCount);
1285 return false;
1286 }
1287
1288 if (actualSwapChainBufferCount > QVkSwapChain::MAX_BUFFER_COUNT) {
1289 qWarning("Too many swapchain buffers (%u)", actualSwapChainBufferCount);
1290 return false;
1291 }
1292 if (actualSwapChainBufferCount != reqBufferCount)
1293 qDebug("Actual swapchain buffer count is %u", actualSwapChainBufferCount);
1294 swapChainD->bufferCount = actualSwapChainBufferCount;
1295
1296 VkImage swapChainImages[QVkSwapChain::MAX_BUFFER_COUNT];
1297 err = vkGetSwapchainImagesKHR(dev, swapChainD->sc, &actualSwapChainBufferCount, swapChainImages);
1298 if (err != VK_SUCCESS) {
1299 qWarning("Failed to get swapchain images: %d", err);
1300 return false;
1301 }
1302
1303 VkImage msaaImages[QVkSwapChain::MAX_BUFFER_COUNT];
1304 VkImageView msaaViews[QVkSwapChain::MAX_BUFFER_COUNT];
1305 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
1306 if (!createTransientImage(swapChainD->colorFormat,
1307 swapChainD->pixelSize,
1308 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
1309 VK_IMAGE_ASPECT_COLOR_BIT,
1310 swapChainD->samples,
1311 &swapChainD->msaaImageMem,
1312 msaaImages,
1313 msaaViews,
1314 swapChainD->bufferCount))
1315 {
1316 qWarning("Failed to create transient image for MSAA color buffer");
1317 return false;
1318 }
1319 }
1320
1321 VkFenceCreateInfo fenceInfo;
1322 memset(&fenceInfo, 0, sizeof(fenceInfo));
1323 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1324 fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1325
1326 for (int i = 0; i < swapChainD->bufferCount; ++i) {
1327 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
1328 image.image = swapChainImages[i];
1329 if (swapChainD->samples > VK_SAMPLE_COUNT_1_BIT) {
1330 image.msaaImage = msaaImages[i];
1331 image.msaaImageView = msaaViews[i];
1332 }
1333
1334 VkImageViewCreateInfo imgViewInfo;
1335 memset(&imgViewInfo, 0, sizeof(imgViewInfo));
1336 imgViewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
1337 imgViewInfo.image = swapChainImages[i];
1338 imgViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
1339 imgViewInfo.format = swapChainD->colorFormat;
1340 imgViewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
1341 imgViewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
1342 imgViewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
1343 imgViewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
1344 imgViewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1345 imgViewInfo.subresourceRange.levelCount = imgViewInfo.subresourceRange.layerCount = 1;
1346 err = df->vkCreateImageView(dev, &imgViewInfo, nullptr, &image.imageView);
1347 if (err != VK_SUCCESS) {
1348 qWarning("Failed to create swapchain image view %d: %d", i, err);
1349 return false;
1350 }
1351
1352 image.lastUse = QVkSwapChain::ImageResources::ScImageUseNone;
1353 }
1354
1355 swapChainD->currentImageIndex = 0;
1356
1357 VkSemaphoreCreateInfo semInfo;
1358 memset(&semInfo, 0, sizeof(semInfo));
1359 semInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
1360
1361 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
1362 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
1363
1364 frame.imageAcquired = false;
1365 frame.imageSemWaitable = false;
1366
1367 df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.imageFence);
1368 frame.imageFenceWaitable = true; // fence was created in signaled state
1369
1370 df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.imageSem);
1371 df->vkCreateSemaphore(dev, &semInfo, nullptr, &frame.drawSem);
1372
1373 err = df->vkCreateFence(dev, &fenceInfo, nullptr, &frame.cmdFence);
1374 if (err != VK_SUCCESS) {
1375 qWarning("Failed to create command buffer fence: %d", err);
1376 return false;
1377 }
1378 frame.cmdFenceWaitable = true; // fence was created in signaled state
1379 }
1380
1381 swapChainD->currentFrameSlot = 0;
1382
1383 return true;
1384}
1385
1386void QRhiVulkan::releaseSwapChainResources(QRhiSwapChain *swapChain)
1387{
1388 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
1389
1390 if (swapChainD->sc == VK_NULL_HANDLE)
1391 return;
1392
1393 df->vkDeviceWaitIdle(dev);
1394
1395 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
1396 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[i]);
1397 if (frame.cmdFence) {
1398 if (frame.cmdFenceWaitable)
1399 df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
1400 df->vkDestroyFence(dev, frame.cmdFence, nullptr);
1401 frame.cmdFence = VK_NULL_HANDLE;
1402 frame.cmdFenceWaitable = false;
1403 }
1404 if (frame.imageFence) {
1405 if (frame.imageFenceWaitable)
1406 df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX);
1407 df->vkDestroyFence(dev, frame.imageFence, nullptr);
1408 frame.imageFence = VK_NULL_HANDLE;
1409 frame.imageFenceWaitable = false;
1410 }
1411 if (frame.imageSem) {
1412 df->vkDestroySemaphore(dev, frame.imageSem, nullptr);
1413 frame.imageSem = VK_NULL_HANDLE;
1414 }
1415 if (frame.drawSem) {
1416 df->vkDestroySemaphore(dev, frame.drawSem, nullptr);
1417 frame.drawSem = VK_NULL_HANDLE;
1418 }
1419 if (frame.cmdBuf) {
1420 df->vkFreeCommandBuffers(dev, cmdPool, 1, &frame.cmdBuf);
1421 frame.cmdBuf = VK_NULL_HANDLE;
1422 }
1423 }
1424
1425 for (int i = 0; i < swapChainD->bufferCount; ++i) {
1426 QVkSwapChain::ImageResources &image(swapChainD->imageRes[i]);
1427 if (image.fb) {
1428 df->vkDestroyFramebuffer(dev, image.fb, nullptr);
1429 image.fb = VK_NULL_HANDLE;
1430 }
1431 if (image.imageView) {
1432 df->vkDestroyImageView(dev, image.imageView, nullptr);
1433 image.imageView = VK_NULL_HANDLE;
1434 }
1435 if (image.msaaImageView) {
1436 df->vkDestroyImageView(dev, image.msaaImageView, nullptr);
1437 image.msaaImageView = VK_NULL_HANDLE;
1438 }
1439 if (image.msaaImage) {
1440 df->vkDestroyImage(dev, image.msaaImage, nullptr);
1441 image.msaaImage = VK_NULL_HANDLE;
1442 }
1443 }
1444
1445 if (swapChainD->msaaImageMem) {
1446 df->vkFreeMemory(dev, swapChainD->msaaImageMem, nullptr);
1447 swapChainD->msaaImageMem = VK_NULL_HANDLE;
1448 }
1449
1450 vkDestroySwapchainKHR(dev, swapChainD->sc, nullptr);
1451 swapChainD->sc = VK_NULL_HANDLE;
1452
1453 // NB! surface and similar must remain intact
1454}
1455
1456static inline bool checkDeviceLost(VkResult err)
1457{
1458 if (err == VK_ERROR_DEVICE_LOST) {
1459 qWarning("Device lost");
1460 return true;
1461 }
1462 return false;
1463}
1464
1465QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::BeginFrameFlags flags)
1466{
1467 Q_UNUSED(flags);
1468 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
1469 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
1470 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
1471
1472 if (!frame.imageAcquired) {
1473 // Wait if we are too far ahead, i.e. the thread gets throttled based on the presentation rate
1474 // (note that we are using FIFO mode -> vsync)
1475 if (frame.imageFenceWaitable) {
1476 df->vkWaitForFences(dev, 1, &frame.imageFence, VK_TRUE, UINT64_MAX);
1477 df->vkResetFences(dev, 1, &frame.imageFence);
1478 frame.imageFenceWaitable = false;
1479 }
1480
1481 // move on to next swapchain image
1482 VkResult err = vkAcquireNextImageKHR(dev, swapChainD->sc, UINT64_MAX,
1483 frame.imageSem, frame.imageFence, &frame.imageIndex);
1484 if (err == VK_SUCCESS || err == VK_SUBOPTIMAL_KHR) {
1485 swapChainD->currentImageIndex = frame.imageIndex;
1486 frame.imageSemWaitable = true;
1487 frame.imageAcquired = true;
1488 frame.imageFenceWaitable = true;
1489 } else if (err == VK_ERROR_OUT_OF_DATE_KHR) {
1490 return QRhi::FrameOpSwapChainOutOfDate;
1491 } else {
1492 if (checkDeviceLost(err))
1493 return QRhi::FrameOpDeviceLost;
1494 else
1495 qWarning("Failed to acquire next swapchain image: %d", err);
1496 return QRhi::FrameOpError;
1497 }
1498 }
1499
1500 // Make sure the previous commands for the same image have finished. (note
1501 // that this is based on the fence from the command buffer submit, nothing
1502 // to do with the Present)
1503 //
1504 // Do this also for any other swapchain's commands with the same frame slot
1505 // While this reduces concurrency, it keeps resource usage safe: swapchain
1506 // A starting its frame 0, followed by swapchain B starting its own frame 0
1507 // will make B wait for A's frame 0 commands, so if a resource is written
1508 // in B's frame or when B checks for pending resource releases, that won't
1509 // mess up A's in-flight commands (as they are not in flight anymore).
1510 waitCommandCompletion(swapChainD->currentFrameSlot);
1511
1512 // Now is the time to read the timestamps for the previous frame for this slot.
1513 if (frame.timestampQueryIndex >= 0) {
1514 quint64 timestamp[2] = { 0, 0 };
1515 VkResult err = df->vkGetQueryPoolResults(dev, timestampQueryPool, frame.timestampQueryIndex, 2,
1516 2 * sizeof(quint64), timestamp, sizeof(quint64), VK_QUERY_RESULT_64_BIT);
1517 timestampQueryPoolMap.clearBit(frame.timestampQueryIndex / 2);
1518 frame.timestampQueryIndex = -1;
1519 if (err == VK_SUCCESS) {
1520 quint64 mask = 0;
1521 for (quint64 i = 0; i < timestampValidBits; i += 8)
1522 mask |= 0xFFULL << i;
1523 const quint64 ts0 = timestamp[0] & mask;
1524 const quint64 ts1 = timestamp[1] & mask;
1525 const float nsecsPerTick = physDevProperties.limits.timestampPeriod;
1526 if (!qFuzzyIsNull(nsecsPerTick)) {
1527 const float elapsedMs = float(ts1 - ts0) * nsecsPerTick / 1000000.0f;
1528 // now we have the gpu time for the previous frame for this slot, report it
1529 // (does not matter that it is not for this frame)
1530 QRHI_PROF_F(swapChainFrameGpuTime(swapChain, elapsedMs));
1531 }
1532 } else {
1533 qWarning("Failed to query timestamp: %d", err);
1534 }
1535 }
1536
1537 // build new draw command buffer
1538 QRhi::FrameOpResult cbres = startCommandBuffer(&frame.cmdBuf);
1539 if (cbres != QRhi::FrameOpSuccess)
1540 return cbres;
1541
1542 // when profiling is enabled, pick a free query (pair) from the pool
1543 int timestampQueryIdx = -1;
1544 if (profilerPrivateOrNull()) {
1545 for (int i = 0; i < timestampQueryPoolMap.count(); ++i) {
1546 if (!timestampQueryPoolMap.testBit(i)) {
1547 timestampQueryPoolMap.setBit(i);
1548 timestampQueryIdx = i * 2;
1549 break;
1550 }
1551 }
1552 }
1553 if (timestampQueryIdx >= 0) {
1554 df->vkCmdResetQueryPool(frame.cmdBuf, timestampQueryPool, timestampQueryIdx, 2);
1555 // record timestamp at the start of the command buffer
1556 df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
1557 timestampQueryPool, timestampQueryIdx);
1558 frame.timestampQueryIndex = timestampQueryIdx;
1559 }
1560
1561 swapChainD->cbWrapper.cb = frame.cmdBuf;
1562 QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
1563 swapChainD->rtWrapper.d.fb = image.fb;
1564
1565 currentFrameSlot = swapChainD->currentFrameSlot;
1566 currentSwapChain = swapChainD;
1567 if (swapChainD->ds)
1568 swapChainD->ds->lastActiveFrameSlot = currentFrameSlot;
1569
1570 QRHI_PROF_F(beginSwapChainFrame(swapChain));
1571
1572 prepareNewFrame(&swapChainD->cbWrapper);
1573
1574 return QRhi::FrameOpSuccess;
1575}
1576
1577QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrameFlags flags)
1578{
1579 QVkSwapChain *swapChainD = QRHI_RES(QVkSwapChain, swapChain);
1580 Q_ASSERT(currentSwapChain == swapChainD);
1581
1582 recordCommandBuffer(&swapChainD->cbWrapper);
1583
1584 QVkSwapChain::FrameResources &frame(swapChainD->frameRes[swapChainD->currentFrameSlot]);
1585 QVkSwapChain::ImageResources &image(swapChainD->imageRes[swapChainD->currentImageIndex]);
1586
1587 if (image.lastUse != QVkSwapChain::ImageResources::ScImageUseRender) {
1588 VkImageMemoryBarrier presTrans;
1589 memset(&presTrans, 0, sizeof(presTrans));
1590 presTrans.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
1591 presTrans.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
1592 presTrans.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
1593 presTrans.image = image.image;
1594 presTrans.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1595 presTrans.subresourceRange.levelCount = presTrans.subresourceRange.layerCount = 1;
1596
1597 if (image.lastUse == QVkSwapChain::ImageResources::ScImageUseNone) {
1598 // was not used at all (no render pass), just transition from undefined to presentable
1599 presTrans.srcAccessMask = 0;
1600 presTrans.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1601 df->vkCmdPipelineBarrier(frame.cmdBuf,
1602 VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
1603 0, 0, nullptr, 0, nullptr,
1604 1, &presTrans);
1605 } else if (image.lastUse == QVkSwapChain::ImageResources::ScImageUseTransferSource) {
1606 // was used in a readback as transfer source, go back to presentable layout
1607 presTrans.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
1608 presTrans.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
1609 df->vkCmdPipelineBarrier(frame.cmdBuf,
1610 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
1611 0, 0, nullptr, 0, nullptr,
1612 1, &presTrans);
1613 }
1614 image.lastUse = QVkSwapChain::ImageResources::ScImageUseRender;
1615 }
1616
1617 // record another timestamp, when enabled
1618 if (frame.timestampQueryIndex >= 0) {
1619 df->vkCmdWriteTimestamp(frame.cmdBuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
1620 timestampQueryPool, frame.timestampQueryIndex + 1);
1621 }
1622
1623 // stop recording and submit to the queue
1624 Q_ASSERT(!frame.cmdFenceWaitable);
1625 const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
1626 QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(frame.cmdBuf,
1627 frame.cmdFence,
1628 frame.imageSemWaitable ? &frame.imageSem : nullptr,
1629 needsPresent ? &frame.drawSem : nullptr);
1630 if (submitres != QRhi::FrameOpSuccess)
1631 return submitres;
1632
1633 frame.imageSemWaitable = false;
1634 frame.cmdFenceWaitable = true;
1635
1636 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
1637 // this must be done before the Present
1638 QRHI_PROF_F(endSwapChainFrame(swapChain, swapChainD->frameCount + 1));
1639
1640 if (needsPresent) {
1641 // add the Present to the queue
1642 VkPresentInfoKHR presInfo;
1643 memset(&presInfo, 0, sizeof(presInfo));
1644 presInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
1645 presInfo.swapchainCount = 1;
1646 presInfo.pSwapchains = &swapChainD->sc;
1647 presInfo.pImageIndices = &swapChainD->currentImageIndex;
1648 presInfo.waitSemaphoreCount = 1;
1649 presInfo.pWaitSemaphores = &frame.drawSem; // gfxQueueFamilyIdx == presQueueFamilyIdx ? &frame.drawSem : &frame.presTransSem;
1650
1651 VkResult err = vkQueuePresentKHR(gfxQueue, &presInfo);
1652 if (err != VK_SUCCESS) {
1653 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
1654 return QRhi::FrameOpSwapChainOutOfDate;
1655 } else if (err != VK_SUBOPTIMAL_KHR) {
1656 if (checkDeviceLost(err))
1657 return QRhi::FrameOpDeviceLost;
1658 else
1659 qWarning("Failed to present: %d", err);
1660 return QRhi::FrameOpError;
1661 }
1662 }
1663
1664 // Do platform-specific WM notification. F.ex. essential on X11 in
1665 // order to prevent glitches on resizing the window.
1666 inst->presentQueued(swapChainD->window);
1667
1668 // mark the current swapchain buffer as unused from our side
1669 frame.imageAcquired = false;
1670 // and move on to the next buffer
1671 swapChainD->currentFrameSlot = (swapChainD->currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
1672 }
1673
1674 swapChainD->frameCount += 1;
1675 currentSwapChain = nullptr;
1676 return QRhi::FrameOpSuccess;
1677}
1678
1679void QRhiVulkan::prepareNewFrame(QRhiCommandBuffer *cb)
1680{
1681 // Now is the time to do things for frame N-F, where N is the current one,
1682 // F is QVK_FRAMES_IN_FLIGHT, because only here it is guaranteed that that
1683 // frame has completed on the GPU (due to the fence wait in beginFrame). To
1684 // decide if something is safe to handle now a simple "lastActiveFrameSlot
1685 // == currentFrameSlot" is sufficient (remember that e.g. with F==2
1686 // currentFrameSlot goes 0, 1, 0, 1, 0, ...)
1687 //
1688 // With multiple swapchains on the same QRhi things get more convoluted
1689 // (and currentFrameSlot strictly alternating is not true anymore) but
1690 // beginNonWrapperFrame() solves that by blocking as necessary so the rest
1691 // here is safe regardless.
1692
1693 executeDeferredReleases();
1694
1695 QRHI_RES(QVkCommandBuffer, cb)->resetState();
1696
1697 finishActiveReadbacks(); // last, in case the readback-completed callback issues rhi calls
1698}
1699
1700QRhi::FrameOpResult QRhiVulkan::startCommandBuffer(VkCommandBuffer *cb)
1701{
1702 if (*cb) {
1703 df->vkFreeCommandBuffers(dev, cmdPool, 1, cb);
1704 *cb = VK_NULL_HANDLE;
1705 }
1706
1707 VkCommandBufferAllocateInfo cmdBufInfo;
1708 memset(&cmdBufInfo, 0, sizeof(cmdBufInfo));
1709 cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
1710 cmdBufInfo.commandPool = cmdPool;
1711 cmdBufInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
1712 cmdBufInfo.commandBufferCount = 1;
1713
1714 VkResult err = df->vkAllocateCommandBuffers(dev, &cmdBufInfo, cb);
1715 if (err != VK_SUCCESS) {
1716 if (checkDeviceLost(err))
1717 return QRhi::FrameOpDeviceLost;
1718 else
1719 qWarning("Failed to allocate frame command buffer: %d", err);
1720 return QRhi::FrameOpError;
1721 }
1722
1723 VkCommandBufferBeginInfo cmdBufBeginInfo;
1724 memset(&cmdBufBeginInfo, 0, sizeof(cmdBufBeginInfo));
1725 cmdBufBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
1726
1727 err = df->vkBeginCommandBuffer(*cb, &cmdBufBeginInfo);
1728 if (err != VK_SUCCESS) {
1729 if (checkDeviceLost(err))
1730 return QRhi::FrameOpDeviceLost;
1731 else
1732 qWarning("Failed to begin frame command buffer: %d", err);
1733 return QRhi::FrameOpError;
1734 }
1735
1736 return QRhi::FrameOpSuccess;
1737}
1738
1739QRhi::FrameOpResult QRhiVulkan::endAndSubmitCommandBuffer(VkCommandBuffer cb, VkFence cmdFence,
1740 VkSemaphore *waitSem, VkSemaphore *signalSem)
1741{
1742 VkResult err = df->vkEndCommandBuffer(cb);
1743 if (err != VK_SUCCESS) {
1744 if (checkDeviceLost(err))
1745 return QRhi::FrameOpDeviceLost;
1746 else
1747 qWarning("Failed to end frame command buffer: %d", err);
1748 return QRhi::FrameOpError;
1749 }
1750
1751 VkSubmitInfo submitInfo;
1752 memset(&submitInfo, 0, sizeof(submitInfo));
1753 submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1754 submitInfo.commandBufferCount = 1;
1755 submitInfo.pCommandBuffers = &cb;
1756 if (waitSem) {
1757 submitInfo.waitSemaphoreCount = 1;
1758 submitInfo.pWaitSemaphores = waitSem;
1759 }
1760 if (signalSem) {
1761 submitInfo.signalSemaphoreCount = 1;
1762 submitInfo.pSignalSemaphores = signalSem;
1763 }
1764 VkPipelineStageFlags psf = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
1765 submitInfo.pWaitDstStageMask = &psf;
1766
1767 err = df->vkQueueSubmit(gfxQueue, 1, &submitInfo, cmdFence);
1768 if (err != VK_SUCCESS) {
1769 if (checkDeviceLost(err))
1770 return QRhi::FrameOpDeviceLost;
1771 else
1772 qWarning("Failed to submit to graphics queue: %d", err);
1773 return QRhi::FrameOpError;
1774 }
1775
1776 return QRhi::FrameOpSuccess;
1777}
1778
1779void QRhiVulkan::waitCommandCompletion(int frameSlot)
1780{
1781 for (QVkSwapChain *sc : qAsConst(swapchains)) {
1782 QVkSwapChain::FrameResources &frame(sc->frameRes[frameSlot]);
1783 if (frame.cmdFenceWaitable) {
1784 df->vkWaitForFences(dev, 1, &frame.cmdFence, VK_TRUE, UINT64_MAX);
1785 df->vkResetFences(dev, 1, &frame.cmdFence);
1786 frame.cmdFenceWaitable = false;
1787 }
1788 }
1789}
1790
1791QRhi::FrameOpResult QRhiVulkan::beginOffscreenFrame(QRhiCommandBuffer **cb)
1792{
1793 QRhi::FrameOpResult cbres = startCommandBuffer(&ofr.cbWrapper.cb);
1794 if (cbres != QRhi::FrameOpSuccess)
1795 return cbres;
1796
1797 // Switch to the next slot manually. Swapchains do not know about this
1798 // which is good. So for example a - unusual but possible - onscreen,
1799 // onscreen, offscreen, onscreen, onscreen, onscreen sequence of
1800 // begin/endFrame leads to 0, 1, 0, 0, 1, 0. This works because the
1801 // offscreen frame is synchronous in the sense that we wait for execution
1802 // to complete in endFrame, and so no resources used in that frame are busy
1803 // anymore in the next frame.
1804 currentFrameSlot = (currentFrameSlot + 1) % QVK_FRAMES_IN_FLIGHT;
1805 // except that this gets complicated with multiple swapchains so make sure
1806 // any pending commands have finished for the frame slot we are going to use
1807 if (swapchains.count() > 1)
1808 waitCommandCompletion(currentFrameSlot);
1809
1810 prepareNewFrame(&ofr.cbWrapper);
1811 ofr.active = true;
1812
1813 *cb = &ofr.cbWrapper;
1814 return QRhi::FrameOpSuccess;
1815}
1816
1817QRhi::FrameOpResult QRhiVulkan::endOffscreenFrame()
1818{
1819 Q_ASSERT(ofr.active);
1820 ofr.active = false;
1821
1822 recordCommandBuffer(&ofr.cbWrapper);
1823
1824 if (!ofr.cmdFence) {
1825 VkFenceCreateInfo fenceInfo;
1826 memset(&fenceInfo, 0, sizeof(fenceInfo));
1827 fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1828 VkResult err = df->vkCreateFence(dev, &fenceInfo, nullptr, &ofr.cmdFence);
1829 if (err != VK_SUCCESS) {
1830 qWarning("Failed to create command buffer fence: %d", err);
1831 return QRhi::FrameOpError;
1832 }
1833 }
1834
1835 QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(ofr.cbWrapper.cb, ofr.cmdFence, nullptr, nullptr);
1836 if (submitres != QRhi::FrameOpSuccess)
1837 return submitres;
1838
1839 // wait for completion
1840 df->vkWaitForFences(dev, 1, &ofr.cmdFence, VK_TRUE, UINT64_MAX);
1841 df->vkResetFences(dev, 1, &ofr.cmdFence);
1842
1843 // Here we know that executing the host-side reads for this (or any
1844 // previous) frame is safe since we waited for completion above.
1845 finishActiveReadbacks(true);
1846
1847 return QRhi::FrameOpSuccess;
1848}
1849
1850QRhi::FrameOpResult QRhiVulkan::finish()
1851{
1852 QVkSwapChain *swapChainD = nullptr;
1853 if (inFrame) {
1854 // There is either a swapchain or an offscreen frame on-going.
1855 // End command recording and submit what we have.
1856 VkCommandBuffer cb;
1857 if (ofr.active) {
1858 Q_ASSERT(!currentSwapChain);
1859 recordCommandBuffer(&ofr.cbWrapper);
1860 cb = ofr.cbWrapper.cb;
1861 } else {
1862 Q_ASSERT(currentSwapChain);
1863 swapChainD = currentSwapChain;
1864 recordCommandBuffer(&swapChainD->cbWrapper);
1865 cb = swapChainD->cbWrapper.cb;
1866 }
1867 QRhi::FrameOpResult submitres = endAndSubmitCommandBuffer(cb, VK_NULL_HANDLE, nullptr, nullptr);
1868 if (submitres != QRhi::FrameOpSuccess)
1869 return submitres;
1870 }
1871
1872 df->vkQueueWaitIdle(gfxQueue);
1873
1874 if (inFrame) {
1875 // Allocate and begin recording on a new command buffer.
1876 if (ofr.active)
1877 startCommandBuffer(&ofr.cbWrapper.cb);
1878 else
1879 startCommandBuffer(&swapChainD->frameRes[swapChainD->currentFrameSlot].cmdBuf);
1880 }
1881
1882 executeDeferredReleases(true);
1883 finishActiveReadbacks(true);
1884
1885 return QRhi::FrameOpSuccess;
1886}
1887
1888static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkBuffer::UsageState &bufUsage)
1889{
1890 QRhiPassResourceTracker::UsageState u;
1891 u.layout = 0; // unused with buffers
1892 u.access = bufUsage.access;
1893 u.stage = bufUsage.stage;
1894 return u;
1895}
1896
1897static inline QRhiPassResourceTracker::UsageState toPassTrackerUsageState(const QVkTexture::UsageState &texUsage)
1898{
1899 QRhiPassResourceTracker::UsageState u;
1900 u.layout = texUsage.layout;
1901 u.access = texUsage.access;
1902 u.stage = texUsage.stage;
1903 return u;
1904}
1905
1906void QRhiVulkan::activateTextureRenderTarget(QVkCommandBuffer *cbD, QVkTextureRenderTarget *rtD)
1907{
1908 rtD->lastActiveFrameSlot = currentFrameSlot;
1909 rtD->d.rp->lastActiveFrameSlot = currentFrameSlot;
1910 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
1911 const QVector<QRhiColorAttachment> colorAttachments = rtD->m_desc.colorAttachments();
1912 for (const QRhiColorAttachment &colorAttachment : colorAttachments) {
1913 QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachment.texture());
1914 QVkTexture *resolveTexD = QRHI_RES(QVkTexture, colorAttachment.resolveTexture());
1915 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachment.renderBuffer());
1916 if (texD) {
1917 trackedRegisterTexture(&passResTracker, texD,
1918 QRhiPassResourceTracker::TexColorOutput,
1919 QRhiPassResourceTracker::TexColorOutputStage);
1920 texD->lastActiveFrameSlot = currentFrameSlot;
1921 } else if (rbD) {
1922 // Won't register rbD->backingTexture because it cannot be used for
1923 // anything in a renderpass, its use makes only sense in
1924 // combination with a resolveTexture.
1925 rbD->lastActiveFrameSlot = currentFrameSlot;
1926 }
1927 if (resolveTexD) {
1928 trackedRegisterTexture(&passResTracker, resolveTexD,
1929 QRhiPassResourceTracker::TexColorOutput,
1930 QRhiPassResourceTracker::TexColorOutputStage);
1931 resolveTexD->lastActiveFrameSlot = currentFrameSlot;
1932 }
1933 }
1934 if (rtD->m_desc.depthStencilBuffer())
1935 QRHI_RES(QVkRenderBuffer, rtD->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
1936 if (rtD->m_desc.depthTexture()) {
1937 QVkTexture *depthTexD = QRHI_RES(QVkTexture, rtD->m_desc.depthTexture());
1938 trackedRegisterTexture(&passResTracker, depthTexD,
1939 QRhiPassResourceTracker::TexDepthOutput,
1940 QRhiPassResourceTracker::TexDepthOutputStage);
1941 depthTexD->lastActiveFrameSlot = currentFrameSlot;
1942 }
1943}
1944
1945void QRhiVulkan::resourceUpdate(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
1946{
1947 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
1948 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
1949
1950 enqueueResourceUpdates(cbD, resourceUpdates);
1951}
1952
1953void QRhiVulkan::beginPass(QRhiCommandBuffer *cb,
1954 QRhiRenderTarget *rt,
1955 const QColor &colorClearValue,
1956 const QRhiDepthStencilClearValue &depthStencilClearValue,
1957 QRhiResourceUpdateBatch *resourceUpdates)
1958{
1959 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
1960 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
1961
1962 if (resourceUpdates)
1963 enqueueResourceUpdates(cbD, resourceUpdates);
1964
1965 // Insert a TransitionPassResources into the command stream, pointing to
1966 // the tracker this pass is going to use. That's how we generate the
1967 // barriers later during recording the real VkCommandBuffer, right before
1968 // the vkCmdBeginRenderPass.
1969 enqueueTransitionPassResources(cbD);
1970
1971 QVkRenderTargetData *rtD = nullptr;
1972 switch (rt->resourceType()) {
1973 case QRhiResource::RenderTarget:
1974 rtD = &QRHI_RES(QVkReferenceRenderTarget, rt)->d;
1975 rtD->rp->lastActiveFrameSlot = currentFrameSlot;
1976 Q_ASSERT(currentSwapChain);
1977 currentSwapChain->imageRes[currentSwapChain->currentImageIndex].lastUse =
1978 QVkSwapChain::ImageResources::ScImageUseRender;
1979 break;
1980 case QRhiResource::TextureRenderTarget:
1981 {
1982 QVkTextureRenderTarget *rtTex = QRHI_RES(QVkTextureRenderTarget, rt);
1983 rtD = &rtTex->d;
1984 activateTextureRenderTarget(cbD, rtTex);
1985 }
1986 break;
1987 default:
1988 Q_UNREACHABLE();
1989 break;
1990 }
1991
1992 cbD->recordingPass = QVkCommandBuffer::RenderPass;
1993 cbD->currentTarget = rt;
1994
1995 // No copy operations or image layout transitions allowed after this point
1996 // (up until endPass) as we are going to begin the renderpass.
1997
1998 VkRenderPassBeginInfo rpBeginInfo;
1999 memset(&rpBeginInfo, 0, sizeof(rpBeginInfo));
2000 rpBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
2001 rpBeginInfo.renderPass = rtD->rp->rp;
2002 rpBeginInfo.framebuffer = rtD->fb;
2003 rpBeginInfo.renderArea.extent.width = rtD->pixelSize.width();
2004 rpBeginInfo.renderArea.extent.height = rtD->pixelSize.height();
2005
2006 QVarLengthArray<VkClearValue, 4> cvs;
2007 for (int i = 0; i < rtD->colorAttCount; ++i) {
2008 VkClearValue cv;
2009 cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()),
2010 float(colorClearValue.alphaF()) } };
2011 cvs.append(cv);
2012 }
2013 for (int i = 0; i < rtD->dsAttCount; ++i) {
2014 VkClearValue cv;
2015 cv.depthStencil = { depthStencilClearValue.depthClearValue(), depthStencilClearValue.stencilClearValue() };
2016 cvs.append(cv);
2017 }
2018 for (int i = 0; i < rtD->resolveAttCount; ++i) {
2019 VkClearValue cv;
2020 cv.color = { { float(colorClearValue.redF()), float(colorClearValue.greenF()), float(colorClearValue.blueF()),
2021 float(colorClearValue.alphaF()) } };
2022 cvs.append(cv);
2023 }
2024 rpBeginInfo.clearValueCount = cvs.count();
2025
2026 QVkCommandBuffer::Command cmd;
2027 cmd.cmd = QVkCommandBuffer::Command::BeginRenderPass;
2028 cmd.args.beginRenderPass.desc = rpBeginInfo;
2029 cmd.args.beginRenderPass.clearValueIndex = cbD->pools.clearValue.count();
2030 cbD->pools.clearValue.append(cvs.constData(), cvs.count());
2031 cbD->commands.append(cmd);
2032}
2033
2034void QRhiVulkan::endPass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2035{
2036 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
2037 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
2038
2039 QVkCommandBuffer::Command cmd;
2040 cmd.cmd = QVkCommandBuffer::Command::EndRenderPass;
2041 cbD->commands.append(cmd);
2042
2043 cbD->recordingPass = QVkCommandBuffer::NoPass;
2044 cbD->currentTarget = nullptr;
2045
2046 if (resourceUpdates)
2047 enqueueResourceUpdates(cbD, resourceUpdates);
2048}
2049
2050void QRhiVulkan::beginComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2051{
2052 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
2053 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
2054
2055 if (resourceUpdates)
2056 enqueueResourceUpdates(cbD, resourceUpdates);
2057
2058 enqueueTransitionPassResources(cbD);
2059
2060 cbD->recordingPass = QVkCommandBuffer::ComputePass;
2061}
2062
2063void QRhiVulkan::endComputePass(QRhiCommandBuffer *cb, QRhiResourceUpdateBatch *resourceUpdates)
2064{
2065 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
2066 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
2067
2068 cbD->recordingPass = QVkCommandBuffer::NoPass;
2069
2070 if (resourceUpdates)
2071 enqueueResourceUpdates(cbD, resourceUpdates);
2072}
2073
2074void QRhiVulkan::setComputePipeline(QRhiCommandBuffer *cb, QRhiComputePipeline *ps)
2075{
2076 QVkComputePipeline *psD = QRHI_RES(QVkComputePipeline, ps);
2077 Q_ASSERT(psD->pipeline);
2078 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
2079 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
2080
2081 if (cbD->currentComputePipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
2082 QVkCommandBuffer::Command cmd;
2083 cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
2084 cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_COMPUTE;
2085 cmd.args.bindPipeline.pipeline = psD->pipeline;
2086 cbD->commands.append(cmd);
2087
2088 cbD->currentGraphicsPipeline = nullptr;
2089 cbD->currentComputePipeline = ps;
2090 cbD->currentPipelineGeneration = psD->generation;
2091 }
2092
2093 psD->lastActiveFrameSlot = currentFrameSlot;
2094}
2095
2096void QRhiVulkan::dispatch(QRhiCommandBuffer *cb, int x, int y, int z)
2097{
2098 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
2099 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::ComputePass);
2100
2101 QVkCommandBuffer::Command cmd;
2102 cmd.cmd = QVkCommandBuffer::Command::Dispatch;
2103 cmd.args.dispatch.x = x;
2104 cmd.args.dispatch.y = y;
2105 cmd.args.dispatch.z = z;
2106 cbD->commands.append(cmd);
2107}
2108
2109VkShaderModule QRhiVulkan::createShader(const QByteArray &spirv)
2110{
2111 VkShaderModuleCreateInfo shaderInfo;
2112 memset(&shaderInfo, 0, sizeof(shaderInfo));
2113 shaderInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
2114 shaderInfo.codeSize = spirv.size();
2115 shaderInfo.pCode = reinterpret_cast<const quint32 *>(spirv.constData());
2116 VkShaderModule shaderModule;
2117 VkResult err = df->vkCreateShaderModule(dev, &shaderInfo, nullptr, &shaderModule);
2118 if (err != VK_SUCCESS) {
2119 qWarning("Failed to create shader module: %d", err);
2120 return VK_NULL_HANDLE;
2121 }
2122 return shaderModule;
2123}
2124
2125bool QRhiVulkan::ensurePipelineCache()
2126{
2127 if (pipelineCache)
2128 return true;
2129
2130 VkPipelineCacheCreateInfo pipelineCacheInfo;
2131 memset(&pipelineCacheInfo, 0, sizeof(pipelineCacheInfo));
2132 pipelineCacheInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
2133 VkResult err = df->vkCreatePipelineCache(dev, &pipelineCacheInfo, nullptr, &pipelineCache);
2134 if (err != VK_SUCCESS) {
2135 qWarning("Failed to create pipeline cache: %d", err);
2136 return false;
2137 }
2138 return true;
2139}
2140
2141void QRhiVulkan::updateShaderResourceBindings(QRhiShaderResourceBindings *srb, int descSetIdx)
2142{
2143 QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
2144
2145 QVarLengthArray<VkDescriptorBufferInfo, 4> bufferInfos;
2146 QVarLengthArray<VkDescriptorImageInfo, 4> imageInfos;
2147 QVarLengthArray<VkWriteDescriptorSet, 8> writeInfos;
2148
2149 const bool updateAll = descSetIdx < 0;
2150 int frameSlot = updateAll ? 0 : descSetIdx;
2151 while (frameSlot < (updateAll ? QVK_FRAMES_IN_FLIGHT : descSetIdx + 1)) {
2152 srbD->boundResourceData[frameSlot].resize(srbD->sortedBindings.count());
2153 for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
2154 const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
2155 QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[frameSlot][i]);
2156
2157 VkWriteDescriptorSet writeInfo;
2158 memset(&writeInfo, 0, sizeof(writeInfo));
2159 writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
2160 writeInfo.dstSet = srbD->descSets[frameSlot];
2161 writeInfo.dstBinding = b->binding;
2162 writeInfo.descriptorCount = 1;
2163
2164 switch (b->type) {
2165 case QRhiShaderResourceBinding::UniformBuffer:
2166 {
2167 writeInfo.descriptorType = b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
2168 : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
2169 QRhiBuffer *buf = b->u.ubuf.buf;
2170 QVkBuffer *bufD = QRHI_RES(QVkBuffer, buf);
2171 bd.ubuf.id = bufD->m_id;
2172 bd.ubuf.generation = bufD->generation;
2173 VkDescriptorBufferInfo bufInfo;
2174 bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
2175 bufInfo.offset = b->u.ubuf.offset;
2176 bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
2177 // be nice and assert when we know the vulkan device would die a horrible death due to non-aligned reads
2178 Q_ASSERT(aligned(bufInfo.offset, ubufAlign) == bufInfo.offset);
2179 bufferInfos.append(bufInfo);
2180 writeInfo.pBufferInfo = &bufferInfos.last();
2181 }
2182 break;
2183 case QRhiShaderResourceBinding::SampledTexture:
2184 {
2185 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex);
2186 QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler);
2187 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
2188 bd.stex.texId = texD->m_id;
2189 bd.stex.texGeneration = texD->generation;
2190 bd.stex.samplerId = samplerD->m_id;
2191 bd.stex.samplerGeneration = samplerD->generation;
2192 VkDescriptorImageInfo imageInfo;
2193 imageInfo.sampler = samplerD->sampler;
2194 imageInfo.imageView = texD->imageView;
2195 imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
2196 imageInfos.append(imageInfo);
2197 writeInfo.pImageInfo = &imageInfos.last();
2198 }
2199 break;
2200 case QRhiShaderResourceBinding::ImageLoad:
2201 Q_FALLTHROUGH();
2202 case QRhiShaderResourceBinding::ImageStore:
2203 Q_FALLTHROUGH();
2204 case QRhiShaderResourceBinding::ImageLoadStore:
2205 {
2206 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
2207 VkImageView view = texD->imageViewForLevel(b->u.simage.level);
2208 if (view) {
2209 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
2210 bd.simage.id = texD->m_id;
2211 bd.simage.generation = texD->generation;
2212 VkDescriptorImageInfo imageInfo;
2213 imageInfo.sampler = VK_NULL_HANDLE;
2214 imageInfo.imageView = view;
2215 imageInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL;
2216 imageInfos.append(imageInfo);
2217 writeInfo.pImageInfo = &imageInfos.last();
2218 }
2219 }
2220 break;
2221 case QRhiShaderResourceBinding::BufferLoad:
2222 Q_FALLTHROUGH();
2223 case QRhiShaderResourceBinding::BufferStore:
2224 Q_FALLTHROUGH();
2225 case QRhiShaderResourceBinding::BufferLoadStore:
2226 {
2227 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
2228 writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
2229 bd.sbuf.id = bufD->m_id;
2230 bd.sbuf.generation = bufD->generation;
2231 VkDescriptorBufferInfo bufInfo;
2232 bufInfo.buffer = bufD->m_type == QRhiBuffer::Dynamic ? bufD->buffers[frameSlot] : bufD->buffers[0];
2233 bufInfo.offset = b->u.ubuf.offset;
2234 bufInfo.range = b->u.ubuf.maybeSize ? b->u.ubuf.maybeSize : bufD->m_size;
2235 bufferInfos.append(bufInfo);
2236 writeInfo.pBufferInfo = &bufferInfos.last();
2237 }
2238 break;
2239 default:
2240 continue;
2241 }
2242
2243 writeInfos.append(writeInfo);
2244 }
2245 ++frameSlot;
2246 }
2247
2248 df->vkUpdateDescriptorSets(dev, writeInfos.count(), writeInfos.constData(), 0, nullptr);
2249}
2250
2251static inline bool accessIsWrite(VkAccessFlags access)
2252{
2253 return (access & VK_ACCESS_SHADER_WRITE_BIT) != 0
2254 || (access & VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) != 0
2255 || (access & VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT) != 0
2256 || (access & VK_ACCESS_TRANSFER_WRITE_BIT) != 0
2257 || (access & VK_ACCESS_HOST_WRITE_BIT) != 0
2258 || (access & VK_ACCESS_MEMORY_WRITE_BIT) != 0;
2259}
2260
2261void QRhiVulkan::trackedBufferBarrier(QVkCommandBuffer *cbD, QVkBuffer *bufD, int slot,
2262 VkAccessFlags access, VkPipelineStageFlags stage)
2263{
2264 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
2265 Q_ASSERT(access && stage);
2266 QVkBuffer::UsageState &s(bufD->usageState[slot]);
2267 if (!s.stage) {
2268 s.access = access;
2269 s.stage = stage;
2270 return;
2271 }
2272
2273 if (s.access == access && s.stage == stage) {
2274 // No need to flood with unnecessary read-after-read barriers.
2275 // Write-after-write is a different matter, however.
2276 if (!accessIsWrite(access))
2277 return;
2278 }
2279
2280 VkBufferMemoryBarrier bufMemBarrier;
2281 memset(&bufMemBarrier, 0, sizeof(bufMemBarrier));
2282 bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
2283 bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2284 bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2285 bufMemBarrier.srcAccessMask = s.access;
2286 bufMemBarrier.dstAccessMask = access;
2287 bufMemBarrier.buffer = bufD->buffers[slot];
2288 bufMemBarrier.size = VK_WHOLE_SIZE;
2289
2290 QVkCommandBuffer::Command cmd;
2291 cmd.cmd = QVkCommandBuffer::Command::BufferBarrier;
2292 cmd.args.bufferBarrier.srcStageMask = s.stage;
2293 cmd.args.bufferBarrier.dstStageMask = stage;
2294 cmd.args.bufferBarrier.desc = bufMemBarrier;
2295 cbD->commands.append(cmd);
2296
2297 s.access = access;
2298 s.stage = stage;
2299}
2300
2301void QRhiVulkan::trackedImageBarrier(QVkCommandBuffer *cbD, QVkTexture *texD,
2302 VkImageLayout layout, VkAccessFlags access, VkPipelineStageFlags stage)
2303{
2304 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
2305 Q_ASSERT(layout && access && stage);
2306 QVkTexture::UsageState &s(texD->usageState);
2307 if (s.access == access && s.stage == stage && s.layout == layout) {
2308 if (!accessIsWrite(access))
2309 return;
2310 }
2311
2312 VkImageMemoryBarrier barrier;
2313 memset(&barrier, 0, sizeof(barrier));
2314 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2315 barrier.subresourceRange.aspectMask = !isDepthTextureFormat(texD->m_format)
2316 ? VK_IMAGE_ASPECT_COLOR_BIT : VK_IMAGE_ASPECT_DEPTH_BIT;
2317 barrier.subresourceRange.baseMipLevel = 0;
2318 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
2319 barrier.subresourceRange.baseArrayLayer = 0;
2320 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
2321 barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
2322 barrier.newLayout = layout;
2323 barrier.srcAccessMask = s.access; // may be 0 but that's fine
2324 barrier.dstAccessMask = access;
2325 barrier.image = texD->image;
2326
2327 VkPipelineStageFlags srcStage = s.stage;
2328 // stage mask cannot be 0
2329 if (!srcStage)
2330 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
2331
2332 QVkCommandBuffer::Command cmd;
2333 cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
2334 cmd.args.imageBarrier.srcStageMask = srcStage;
2335 cmd.args.imageBarrier.dstStageMask = stage;
2336 cmd.args.imageBarrier.desc = barrier;
2337 cbD->commands.append(cmd);
2338
2339 s.layout = layout;
2340 s.access = access;
2341 s.stage = stage;
2342}
2343
2344void QRhiVulkan::subresourceBarrier(QVkCommandBuffer *cbD, VkImage image,
2345 VkImageLayout oldLayout, VkImageLayout newLayout,
2346 VkAccessFlags srcAccess, VkAccessFlags dstAccess,
2347 VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage,
2348 int startLayer, int layerCount,
2349 int startLevel, int levelCount)
2350{
2351 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
2352 VkImageMemoryBarrier barrier;
2353 memset(&barrier, 0, sizeof(barrier));
2354 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2355 barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2356 barrier.subresourceRange.baseMipLevel = startLevel;
2357 barrier.subresourceRange.levelCount = levelCount;
2358 barrier.subresourceRange.baseArrayLayer = startLayer;
2359 barrier.subresourceRange.layerCount = layerCount;
2360 barrier.oldLayout = oldLayout;
2361 barrier.newLayout = newLayout;
2362 barrier.srcAccessMask = srcAccess;
2363 barrier.dstAccessMask = dstAccess;
2364 barrier.image = image;
2365
2366 QVkCommandBuffer::Command cmd;
2367 cmd.cmd = QVkCommandBuffer::Command::ImageBarrier;
2368 cmd.args.imageBarrier.srcStageMask = srcStage;
2369 cmd.args.imageBarrier.dstStageMask = dstStage;
2370 cmd.args.imageBarrier.desc = barrier;
2371 cbD->commands.append(cmd);
2372}
2373
2374VkDeviceSize QRhiVulkan::subresUploadByteSize(const QRhiTextureSubresourceUploadDescription &subresDesc) const
2375{
2376 VkDeviceSize size = 0;
2377 const qsizetype imageSizeBytes = subresDesc.image().isNull() ?
2378 subresDesc.data().size() : subresDesc.image().sizeInBytes();
2379 if (imageSizeBytes > 0)
2380 size += aligned(imageSizeBytes, texbufAlign);
2381 return size;
2382}
2383
2384void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
2385 const QRhiTextureSubresourceUploadDescription &subresDesc,
2386 size_t *curOfs, void *mp,
2387 BufferImageCopyList *copyInfos)
2388{
2389 qsizetype copySizeBytes = 0;
2390 qsizetype imageSizeBytes = 0;
2391 const void *src = nullptr;
2392
2393 VkBufferImageCopy copyInfo;
2394 memset(&copyInfo, 0, sizeof(copyInfo));
2395 copyInfo.bufferOffset = *curOfs;
2396 copyInfo.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2397 copyInfo.imageSubresource.mipLevel = level;
2398 copyInfo.imageSubresource.baseArrayLayer = layer;
2399 copyInfo.imageSubresource.layerCount = 1;
2400 copyInfo.imageExtent.depth = 1;
2401
2402 const QByteArray rawData = subresDesc.data();
2403 const QPoint dp = subresDesc.destinationTopLeft();
2404 QImage image = subresDesc.image();
2405 if (!image.isNull()) {
2406 copySizeBytes = imageSizeBytes = image.sizeInBytes();
2407 QSize size = image.size();
2408 src = image.constBits();
2409 // Scanlines in QImage are 4 byte aligned so bpl must
2410 // be taken into account for bufferRowLength.
2411 int bpc = qMax(1, image.depth() / 8);
2412 // this is in pixels, not bytes, to make it more complicated...
2413 copyInfo.bufferRowLength = image.bytesPerLine() / bpc;
2414 if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
2415 const int sx = subresDesc.sourceTopLeft().x();
2416 const int sy = subresDesc.sourceTopLeft().y();
2417 if (!subresDesc.sourceSize().isEmpty())
2418 size = subresDesc.sourceSize();
2419 if (image.depth() == 32) {
2420 // The staging buffer will get the full image
2421 // regardless, just adjust the vk
2422 // buffer-to-image copy start offset.
2423 copyInfo.bufferOffset += sy * image.bytesPerLine() + sx * 4;
2424 // bufferRowLength remains set to the original image's width
2425 } else {
2426 image = image.copy(sx, sy, size.width(), size.height());
2427 src = image.constBits();
2428 // The staging buffer gets the slice only. The rest of the
2429 // space reserved for this mip will be unused.
2430 copySizeBytes = image.sizeInBytes();
2431 bpc = qMax(1, image.depth() / 8);
2432 copyInfo.bufferRowLength = image.bytesPerLine() / bpc;
2433 }
2434 }
2435 copyInfo.imageOffset.x = dp.x();
2436 copyInfo.imageOffset.y = dp.y();
2437 copyInfo.imageExtent.width = size.width();
2438 copyInfo.imageExtent.height = size.height();
2439 copyInfos->append(copyInfo);
2440 } else if (!rawData.isEmpty() && isCompressedFormat(texD->m_format)) {
2441 copySizeBytes = imageSizeBytes = rawData.size();
2442 src = rawData.constData();
2443 QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
2444 const int subresw = size.width();
2445 const int subresh = size.height();
2446 if (!subresDesc.sourceSize().isEmpty())
2447 size = subresDesc.sourceSize();
2448 const int w = size.width();
2449 const int h = size.height();
2450 QSize blockDim;
2451 compressedFormatInfo(texD->m_format, QSize(w, h), nullptr, nullptr, &blockDim);
2452 // x and y must be multiples of the block width and height
2453 copyInfo.imageOffset.x = aligned(dp.x(), blockDim.width());
2454 copyInfo.imageOffset.y = aligned(dp.y(), blockDim.height());
2455 // width and height must be multiples of the block width and height
2456 // or x + width and y + height must equal the subresource width and height
2457 copyInfo.imageExtent.width = dp.x() + w == subresw ? w : aligned(w, blockDim.width());
2458 copyInfo.imageExtent.height = dp.y() + h == subresh ? h : aligned(h, blockDim.height());
2459 copyInfos->append(copyInfo);
2460 } else if (!rawData.isEmpty()) {
2461 copySizeBytes = imageSizeBytes = rawData.size();
2462 src = rawData.constData();
2463 QSize size = q->sizeForMipLevel(level, texD->m_pixelSize);
2464 if (!subresDesc.sourceSize().isEmpty())
2465 size = subresDesc.sourceSize();
2466 copyInfo.imageOffset.x = dp.x();
2467 copyInfo.imageOffset.y = dp.y();
2468 copyInfo.imageExtent.width = size.width();
2469 copyInfo.imageExtent.height = size.height();
2470 copyInfos->append(copyInfo);
2471 } else {
2472 qWarning("Invalid texture upload for %p layer=%d mip=%d", texD, layer, level);
2473 }
2474
2475 memcpy(reinterpret_cast<char *>(mp) + *curOfs, src, copySizeBytes);
2476 *curOfs += aligned(imageSizeBytes, texbufAlign);
2477}
2478
2479void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates)
2480{
2481 QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates);
2482 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
2483
2484 for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : ud->dynamicBufferUpdates) {
2485 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
2486 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2487 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
2488 bufD->pendingDynamicUpdates[i].append(u);
2489 }
2490
2491 for (const QRhiResourceUpdateBatchPrivate::StaticBufferUpload &u : ud->staticBufferUploads) {
2492 QVkBuffer *bufD = QRHI_RES(QVkBuffer, u.buf);
2493 Q_ASSERT(bufD->m_type != QRhiBuffer::Dynamic);
2494 Q_ASSERT(u.offset + u.data.size() <= bufD->m_size);
2495
2496 if (!bufD->stagingBuffers[currentFrameSlot]) {
2497 VkBufferCreateInfo bufferInfo;
2498 memset(&bufferInfo, 0, sizeof(bufferInfo));
2499 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
2500 // must cover the entire buffer - this way multiple, partial updates per frame
2501 // are supported even when the staging buffer is reused (Static)
2502 bufferInfo.size = bufD->m_size;
2503 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2504
2505 VmaAllocationCreateInfo allocInfo;
2506 memset(&allocInfo, 0, sizeof(allocInfo));
2507 allocInfo.usage = VMA_MEMORY_USAGE_CPU_ONLY;
2508
2509 VmaAllocation allocation;
2510 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
2511 &bufD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
2512 if (err == VK_SUCCESS) {
2513 bufD->stagingAllocations[currentFrameSlot] = allocation;
2514 QRHI_PROF_F(newBufferStagingArea(bufD, currentFrameSlot, bufD->m_size));
2515 } else {
2516 qWarning("Failed to create staging buffer of size %d: %d", bufD->m_size, err);
2517 continue;
2518 }
2519 }
2520
2521 void *p = nullptr;
2522 VmaAllocation a = toVmaAllocation(bufD->stagingAllocations[currentFrameSlot]);
2523 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
2524 if (err != VK_SUCCESS) {
2525 qWarning("Failed to map buffer: %d", err);
2526 continue;
2527 }
2528 memcpy(static_cast<uchar *>(p) + u.offset, u.data.constData(), u.data.size());
2529 vmaUnmapMemory(toVmaAllocator(allocator), a);
2530 vmaFlushAllocation(toVmaAllocator(allocator), a, u.offset, u.data.size());
2531
2532 trackedBufferBarrier(cbD, bufD, 0,
2533 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
2534
2535 VkBufferCopy copyInfo;
2536 memset(&copyInfo, 0, sizeof(copyInfo));
2537 copyInfo.srcOffset = u.offset;
2538 copyInfo.dstOffset = u.offset;
2539 copyInfo.size = u.data.size();
2540
2541 QVkCommandBuffer::Command cmd;
2542 cmd.cmd = QVkCommandBuffer::Command::CopyBuffer;
2543 cmd.args.copyBuffer.src = bufD->stagingBuffers[currentFrameSlot];
2544 cmd.args.copyBuffer.dst = bufD->buffers[0];
2545 cmd.args.copyBuffer.desc = copyInfo;
2546 cbD->commands.append(cmd);
2547
2548 // Where's the barrier for read-after-write? (assuming the common case
2549 // of binding this buffer as vertex/index, or, less likely, as uniform
2550 // buffer, in a renderpass later on) That is handled by the pass
2551 // resource tracking: the appropriate pipeline barrier will be
2552 // generated and recorded right before the renderpass, that binds this
2553 // buffer in one of its commands, gets its BeginRenderPass recorded.
2554
2555 bufD->lastActiveFrameSlot = currentFrameSlot;
2556
2557 if (bufD->m_type == QRhiBuffer::Immutable) {
2558 QRhiVulkan::DeferredReleaseEntry e;
2559 e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer;
2560 e.lastActiveFrameSlot = currentFrameSlot;
2561 e.stagingBuffer.stagingBuffer = bufD->stagingBuffers[currentFrameSlot];
2562 e.stagingBuffer.stagingAllocation = bufD->stagingAllocations[currentFrameSlot];
2563 bufD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
2564 bufD->stagingAllocations[currentFrameSlot] = nullptr;
2565 releaseQueue.append(e);
2566 QRHI_PROF_F(releaseBufferStagingArea(bufD, currentFrameSlot));
2567 }
2568 }
2569
2570 for (const QRhiResourceUpdateBatchPrivate::TextureOp &u : ud->textureOps) {
2571 if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Upload) {
2572 QVkTexture *utexD = QRHI_RES(QVkTexture, u.upload.tex);
2573 // batch into a single staging buffer and a single CopyBufferToImage with multiple copyInfos
2574 VkDeviceSize stagingSize = 0;
2575 for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
2576 for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
2577 for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(u.upload.subresDesc[layer][level]))
2578 stagingSize += subresUploadByteSize(subresDesc);
2579 }
2580 }
2581
2582 Q_ASSERT(!utexD->stagingBuffers[currentFrameSlot]);
2583 VkBufferCreateInfo bufferInfo;
2584 memset(&bufferInfo, 0, sizeof(bufferInfo));
2585 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
2586 bufferInfo.size = stagingSize;
2587 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
2588
2589 VmaAllocationCreateInfo allocInfo;
2590 memset(&allocInfo, 0, sizeof(allocInfo));
2591 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
2592
2593 VmaAllocation allocation;
2594 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo,
2595 &utexD->stagingBuffers[currentFrameSlot], &allocation, nullptr);
2596 if (err != VK_SUCCESS) {
2597 qWarning("Failed to create image staging buffer of size %d: %d", int(stagingSize), err);
2598 continue;
2599 }
2600 utexD->stagingAllocations[currentFrameSlot] = allocation;
2601 QRHI_PROF_F(newTextureStagingArea(utexD, currentFrameSlot, stagingSize));
2602
2603 BufferImageCopyList copyInfos;
2604 size_t curOfs = 0;
2605 void *mp = nullptr;
2606 VmaAllocation a = toVmaAllocation(utexD->stagingAllocations[currentFrameSlot]);
2607 err = vmaMapMemory(toVmaAllocator(allocator), a, &mp);
2608 if (err != VK_SUCCESS) {
2609 qWarning("Failed to map image data: %d", err);
2610 continue;
2611 }
2612
2613 for (int layer = 0; layer < QRhi::MAX_LAYERS; ++layer) {
2614 for (int level = 0; level < QRhi::MAX_LEVELS; ++level) {
2615 const QVector<QRhiTextureSubresourceUploadDescription> &srd(u.upload.subresDesc[layer][level]);
2616 if (srd.isEmpty())
2617 continue;
2618 for (const QRhiTextureSubresourceUploadDescription &subresDesc : qAsConst(srd)) {
2619 prepareUploadSubres(utexD, layer, level,
2620 subresDesc, &curOfs, mp, &copyInfos);
2621 }
2622 }
2623 }
2624 vmaUnmapMemory(toVmaAllocator(allocator), a);
2625 vmaFlushAllocation(toVmaAllocator(allocator), a, 0, stagingSize);
2626
2627 trackedImageBarrier(cbD, utexD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
2628 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
2629
2630 QVkCommandBuffer::Command cmd;
2631 cmd.cmd = QVkCommandBuffer::Command::CopyBufferToImage;
2632 cmd.args.copyBufferToImage.src = utexD->stagingBuffers[currentFrameSlot];
2633 cmd.args.copyBufferToImage.dst = utexD->image;
2634 cmd.args.copyBufferToImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2635 cmd.args.copyBufferToImage.count = copyInfos.count();
2636 cmd.args.copyBufferToImage.bufferImageCopyIndex = cbD->pools.bufferImageCopy.count();
2637 cbD->pools.bufferImageCopy.append(copyInfos.constData(), copyInfos.count());
2638 cbD->commands.append(cmd);
2639
2640 // no reuse of staging, this is intentional
2641 QRhiVulkan::DeferredReleaseEntry e;
2642 e.type = QRhiVulkan::DeferredReleaseEntry::StagingBuffer;
2643 e.lastActiveFrameSlot = currentFrameSlot;
2644 e.stagingBuffer.stagingBuffer = utexD->stagingBuffers[currentFrameSlot];
2645 e.stagingBuffer.stagingAllocation = utexD->stagingAllocations[currentFrameSlot];
2646 utexD->stagingBuffers[currentFrameSlot] = VK_NULL_HANDLE;
2647 utexD->stagingAllocations[currentFrameSlot] = nullptr;
2648 releaseQueue.append(e);
2649 QRHI_PROF_F(releaseTextureStagingArea(utexD, currentFrameSlot));
2650
2651 // Similarly to buffers, transitioning away from DST is done later,
2652 // when a renderpass using the texture is encountered.
2653
2654 utexD->lastActiveFrameSlot = currentFrameSlot;
2655 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Copy) {
2656 Q_ASSERT(u.copy.src && u.copy.dst);
2657 if (u.copy.src == u.copy.dst) {
2658 qWarning("Texture copy with matching source and destination is not supported");
2659 continue;
2660 }
2661 QVkTexture *srcD = QRHI_RES(QVkTexture, u.copy.src);
2662 QVkTexture *dstD = QRHI_RES(QVkTexture, u.copy.dst);
2663
2664 VkImageCopy region;
2665 memset(&region, 0, sizeof(region));
2666
2667 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2668 region.srcSubresource.mipLevel = u.copy.desc.sourceLevel();
2669 region.srcSubresource.baseArrayLayer = u.copy.desc.sourceLayer();
2670 region.srcSubresource.layerCount = 1;
2671
2672 region.srcOffset.x = u.copy.desc.sourceTopLeft().x();
2673 region.srcOffset.y = u.copy.desc.sourceTopLeft().y();
2674
2675 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2676 region.dstSubresource.mipLevel = u.copy.desc.destinationLevel();
2677 region.dstSubresource.baseArrayLayer = u.copy.desc.destinationLayer();
2678 region.dstSubresource.layerCount = 1;
2679
2680 region.dstOffset.x = u.copy.desc.destinationTopLeft().x();
2681 region.dstOffset.y = u.copy.desc.destinationTopLeft().y();
2682
2683 const QSize size = u.copy.desc.pixelSize().isEmpty() ? srcD->m_pixelSize : u.copy.desc.pixelSize();
2684 region.extent.width = size.width();
2685 region.extent.height = size.height();
2686 region.extent.depth = 1;
2687
2688 trackedImageBarrier(cbD, srcD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
2689 VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
2690 trackedImageBarrier(cbD, dstD, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
2691 VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
2692
2693 QVkCommandBuffer::Command cmd;
2694 cmd.cmd = QVkCommandBuffer::Command::CopyImage;
2695 cmd.args.copyImage.src = srcD->image;
2696 cmd.args.copyImage.srcLayout = srcD->usageState.layout;
2697 cmd.args.copyImage.dst = dstD->image;
2698 cmd.args.copyImage.dstLayout = dstD->usageState.layout;
2699 cmd.args.copyImage.desc = region;
2700 cbD->commands.append(cmd);
2701
2702 srcD->lastActiveFrameSlot = dstD->lastActiveFrameSlot = currentFrameSlot;
2703 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::Read) {
2704 ActiveReadback aRb;
2705 aRb.activeFrameSlot = currentFrameSlot;
2706 aRb.desc = u.read.rb;
2707 aRb.result = u.read.result;
2708
2709 QVkTexture *texD = QRHI_RES(QVkTexture, u.read.rb.texture());
2710 QVkSwapChain *swapChainD = nullptr;
2711 if (texD) {
2712 if (texD->samples > VK_SAMPLE_COUNT_1_BIT) {
2713 qWarning("Multisample texture cannot be read back");
2714 continue;
2715 }
2716 aRb.pixelSize = u.read.rb.level() > 0 ? q->sizeForMipLevel(u.read.rb.level(), texD->m_pixelSize)
2717 : texD->m_pixelSize;
2718 aRb.format = texD->m_format;
2719 texD->lastActiveFrameSlot = currentFrameSlot;
2720 } else {
2721 Q_ASSERT(currentSwapChain);
2722 swapChainD = QRHI_RES(QVkSwapChain, currentSwapChain);
2723 if (!swapChainD->supportsReadback) {
2724 qWarning("Swapchain does not support readback");
2725 continue;
2726 }
2727 aRb.pixelSize = swapChainD->pixelSize;
2728 aRb.format = colorTextureFormatFromVkFormat(swapChainD->colorFormat, nullptr);
2729 if (aRb.format == QRhiTexture::UnknownFormat)
2730 continue;
2731
2732 // Multisample swapchains need nothing special since resolving
2733 // happens when ending a renderpass.
2734 }
2735 textureFormatInfo(aRb.format, aRb.pixelSize, nullptr, &aRb.bufSize);
2736
2737 // Create a host visible buffer.
2738 VkBufferCreateInfo bufferInfo;
2739 memset(&bufferInfo, 0, sizeof(bufferInfo));
2740 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
2741 bufferInfo.size = aRb.bufSize;
2742 bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT;
2743
2744 VmaAllocationCreateInfo allocInfo;
2745 memset(&allocInfo, 0, sizeof(allocInfo));
2746 allocInfo.usage = VMA_MEMORY_USAGE_GPU_TO_CPU;
2747
2748 VmaAllocation allocation;
2749 VkResult err = vmaCreateBuffer(toVmaAllocator(allocator), &bufferInfo, &allocInfo, &aRb.buf, &allocation, nullptr);
2750 if (err == VK_SUCCESS) {
2751 aRb.bufAlloc = allocation;
2752 QRHI_PROF_F(newReadbackBuffer(quint64(aRb.buf),
2753 texD ? static_cast<QRhiResource *>(texD) : static_cast<QRhiResource *>(swapChainD),
2754 aRb.bufSize));
2755 } else {
2756 qWarning("Failed to create readback buffer of size %u: %d", aRb.bufSize, err);
2757 continue;
2758 }
2759
2760 // Copy from the (optimal and not host visible) image into the buffer.
2761 VkBufferImageCopy copyDesc;
2762 memset(&copyDesc, 0, sizeof(copyDesc));
2763 copyDesc.bufferOffset = 0;
2764 copyDesc.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2765 copyDesc.imageSubresource.mipLevel = u.read.rb.level();
2766 copyDesc.imageSubresource.baseArrayLayer = u.read.rb.layer();
2767 copyDesc.imageSubresource.layerCount = 1;
2768 copyDesc.imageExtent.width = aRb.pixelSize.width();
2769 copyDesc.imageExtent.height = aRb.pixelSize.height();
2770 copyDesc.imageExtent.depth = 1;
2771
2772 if (texD) {
2773 trackedImageBarrier(cbD, texD, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
2774 VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
2775 QVkCommandBuffer::Command cmd;
2776 cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer;
2777 cmd.args.copyImageToBuffer.src = texD->image;
2778 cmd.args.copyImageToBuffer.srcLayout = texD->usageState.layout;
2779 cmd.args.copyImageToBuffer.dst = aRb.buf;
2780 cmd.args.copyImageToBuffer.desc = copyDesc;
2781 cbD->commands.append(cmd);
2782 } else {
2783 // use the swapchain image
2784 QVkSwapChain::ImageResources &imageRes(swapChainD->imageRes[swapChainD->currentImageIndex]);
2785 VkImage image = imageRes.image;
2786 if (imageRes.lastUse != QVkSwapChain::ImageResources::ScImageUseTransferSource) {
2787 if (imageRes.lastUse != QVkSwapChain::ImageResources::ScImageUseRender) {
2788 qWarning("Attempted to read back undefined swapchain image content, "
2789 "results are undefined. (do a render pass first)");
2790 }
2791 subresourceBarrier(cbD, image,
2792 VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
2793 VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT,
2794 VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
2795 0, 1,
2796 0, 1);
2797 imageRes.lastUse = QVkSwapChain::ImageResources::ScImageUseTransferSource;
2798 }
2799
2800 QVkCommandBuffer::Command cmd;
2801 cmd.cmd = QVkCommandBuffer::Command::CopyImageToBuffer;
2802 cmd.args.copyImageToBuffer.src = image;
2803 cmd.args.copyImageToBuffer.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2804 cmd.args.copyImageToBuffer.dst = aRb.buf;
2805 cmd.args.copyImageToBuffer.desc = copyDesc;
2806 cbD->commands.append(cmd);
2807 }
2808
2809 activeReadbacks.append(aRb);
2810 } else if (u.type == QRhiResourceUpdateBatchPrivate::TextureOp::MipGen) {
2811 QVkTexture *utexD = QRHI_RES(QVkTexture, u.mipgen.tex);
2812 Q_ASSERT(utexD->m_flags.testFlag(QRhiTexture::UsedWithGenerateMips));
2813 int w = utexD->m_pixelSize.width();
2814 int h = utexD->m_pixelSize.height();
2815
2816 VkImageLayout origLayout = utexD->usageState.layout;
2817 VkAccessFlags origAccess = utexD->usageState.access;
2818 VkPipelineStageFlags origStage = utexD->usageState.stage;
2819 if (!origStage)
2820 origStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
2821
2822 for (uint level = 1; level < utexD->mipLevelCount; ++level) {
2823 if (level == 1) {
2824 subresourceBarrier(cbD, utexD->image,
2825 origLayout, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
2826 origAccess, VK_ACCESS_TRANSFER_READ_BIT,
2827 origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
2828 u.mipgen.layer, 1,
2829 level - 1, 1);
2830 } else {
2831 subresourceBarrier(cbD, utexD->image,
2832 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
2833 VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT,
2834 VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT,
2835 u.mipgen.layer, 1,
2836 level - 1, 1);
2837 }
2838
2839 subresourceBarrier(cbD, utexD->image,
2840 origLayout, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
2841 origAccess, VK_ACCESS_TRANSFER_WRITE_BIT,
2842 origStage, VK_PIPELINE_STAGE_TRANSFER_BIT,
2843 u.mipgen.layer, 1,
2844 level, 1);
2845
2846 VkImageBlit region;
2847 memset(&region, 0, sizeof(region));
2848
2849 region.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2850 region.srcSubresource.mipLevel = level - 1;
2851 region.srcSubresource.baseArrayLayer = u.mipgen.layer;
2852 region.srcSubresource.layerCount = 1;
2853
2854 region.srcOffsets[1].x = qMax(1, w);
2855 region.srcOffsets[1].y = qMax(1, h);
2856 region.srcOffsets[1].z = 1;
2857
2858 region.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2859 region.dstSubresource.mipLevel = level;
2860 region.dstSubresource.baseArrayLayer = u.mipgen.layer;
2861 region.dstSubresource.layerCount = 1;
2862
2863 region.dstOffsets[1].x = qMax(1, w >> 1);
2864 region.dstOffsets[1].y = qMax(1, h >> 1);
2865 region.dstOffsets[1].z = 1;
2866
2867 QVkCommandBuffer::Command cmd;
2868 cmd.cmd = QVkCommandBuffer::Command::BlitImage;
2869 cmd.args.blitImage.src = utexD->image;
2870 cmd.args.blitImage.srcLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2871 cmd.args.blitImage.dst = utexD->image;
2872 cmd.args.blitImage.dstLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2873 cmd.args.blitImage.filter = VK_FILTER_LINEAR;
2874 cmd.args.blitImage.desc = region;
2875 cbD->commands.append(cmd);
2876
2877 w >>= 1;
2878 h >>= 1;
2879 }
2880
2881 if (utexD->mipLevelCount > 1) {
2882 subresourceBarrier(cbD, utexD->image,
2883 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, origLayout,
2884 VK_ACCESS_TRANSFER_READ_BIT, origAccess,
2885 VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
2886 u.mipgen.layer, 1,
2887 0, utexD->mipLevelCount - 1);
2888 subresourceBarrier(cbD, utexD->image,
2889 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, origLayout,
2890 VK_ACCESS_TRANSFER_WRITE_BIT, origAccess,
2891 VK_PIPELINE_STAGE_TRANSFER_BIT, origStage,
2892 u.mipgen.layer, 1,
2893 utexD->mipLevelCount - 1, 1);
2894 }
2895
2896 utexD->lastActiveFrameSlot = currentFrameSlot;
2897 }
2898 }
2899
2900 ud->free();
2901}
2902
2903void QRhiVulkan::executeBufferHostWritesForCurrentFrame(QVkBuffer *bufD)
2904{
2905 QVector<QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate> &updates(bufD->pendingDynamicUpdates[currentFrameSlot]);
2906 if (updates.isEmpty())
2907 return;
2908
2909 Q_ASSERT(bufD->m_type == QRhiBuffer::Dynamic);
2910 void *p = nullptr;
2911 VmaAllocation a = toVmaAllocation(bufD->allocations[currentFrameSlot]);
2912 // The vmaMap/Unmap are basically a no-op when persistently mapped since it
2913 // refcounts; this is great because we don't need to care if the allocation
2914 // was created as persistently mapped or not.
2915 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
2916 if (err != VK_SUCCESS) {
2917 qWarning("Failed to map buffer: %d", err);
2918 return;
2919 }
2920 int changeBegin = -1;
2921 int changeEnd = -1;
2922 for (const QRhiResourceUpdateBatchPrivate::DynamicBufferUpdate &u : updates) {
2923 Q_ASSERT(bufD == QRHI_RES(QVkBuffer, u.buf));
2924 memcpy(static_cast<char *>(p) + u.offset, u.data.constData(), u.data.size());
2925 if (changeBegin == -1 || u.offset < changeBegin)
2926 changeBegin = u.offset;
2927 if (changeEnd == -1 || u.offset + u.data.size() > changeEnd)
2928 changeEnd = u.offset + u.data.size();
2929 }
2930 vmaUnmapMemory(toVmaAllocator(allocator), a);
2931 if (changeBegin >= 0)
2932 vmaFlushAllocation(toVmaAllocator(allocator), a, changeBegin, changeEnd - changeBegin);
2933
2934 updates.clear();
2935}
2936
2937static void qrhivk_releaseBuffer(const QRhiVulkan::DeferredReleaseEntry &e, void *allocator)
2938{
2939 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
2940 vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.buffers[i], toVmaAllocation(e.buffer.allocations[i]));
2941 vmaDestroyBuffer(toVmaAllocator(allocator), e.buffer.stagingBuffers[i], toVmaAllocation(e.buffer.stagingAllocations[i]));
2942 }
2943}
2944
2945static void qrhivk_releaseRenderBuffer(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
2946{
2947 df->vkDestroyImageView(dev, e.renderBuffer.imageView, nullptr);
2948 df->vkDestroyImage(dev, e.renderBuffer.image, nullptr);
2949 df->vkFreeMemory(dev, e.renderBuffer.memory, nullptr);
2950}
2951
2952static void qrhivk_releaseTexture(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df, void *allocator)
2953{
2954 df->vkDestroyImageView(dev, e.texture.imageView, nullptr);
2955 vmaDestroyImage(toVmaAllocator(allocator), e.texture.image, toVmaAllocation(e.texture.allocation));
2956 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
2957 vmaDestroyBuffer(toVmaAllocator(allocator), e.texture.stagingBuffers[i], toVmaAllocation(e.texture.stagingAllocations[i]));
2958 for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
2959 if (e.texture.extraImageViews[i])
2960 df->vkDestroyImageView(dev, e.texture.extraImageViews[i], nullptr);
2961 }
2962}
2963
2964static void qrhivk_releaseSampler(const QRhiVulkan::DeferredReleaseEntry &e, VkDevice dev, QVulkanDeviceFunctions *df)
2965{
2966 df->vkDestroySampler(dev, e.sampler.sampler, nullptr);
2967}
2968
2969void QRhiVulkan::executeDeferredReleases(bool forced)
2970{
2971 for (int i = releaseQueue.count() - 1; i >= 0; --i) {
2972 const QRhiVulkan::DeferredReleaseEntry &e(releaseQueue[i]);
2973 if (forced || currentFrameSlot == e.lastActiveFrameSlot || e.lastActiveFrameSlot < 0) {
2974 switch (e.type) {
2975 case QRhiVulkan::DeferredReleaseEntry::Pipeline:
2976 df->vkDestroyPipeline(dev, e.pipelineState.pipeline, nullptr);
2977 df->vkDestroyPipelineLayout(dev, e.pipelineState.layout, nullptr);
2978 break;
2979 case QRhiVulkan::DeferredReleaseEntry::ShaderResourceBindings:
2980 df->vkDestroyDescriptorSetLayout(dev, e.shaderResourceBindings.layout, nullptr);
2981 if (e.shaderResourceBindings.poolIndex >= 0) {
2982 descriptorPools[e.shaderResourceBindings.poolIndex].refCount -= 1;
2983 Q_ASSERT(descriptorPools[e.shaderResourceBindings.poolIndex].refCount >= 0);
2984 }
2985 break;
2986 case QRhiVulkan::DeferredReleaseEntry::Buffer:
2987 qrhivk_releaseBuffer(e, allocator);
2988 break;
2989 case QRhiVulkan::DeferredReleaseEntry::RenderBuffer:
2990 qrhivk_releaseRenderBuffer(e, dev, df);
2991 break;
2992 case QRhiVulkan::DeferredReleaseEntry::Texture:
2993 qrhivk_releaseTexture(e, dev, df, allocator);
2994 break;
2995 case QRhiVulkan::DeferredReleaseEntry::Sampler:
2996 qrhivk_releaseSampler(e, dev, df);
2997 break;
2998 case QRhiVulkan::DeferredReleaseEntry::TextureRenderTarget:
2999 df->vkDestroyFramebuffer(dev, e.textureRenderTarget.fb, nullptr);
3000 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
3001 df->vkDestroyImageView(dev, e.textureRenderTarget.rtv[att], nullptr);
3002 df->vkDestroyImageView(dev, e.textureRenderTarget.resrtv[att], nullptr);
3003 }
3004 break;
3005 case QRhiVulkan::DeferredReleaseEntry::RenderPass:
3006 df->vkDestroyRenderPass(dev, e.renderPass.rp, nullptr);
3007 break;
3008 case QRhiVulkan::DeferredReleaseEntry::StagingBuffer:
3009 vmaDestroyBuffer(toVmaAllocator(allocator), e.stagingBuffer.stagingBuffer, toVmaAllocation(e.stagingBuffer.stagingAllocation));
3010 break;
3011 default:
3012 Q_UNREACHABLE();
3013 break;
3014 }
3015 releaseQueue.removeAt(i);
3016 }
3017 }
3018}
3019
3020void QRhiVulkan::finishActiveReadbacks(bool forced)
3021{
3022 QVarLengthArray<std::function<void()>, 4> completedCallbacks;
3023 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
3024
3025 for (int i = activeReadbacks.count() - 1; i >= 0; --i) {
3026 const QRhiVulkan::ActiveReadback &aRb(activeReadbacks[i]);
3027 if (forced || currentFrameSlot == aRb.activeFrameSlot || aRb.activeFrameSlot < 0) {
3028 aRb.result->format = aRb.format;
3029 aRb.result->pixelSize = aRb.pixelSize;
3030 aRb.result->data.resize(aRb.bufSize);
3031 void *p = nullptr;
3032 VmaAllocation a = toVmaAllocation(aRb.bufAlloc);
3033 VkResult err = vmaMapMemory(toVmaAllocator(allocator), a, &p);
3034 if (err != VK_SUCCESS) {
3035 qWarning("Failed to map readback buffer: %d", err);
3036 continue;
3037 }
3038 memcpy(aRb.result->data.data(), p, aRb.bufSize);
3039 vmaUnmapMemory(toVmaAllocator(allocator), a);
3040
3041 vmaDestroyBuffer(toVmaAllocator(allocator), aRb.buf, a);
3042 QRHI_PROF_F(releaseReadbackBuffer(quint64(aRb.buf)));
3043
3044 if (aRb.result->completed)
3045 completedCallbacks.append(aRb.result->completed);
3046
3047 activeReadbacks.removeAt(i);
3048 }
3049 }
3050
3051 for (auto f : completedCallbacks)
3052 f();
3053}
3054
3055static struct {
3056 VkSampleCountFlagBits mask;
3057 int count;
3058} qvk_sampleCounts[] = {
3059 // keep this sorted by 'count'
3060 { VK_SAMPLE_COUNT_1_BIT, 1 },
3061 { VK_SAMPLE_COUNT_2_BIT, 2 },
3062 { VK_SAMPLE_COUNT_4_BIT, 4 },
3063 { VK_SAMPLE_COUNT_8_BIT, 8 },
3064 { VK_SAMPLE_COUNT_16_BIT, 16 },
3065 { VK_SAMPLE_COUNT_32_BIT, 32 },
3066 { VK_SAMPLE_COUNT_64_BIT, 64 }
3067};
3068
3069QVector<int> QRhiVulkan::supportedSampleCounts() const
3070{
3071 const VkPhysicalDeviceLimits *limits = &physDevProperties.limits;
3072 VkSampleCountFlags color = limits->framebufferColorSampleCounts;
3073 VkSampleCountFlags depth = limits->framebufferDepthSampleCounts;
3074 VkSampleCountFlags stencil = limits->framebufferStencilSampleCounts;
3075 QVector<int> result;
3076
3077 for (size_t i = 0; i < sizeof(qvk_sampleCounts) / sizeof(qvk_sampleCounts[0]); ++i) {
3078 if ((color & qvk_sampleCounts[i].mask)
3079 && (depth & qvk_sampleCounts[i].mask)
3080 && (stencil & qvk_sampleCounts[i].mask))
3081 {
3082 result.append(qvk_sampleCounts[i].count);
3083 }
3084 }
3085
3086 return result;
3087}
3088
3089VkSampleCountFlagBits QRhiVulkan::effectiveSampleCount(int sampleCount)
3090{
3091 // Stay compatible with QSurfaceFormat and friends where samples == 0 means the same as 1.
3092 sampleCount = qBound(1, sampleCount, 64);
3093
3094 if (!supportedSampleCounts().contains(sampleCount)) {
3095 qWarning("Attempted to set unsupported sample count %d", sampleCount);
3096 return VK_SAMPLE_COUNT_1_BIT;
3097 }
3098
3099 for (size_t i = 0; i < sizeof(qvk_sampleCounts) / sizeof(qvk_sampleCounts[0]); ++i) {
3100 if (qvk_sampleCounts[i].count == sampleCount)
3101 return qvk_sampleCounts[i].mask;
3102 }
3103
3104 Q_UNREACHABLE();
3105 return VK_SAMPLE_COUNT_1_BIT;
3106}
3107
3108void QRhiVulkan::enqueueTransitionPassResources(QVkCommandBuffer *cbD)
3109{
3110 cbD->passResTrackers.append(QRhiPassResourceTracker());
3111 QVkCommandBuffer::Command cmd;
3112 cmd.cmd = QVkCommandBuffer::Command::TransitionPassResources;
3113 cmd.args.transitionResources.trackerIndex = cbD->passResTrackers.count() - 1;
3114 cbD->commands.append(cmd);
3115 cbD->currentPassResTrackerIndex = cbD->passResTrackers.count() - 1;
3116}
3117
3118void QRhiVulkan::recordCommandBuffer(QVkCommandBuffer *cbD)
3119{
3120 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::NoPass);
3121
3122 for (QVkCommandBuffer::Command &cmd : cbD->commands) {
3123 switch (cmd.cmd) {
3124 case QVkCommandBuffer::Command::CopyBuffer:
3125 df->vkCmdCopyBuffer(cbD->cb, cmd.args.copyBuffer.src, cmd.args.copyBuffer.dst,
3126 1, &cmd.args.copyBuffer.desc);
3127 break;
3128 case QVkCommandBuffer::Command::CopyBufferToImage:
3129 df->vkCmdCopyBufferToImage(cbD->cb, cmd.args.copyBufferToImage.src, cmd.args.copyBufferToImage.dst,
3130 cmd.args.copyBufferToImage.dstLayout,
3131 cmd.args.copyBufferToImage.count,
3132 cbD->pools.bufferImageCopy.constData() + cmd.args.copyBufferToImage.bufferImageCopyIndex);
3133 break;
3134 case QVkCommandBuffer::Command::CopyImage:
3135 df->vkCmdCopyImage(cbD->cb, cmd.args.copyImage.src, cmd.args.copyImage.srcLayout,
3136 cmd.args.copyImage.dst, cmd.args.copyImage.dstLayout,
3137 1, &cmd.args.copyImage.desc);
3138 break;
3139 case QVkCommandBuffer::Command::CopyImageToBuffer:
3140 df->vkCmdCopyImageToBuffer(cbD->cb, cmd.args.copyImageToBuffer.src, cmd.args.copyImageToBuffer.srcLayout,
3141 cmd.args.copyImageToBuffer.dst,
3142 1, &cmd.args.copyImageToBuffer.desc);
3143 break;
3144 case QVkCommandBuffer::Command::ImageBarrier:
3145 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.imageBarrier.srcStageMask, cmd.args.imageBarrier.dstStageMask,
3146 0, 0, nullptr, 0, nullptr,
3147 1, &cmd.args.imageBarrier.desc);
3148 break;
3149 case QVkCommandBuffer::Command::BufferBarrier:
3150 df->vkCmdPipelineBarrier(cbD->cb, cmd.args.bufferBarrier.srcStageMask, cmd.args.bufferBarrier.dstStageMask,
3151 0, 0, nullptr,
3152 1, &cmd.args.bufferBarrier.desc,
3153 0, nullptr);
3154 break;
3155 case QVkCommandBuffer::Command::BlitImage:
3156 df->vkCmdBlitImage(cbD->cb, cmd.args.blitImage.src, cmd.args.blitImage.srcLayout,
3157 cmd.args.blitImage.dst, cmd.args.blitImage.dstLayout,
3158 1, &cmd.args.blitImage.desc,
3159 cmd.args.blitImage.filter);
3160 break;
3161 case QVkCommandBuffer::Command::BeginRenderPass:
3162 cmd.args.beginRenderPass.desc.pClearValues = cbD->pools.clearValue.constData() + cmd.args.beginRenderPass.clearValueIndex;
3163 df->vkCmdBeginRenderPass(cbD->cb, &cmd.args.beginRenderPass.desc, VK_SUBPASS_CONTENTS_INLINE);
3164 break;
3165 case QVkCommandBuffer::Command::EndRenderPass:
3166 df->vkCmdEndRenderPass(cbD->cb);
3167 break;
3168 case QVkCommandBuffer::Command::BindPipeline:
3169 df->vkCmdBindPipeline(cbD->cb, cmd.args.bindPipeline.bindPoint, cmd.args.bindPipeline.pipeline);
3170 break;
3171 case QVkCommandBuffer::Command::BindDescriptorSet:
3172 {
3173 const uint32_t *offsets = nullptr;
3174 if (cmd.args.bindDescriptorSet.dynamicOffsetCount > 0)
3175 offsets = cbD->pools.dynamicOffset.constData() + cmd.args.bindDescriptorSet.dynamicOffsetIndex;
3176 df->vkCmdBindDescriptorSets(cbD->cb, cmd.args.bindDescriptorSet.bindPoint,
3177 cmd.args.bindDescriptorSet.pipelineLayout,
3178 0, 1, &cmd.args.bindDescriptorSet.descSet,
3179 cmd.args.bindDescriptorSet.dynamicOffsetCount,
3180 offsets);
3181 }
3182 break;
3183 case QVkCommandBuffer::Command::BindVertexBuffer:
3184 df->vkCmdBindVertexBuffers(cbD->cb, cmd.args.bindVertexBuffer.startBinding,
3185 cmd.args.bindVertexBuffer.count,
3186 cbD->pools.vertexBuffer.constData() + cmd.args.bindVertexBuffer.vertexBufferIndex,
3187 cbD->pools.vertexBufferOffset.constData() + cmd.args.bindVertexBuffer.vertexBufferOffsetIndex);
3188 break;
3189 case QVkCommandBuffer::Command::BindIndexBuffer:
3190 df->vkCmdBindIndexBuffer(cbD->cb, cmd.args.bindIndexBuffer.buf,
3191 cmd.args.bindIndexBuffer.ofs, cmd.args.bindIndexBuffer.type);
3192 break;
3193 case QVkCommandBuffer::Command::SetViewport:
3194 df->vkCmdSetViewport(cbD->cb, 0, 1, &cmd.args.setViewport.viewport);
3195 break;
3196 case QVkCommandBuffer::Command::SetScissor:
3197 df->vkCmdSetScissor(cbD->cb, 0, 1, &cmd.args.setScissor.scissor);
3198 break;
3199 case QVkCommandBuffer::Command::SetBlendConstants:
3200 df->vkCmdSetBlendConstants(cbD->cb, cmd.args.setBlendConstants.c);
3201 break;
3202 case QVkCommandBuffer::Command::SetStencilRef:
3203 df->vkCmdSetStencilReference(cbD->cb, VK_STENCIL_FRONT_AND_BACK, cmd.args.setStencilRef.ref);
3204 break;
3205 case QVkCommandBuffer::Command::Draw:
3206 df->vkCmdDraw(cbD->cb, cmd.args.draw.vertexCount, cmd.args.draw.instanceCount,
3207 cmd.args.draw.firstVertex, cmd.args.draw.firstInstance);
3208 break;
3209 case QVkCommandBuffer::Command::DrawIndexed:
3210 df->vkCmdDrawIndexed(cbD->cb, cmd.args.drawIndexed.indexCount, cmd.args.drawIndexed.instanceCount,
3211 cmd.args.drawIndexed.firstIndex, cmd.args.drawIndexed.vertexOffset,
3212 cmd.args.drawIndexed.firstInstance);
3213 break;
3214 case QVkCommandBuffer::Command::DebugMarkerBegin:
3215 cmd.args.debugMarkerBegin.marker.pMarkerName =
3216 cbD->pools.debugMarkerName[cmd.args.debugMarkerBegin.markerNameIndex].constData();
3217 vkCmdDebugMarkerBegin(cbD->cb, &cmd.args.debugMarkerBegin.marker);
3218 break;
3219 case QVkCommandBuffer::Command::DebugMarkerEnd:
3220 vkCmdDebugMarkerEnd(cbD->cb);
3221 break;
3222 case QVkCommandBuffer::Command::DebugMarkerInsert:
3223 vkCmdDebugMarkerInsert(cbD->cb, &cmd.args.debugMarkerInsert.marker);
3224 break;
3225 case QVkCommandBuffer::Command::TransitionPassResources:
3226 recordTransitionPassResources(cbD, cbD->passResTrackers[cmd.args.transitionResources.trackerIndex]);
3227 break;
3228 case QVkCommandBuffer::Command::Dispatch:
3229 df->vkCmdDispatch(cbD->cb, cmd.args.dispatch.x, cmd.args.dispatch.y, cmd.args.dispatch.z);
3230 break;
3231 default:
3232 break;
3233 }
3234 }
3235
3236 cbD->resetCommands();
3237}
3238
3239static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::BufferAccess access)
3240{
3241 switch (access) {
3242 case QRhiPassResourceTracker::BufVertexInput:
3243 return VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;
3244 case QRhiPassResourceTracker::BufIndexRead:
3245 return VK_ACCESS_INDEX_READ_BIT;
3246 case QRhiPassResourceTracker::BufUniformRead:
3247 return VK_ACCESS_UNIFORM_READ_BIT;
3248 case QRhiPassResourceTracker::BufStorageLoad:
3249 return VK_ACCESS_SHADER_READ_BIT;
3250 case QRhiPassResourceTracker::BufStorageStore:
3251 return VK_ACCESS_SHADER_WRITE_BIT;
3252 case QRhiPassResourceTracker::BufStorageLoadStore:
3253 return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
3254 default:
3255 Q_UNREACHABLE();
3256 break;
3257 }
3258 return 0;
3259}
3260
3261static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::BufferStage stage)
3262{
3263 switch (stage) {
3264 case QRhiPassResourceTracker::BufVertexInputStage:
3265 return VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
3266 case QRhiPassResourceTracker::BufVertexStage:
3267 return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
3268 case QRhiPassResourceTracker::BufFragmentStage:
3269 return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
3270 case QRhiPassResourceTracker::BufComputeStage:
3271 return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
3272 default:
3273 Q_UNREACHABLE();
3274 break;
3275 }
3276 return 0;
3277}
3278
3279static inline QVkBuffer::UsageState toVkBufferUsageState(QRhiPassResourceTracker::UsageState usage)
3280{
3281 QVkBuffer::UsageState u;
3282 u.access = usage.access;
3283 u.stage = usage.stage;
3284 return u;
3285}
3286
3287static inline VkImageLayout toVkLayout(QRhiPassResourceTracker::TextureAccess access)
3288{
3289 switch (access) {
3290 case QRhiPassResourceTracker::TexSample:
3291 return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
3292 case QRhiPassResourceTracker::TexColorOutput:
3293 return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
3294 case QRhiPassResourceTracker::TexDepthOutput:
3295 return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
3296 case QRhiPassResourceTracker::TexStorageLoad:
3297 Q_FALLTHROUGH();
3298 case QRhiPassResourceTracker::TexStorageStore:
3299 Q_FALLTHROUGH();
3300 case QRhiPassResourceTracker::TexStorageLoadStore:
3301 return VK_IMAGE_LAYOUT_GENERAL;
3302 default:
3303 Q_UNREACHABLE();
3304 break;
3305 }
3306 return VK_IMAGE_LAYOUT_GENERAL;
3307}
3308
3309static inline VkAccessFlags toVkAccess(QRhiPassResourceTracker::TextureAccess access)
3310{
3311 switch (access) {
3312 case QRhiPassResourceTracker::TexSample:
3313 return VK_ACCESS_SHADER_READ_BIT;
3314 case QRhiPassResourceTracker::TexColorOutput:
3315 return VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
3316 case QRhiPassResourceTracker::TexDepthOutput:
3317 return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
3318 case QRhiPassResourceTracker::TexStorageLoad:
3319 return VK_ACCESS_SHADER_READ_BIT;
3320 case QRhiPassResourceTracker::TexStorageStore:
3321 return VK_ACCESS_SHADER_WRITE_BIT;
3322 case QRhiPassResourceTracker::TexStorageLoadStore:
3323 return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
3324 default:
3325 Q_UNREACHABLE();
3326 break;
3327 }
3328 return 0;
3329}
3330
3331static inline VkPipelineStageFlags toVkPipelineStage(QRhiPassResourceTracker::TextureStage stage)
3332{
3333 switch (stage) {
3334 case QRhiPassResourceTracker::TexVertexStage:
3335 return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
3336 case QRhiPassResourceTracker::TexFragmentStage:
3337 return VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
3338 case QRhiPassResourceTracker::TexColorOutputStage:
3339 return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
3340 case QRhiPassResourceTracker::TexDepthOutputStage:
3341 return VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
3342 case QRhiPassResourceTracker::TexComputeStage:
3343 return VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
3344 default:
3345 Q_UNREACHABLE();
3346 break;
3347 }
3348 return 0;
3349}
3350
3351static inline QVkTexture::UsageState toVkTextureUsageState(QRhiPassResourceTracker::UsageState usage)
3352{
3353 QVkTexture::UsageState u;
3354 u.layout = VkImageLayout(usage.layout);
3355 u.access = usage.access;
3356 u.stage = usage.stage;
3357 return u;
3358}
3359
3360void QRhiVulkan::trackedRegisterBuffer(QRhiPassResourceTracker *passResTracker,
3361 QVkBuffer *bufD,
3362 int slot,
3363 QRhiPassResourceTracker::BufferAccess access,
3364 QRhiPassResourceTracker::BufferStage stage)
3365{
3366 QVkBuffer::UsageState &u(bufD->usageState[slot]);
3367 passResTracker->registerBuffer(bufD, slot, &access, &stage, toPassTrackerUsageState(u));
3368 u.access = toVkAccess(access);
3369 u.stage = toVkPipelineStage(stage);
3370}
3371
3372void QRhiVulkan::trackedRegisterTexture(QRhiPassResourceTracker *passResTracker,
3373 QVkTexture *texD,
3374 QRhiPassResourceTracker::TextureAccess access,
3375 QRhiPassResourceTracker::TextureStage stage)
3376{
3377 QVkTexture::UsageState &u(texD->usageState);
3378 passResTracker->registerTexture(texD, &access, &stage, toPassTrackerUsageState(u));
3379 u.layout = toVkLayout(access);
3380 u.access = toVkAccess(access);
3381 u.stage = toVkPipelineStage(stage);
3382}
3383
3384void QRhiVulkan::recordTransitionPassResources(QVkCommandBuffer *cbD, const QRhiPassResourceTracker &tracker)
3385{
3386 if (tracker.isEmpty())
3387 return;
3388
3389 const QVector<QRhiPassResourceTracker::Buffer> *buffers = tracker.buffers();
3390 for (const QRhiPassResourceTracker::Buffer &b : *buffers) {
3391 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b.buf);
3392 VkAccessFlags access = toVkAccess(b.access);
3393 VkPipelineStageFlags stage = toVkPipelineStage(b.stage);
3394 QVkBuffer::UsageState s = toVkBufferUsageState(b.stateAtPassBegin);
3395 if (!s.stage)
3396 continue;
3397 if (s.access == access && s.stage == stage) {
3398 if (!accessIsWrite(access))
3399 continue;
3400 }
3401 VkBufferMemoryBarrier bufMemBarrier;
3402 memset(&bufMemBarrier, 0, sizeof(bufMemBarrier));
3403 bufMemBarrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
3404 bufMemBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3405 bufMemBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
3406 bufMemBarrier.srcAccessMask = s.access;
3407 bufMemBarrier.dstAccessMask = access;
3408 bufMemBarrier.buffer = bufD->buffers[b.slot];
3409 bufMemBarrier.size = VK_WHOLE_SIZE;
3410 df->vkCmdPipelineBarrier(cbD->cb, s.stage, stage, 0,
3411 0, nullptr,
3412 1, &bufMemBarrier,
3413 0, nullptr);
3414 }
3415
3416 const QVector<QRhiPassResourceTracker::Texture> *textures = tracker.textures();
3417 for (const QRhiPassResourceTracker::Texture &t : *textures) {
3418 QVkTexture *texD = QRHI_RES(QVkTexture, t.tex);
3419 VkImageLayout layout = toVkLayout(t.access);
3420 VkAccessFlags access = toVkAccess(t.access);
3421 VkPipelineStageFlags stage = toVkPipelineStage(t.stage);
3422 QVkTexture::UsageState s = toVkTextureUsageState(t.stateAtPassBegin);
3423 if (s.access == access && s.stage == stage && s.layout == layout) {
3424 if (!accessIsWrite(access))
3425 continue;
3426 }
3427 VkImageMemoryBarrier barrier;
3428 memset(&barrier, 0, sizeof(barrier));
3429 barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
3430 barrier.subresourceRange.aspectMask = !isDepthTextureFormat(texD->m_format)
3431 ? VK_IMAGE_ASPECT_COLOR_BIT : VK_IMAGE_ASPECT_DEPTH_BIT;
3432 barrier.subresourceRange.baseMipLevel = 0;
3433 barrier.subresourceRange.levelCount = VK_REMAINING_MIP_LEVELS;
3434 barrier.subresourceRange.baseArrayLayer = 0;
3435 barrier.subresourceRange.layerCount = VK_REMAINING_ARRAY_LAYERS;
3436 barrier.oldLayout = s.layout; // new textures have this set to PREINITIALIZED
3437 barrier.newLayout = layout;
3438 barrier.srcAccessMask = s.access; // may be 0 but that's fine
3439 barrier.dstAccessMask = access;
3440 barrier.image = texD->image;
3441 VkPipelineStageFlags srcStage = s.stage;
3442 // stage mask cannot be 0
3443 if (!srcStage)
3444 srcStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
3445 df->vkCmdPipelineBarrier(cbD->cb, srcStage, stage, 0,
3446 0, nullptr,
3447 0, nullptr,
3448 1, &barrier);
3449 }
3450}
3451
3452QRhiSwapChain *QRhiVulkan::createSwapChain()
3453{
3454 return new QVkSwapChain(this);
3455}
3456
3457QRhiBuffer *QRhiVulkan::createBuffer(QRhiBuffer::Type type, QRhiBuffer::UsageFlags usage, int size)
3458{
3459 return new QVkBuffer(this, type, usage, size);
3460}
3461
3462int QRhiVulkan::ubufAlignment() const
3463{
3464 return ubufAlign; // typically 256 (bytes)
3465}
3466
3467bool QRhiVulkan::isYUpInFramebuffer() const
3468{
3469 return false;
3470}
3471
3472bool QRhiVulkan::isYUpInNDC() const
3473{
3474 return false;
3475}
3476
3477bool QRhiVulkan::isClipDepthZeroToOne() const
3478{
3479 return true;
3480}
3481
3482QMatrix4x4 QRhiVulkan::clipSpaceCorrMatrix() const
3483{
3484 // See https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/
3485
3486 static QMatrix4x4 m;
3487 if (m.isIdentity()) {
3488 // NB the ctor takes row-major
3489 m = QMatrix4x4(1.0f, 0.0f, 0.0f, 0.0f,
3490 0.0f, -1.0f, 0.0f, 0.0f,
3491 0.0f, 0.0f, 0.5f, 0.5f,
3492 0.0f, 0.0f, 0.0f, 1.0f);
3493 }
3494 return m;
3495}
3496
3497bool QRhiVulkan::isTextureFormatSupported(QRhiTexture::Format format, QRhiTexture::Flags flags) const
3498{
3499 // Note that with some SDKs the validation layer gives an odd warning about
3500 // BC not being supported, even when our check here succeeds. Not much we
3501 // can do about that.
3502 if (format >= QRhiTexture::BC1 && format <= QRhiTexture::BC7) {
3503 if (!physDevFeatures.textureCompressionBC)
3504 return false;
3505 }
3506
3507 if (format >= QRhiTexture::ETC2_RGB8 && format <= QRhiTexture::ETC2_RGBA8) {
3508 if (!physDevFeatures.textureCompressionETC2)
3509 return false;
3510 }
3511
3512 if (format >= QRhiTexture::ASTC_4x4 && format <= QRhiTexture::ASTC_12x12) {
3513 if (!physDevFeatures.textureCompressionASTC_LDR)
3514 return false;
3515 }
3516
3517 VkFormat vkformat = toVkTextureFormat(format, flags);
3518 VkFormatProperties props;
3519 f->vkGetPhysicalDeviceFormatProperties(physDev, vkformat, &props);
3520 return (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) != 0;
3521}
3522
3523bool QRhiVulkan::isFeatureSupported(QRhi::Feature feature) const
3524{
3525 switch (feature) {
3526 case QRhi::MultisampleTexture:
3527 return true;
3528 case QRhi::MultisampleRenderBuffer:
3529 return true;
3530 case QRhi::DebugMarkers:
3531 return debugMarkersAvailable;
3532 case QRhi::Timestamps:
3533 return timestampValidBits != 0;
3534 case QRhi::Instancing:
3535 return true;
3536 case QRhi::CustomInstanceStepRate:
3537 return vertexAttribDivisorAvailable;
3538 case QRhi::PrimitiveRestart:
3539 return true;
3540 case QRhi::NonDynamicUniformBuffers:
3541 return true;
3542 case QRhi::NonFourAlignedEffectiveIndexBufferOffset:
3543 return true;
3544 case QRhi::NPOTTextureRepeat:
3545 return true;
3546 case QRhi::RedOrAlpha8IsRed:
3547 return true;
3548 case QRhi::ElementIndexUint:
3549 return true;
3550 case QRhi::Compute:
3551 return hasCompute;
3552 case QRhi::WideLines:
3553 return hasWideLines;
3554 case QRhi::VertexShaderPointSize:
3555 return true;
3556 case QRhi::BaseVertex:
3557 return true;
3558 case QRhi::BaseInstance:
3559 return true;
3560 default:
3561 Q_UNREACHABLE();
3562 return false;
3563 }
3564}
3565
3566int QRhiVulkan::resourceLimit(QRhi::ResourceLimit limit) const
3567{
3568 switch (limit) {
3569 case QRhi::TextureSizeMin:
3570 return 1;
3571 case QRhi::TextureSizeMax:
3572 return physDevProperties.limits.maxImageDimension2D;
3573 case QRhi::MaxColorAttachments:
3574 return physDevProperties.limits.maxColorAttachments;
3575 case QRhi::FramesInFlight:
3576 return QVK_FRAMES_IN_FLIGHT;
3577 default:
3578 Q_UNREACHABLE();
3579 return 0;
3580 }
3581}
3582
3583const QRhiNativeHandles *QRhiVulkan::nativeHandles()
3584{
3585 return &nativeHandlesStruct;
3586}
3587
3588void QRhiVulkan::sendVMemStatsToProfiler()
3589{
3590 QRhiProfilerPrivate *rhiP = profilerPrivateOrNull();
3591 if (!rhiP)
3592 return;
3593
3594 VmaStats stats;
3595 vmaCalculateStats(toVmaAllocator(allocator), &stats);
3596 QRHI_PROF_F(vmemStat(stats.total.blockCount, stats.total.allocationCount,
3597 stats.total.usedBytes, stats.total.unusedBytes));
3598}
3599
3600void QRhiVulkan::makeThreadLocalNativeContextCurrent()
3601{
3602 // nothing to do here
3603}
3604
3605QRhiRenderBuffer *QRhiVulkan::createRenderBuffer(QRhiRenderBuffer::Type type, const QSize &pixelSize,
3606 int sampleCount, QRhiRenderBuffer::Flags flags)
3607{
3608 return new QVkRenderBuffer(this, type, pixelSize, sampleCount, flags);
3609}
3610
3611QRhiTexture *QRhiVulkan::createTexture(QRhiTexture::Format format, const QSize &pixelSize,
3612 int sampleCount, QRhiTexture::Flags flags)
3613{
3614 return new QVkTexture(this, format, pixelSize, sampleCount, flags);
3615}
3616
3617QRhiSampler *QRhiVulkan::createSampler(QRhiSampler::Filter magFilter, QRhiSampler::Filter minFilter,
3618 QRhiSampler::Filter mipmapMode,
3619 QRhiSampler::AddressMode u, QRhiSampler::AddressMode v)
3620{
3621 return new QVkSampler(this, magFilter, minFilter, mipmapMode, u, v);
3622}
3623
3624QRhiTextureRenderTarget *QRhiVulkan::createTextureRenderTarget(const QRhiTextureRenderTargetDescription &desc,
3625 QRhiTextureRenderTarget::Flags flags)
3626{
3627 return new QVkTextureRenderTarget(this, desc, flags);
3628}
3629
3630QRhiGraphicsPipeline *QRhiVulkan::createGraphicsPipeline()
3631{
3632 return new QVkGraphicsPipeline(this);
3633}
3634
3635QRhiComputePipeline *QRhiVulkan::createComputePipeline()
3636{
3637 return new QVkComputePipeline(this);
3638}
3639
3640QRhiShaderResourceBindings *QRhiVulkan::createShaderResourceBindings()
3641{
3642 return new QVkShaderResourceBindings(this);
3643}
3644
3645void QRhiVulkan::setGraphicsPipeline(QRhiCommandBuffer *cb, QRhiGraphicsPipeline *ps)
3646{
3647 QVkGraphicsPipeline *psD = QRHI_RES(QVkGraphicsPipeline, ps);
3648 Q_ASSERT(psD->pipeline);
3649 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3650 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
3651
3652 if (cbD->currentGraphicsPipeline != ps || cbD->currentPipelineGeneration != psD->generation) {
3653 QVkCommandBuffer::Command cmd;
3654 cmd.cmd = QVkCommandBuffer::Command::BindPipeline;
3655 cmd.args.bindPipeline.bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
3656 cmd.args.bindPipeline.pipeline = psD->pipeline;
3657 cbD->commands.append(cmd);
3658
3659 cbD->currentGraphicsPipeline = ps;
3660 cbD->currentComputePipeline = nullptr;
3661 cbD->currentPipelineGeneration = psD->generation;
3662 }
3663
3664 psD->lastActiveFrameSlot = currentFrameSlot;
3665}
3666
3667void QRhiVulkan::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBindings *srb,
3668 int dynamicOffsetCount,
3669 const QRhiCommandBuffer::DynamicOffset *dynamicOffsets)
3670{
3671 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3672 Q_ASSERT(cbD->recordingPass != QVkCommandBuffer::NoPass);
3673 QVkGraphicsPipeline *gfxPsD = QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline);
3674 QVkComputePipeline *compPsD = QRHI_RES(QVkComputePipeline, cbD->currentComputePipeline);
3675
3676 if (!srb) {
3677 if (gfxPsD)
3678 srb = gfxPsD->m_shaderResourceBindings;
3679 else
3680 srb = compPsD->m_shaderResourceBindings;
3681 }
3682
3683 QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, srb);
3684 bool hasSlottedResourceInSrb = false;
3685 bool hasDynamicOffsetInSrb = false;
3686
3687 for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
3688 const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
3689 switch (b->type) {
3690 case QRhiShaderResourceBinding::UniformBuffer:
3691 if (QRHI_RES(QVkBuffer, b->u.ubuf.buf)->m_type == QRhiBuffer::Dynamic)
3692 hasSlottedResourceInSrb = true;
3693 if (b->u.ubuf.hasDynamicOffset)
3694 hasDynamicOffsetInSrb = true;
3695 break;
3696 default:
3697 break;
3698 }
3699 }
3700
3701 const int descSetIdx = hasSlottedResourceInSrb ? currentFrameSlot : 0;
3702 bool rewriteDescSet = false;
3703
3704 // Do host writes and mark referenced shader resources as in-use.
3705 // Also prepare to ensure the descriptor set we are going to bind refers to up-to-date Vk objects.
3706 for (int i = 0, ie = srbD->sortedBindings.count(); i != ie; ++i) {
3707 const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&srbD->sortedBindings[i]);
3708 QVkShaderResourceBindings::BoundResourceData &bd(srbD->boundResourceData[descSetIdx][i]);
3709 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
3710 switch (b->type) {
3711 case QRhiShaderResourceBinding::UniformBuffer:
3712 {
3713 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.ubuf.buf);
3714 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::UniformBuffer));
3715
3716 if (bufD->m_type == QRhiBuffer::Dynamic)
3717 executeBufferHostWritesForCurrentFrame(bufD);
3718
3719 bufD->lastActiveFrameSlot = currentFrameSlot;
3720 trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
3721 QRhiPassResourceTracker::BufUniformRead,
3722 QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
3723
3724 // Check both the "local" id (the generation counter) and the
3725 // global id. The latter is relevant when a newly allocated
3726 // QRhiResource ends up with the same pointer as a previous one.
3727 // (and that previous one could have been in an srb...)
3728 if (bufD->generation != bd.ubuf.generation || bufD->m_id != bd.ubuf.id) {
3729 rewriteDescSet = true;
3730 bd.ubuf.id = bufD->m_id;
3731 bd.ubuf.generation = bufD->generation;
3732 }
3733 }
3734 break;
3735 case QRhiShaderResourceBinding::SampledTexture:
3736 {
3737 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.stex.tex);
3738 QVkSampler *samplerD = QRHI_RES(QVkSampler, b->u.stex.sampler);
3739 texD->lastActiveFrameSlot = currentFrameSlot;
3740 samplerD->lastActiveFrameSlot = currentFrameSlot;
3741 trackedRegisterTexture(&passResTracker, texD,
3742 QRhiPassResourceTracker::TexSample,
3743 QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
3744
3745 if (texD->generation != bd.stex.texGeneration
3746 || texD->m_id != bd.stex.texId
3747 || samplerD->generation != bd.stex.samplerGeneration
3748 || samplerD->m_id != bd.stex.samplerId)
3749 {
3750 rewriteDescSet = true;
3751 bd.stex.texId = texD->m_id;
3752 bd.stex.texGeneration = texD->generation;
3753 bd.stex.samplerId = samplerD->m_id;
3754 bd.stex.samplerGeneration = samplerD->generation;
3755 }
3756 }
3757 break;
3758 case QRhiShaderResourceBinding::ImageLoad:
3759 Q_FALLTHROUGH();
3760 case QRhiShaderResourceBinding::ImageStore:
3761 Q_FALLTHROUGH();
3762 case QRhiShaderResourceBinding::ImageLoadStore:
3763 {
3764 QVkTexture *texD = QRHI_RES(QVkTexture, b->u.simage.tex);
3765 Q_ASSERT(texD->m_flags.testFlag(QRhiTexture::UsedWithLoadStore));
3766 texD->lastActiveFrameSlot = currentFrameSlot;
3767 QRhiPassResourceTracker::TextureAccess access;
3768 if (b->type == QRhiShaderResourceBinding::ImageLoad)
3769 access = QRhiPassResourceTracker::TexStorageLoad;
3770 else if (b->type == QRhiShaderResourceBinding::ImageStore)
3771 access = QRhiPassResourceTracker::TexStorageStore;
3772 else
3773 access = QRhiPassResourceTracker::TexStorageLoadStore;
3774 trackedRegisterTexture(&passResTracker, texD,
3775 access,
3776 QRhiPassResourceTracker::toPassTrackerTextureStage(b->stage));
3777
3778 if (texD->generation != bd.simage.generation || texD->m_id != bd.simage.id) {
3779 rewriteDescSet = true;
3780 bd.simage.id = texD->m_id;
3781 bd.simage.generation = texD->generation;
3782 }
3783 }
3784 break;
3785 case QRhiShaderResourceBinding::BufferLoad:
3786 Q_FALLTHROUGH();
3787 case QRhiShaderResourceBinding::BufferStore:
3788 Q_FALLTHROUGH();
3789 case QRhiShaderResourceBinding::BufferLoadStore:
3790 {
3791 QVkBuffer *bufD = QRHI_RES(QVkBuffer, b->u.sbuf.buf);
3792 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::StorageBuffer));
3793
3794 if (bufD->m_type == QRhiBuffer::Dynamic)
3795 executeBufferHostWritesForCurrentFrame(bufD);
3796
3797 bufD->lastActiveFrameSlot = currentFrameSlot;
3798 QRhiPassResourceTracker::BufferAccess access;
3799 if (b->type == QRhiShaderResourceBinding::BufferLoad)
3800 access = QRhiPassResourceTracker::BufStorageLoad;
3801 else if (b->type == QRhiShaderResourceBinding::BufferStore)
3802 access = QRhiPassResourceTracker::BufStorageStore;
3803 else
3804 access = QRhiPassResourceTracker::BufStorageLoadStore;
3805 trackedRegisterBuffer(&passResTracker, bufD, bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0,
3806 access,
3807 QRhiPassResourceTracker::toPassTrackerBufferStage(b->stage));
3808
3809 if (bufD->generation != bd.sbuf.generation || bufD->m_id != bd.sbuf.id) {
3810 rewriteDescSet = true;
3811 bd.sbuf.id = bufD->m_id;
3812 bd.sbuf.generation = bufD->generation;
3813 }
3814 }
3815 break;
3816 default:
3817 Q_UNREACHABLE();
3818 break;
3819 }
3820 }
3821
3822 // write descriptor sets, if needed
3823 if (rewriteDescSet)
3824 updateShaderResourceBindings(srb, descSetIdx);
3825
3826 // make sure the descriptors for the correct slot will get bound.
3827 // also, dynamic offsets always need a bind.
3828 const bool forceRebind = (hasSlottedResourceInSrb && cbD->currentDescSetSlot != descSetIdx) || hasDynamicOffsetInSrb;
3829
3830 const bool srbChanged = gfxPsD ? (cbD->currentGraphicsSrb != srb) : (cbD->currentComputeSrb != srb);
3831
3832 if (forceRebind || rewriteDescSet || srbChanged || cbD->currentSrbGeneration != srbD->generation) {
3833 QVarLengthArray<uint32_t, 4> dynOfs;
3834 if (hasDynamicOffsetInSrb) {
3835 // Filling out dynOfs based on the sorted bindings is important
3836 // because dynOfs has to be ordered based on the binding numbers,
3837 // and neither srb nor dynamicOffsets has any such ordering
3838 // requirement.
3839 for (const QRhiShaderResourceBinding &binding : qAsConst(srbD->sortedBindings)) {
3840 const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
3841 if (b->type == QRhiShaderResourceBinding::UniformBuffer && b->u.ubuf.hasDynamicOffset) {
3842 uint32_t offset = 0;
3843 for (int i = 0; i < dynamicOffsetCount; ++i) {
3844 const QRhiCommandBuffer::DynamicOffset &dynOfs(dynamicOffsets[i]);
3845 if (dynOfs.first == b->binding) {
3846 offset = dynOfs.second;
3847 break;
3848 }
3849 }
3850 dynOfs.append(offset); // use 0 if dynamicOffsets did not contain this binding
3851 }
3852 }
3853 }
3854
3855 QVkCommandBuffer::Command cmd;
3856 cmd.cmd = QVkCommandBuffer::Command::BindDescriptorSet;
3857 cmd.args.bindDescriptorSet.bindPoint = gfxPsD ? VK_PIPELINE_BIND_POINT_GRAPHICS
3858 : VK_PIPELINE_BIND_POINT_COMPUTE;
3859 cmd.args.bindDescriptorSet.pipelineLayout = gfxPsD ? gfxPsD->layout : compPsD->layout;
3860 cmd.args.bindDescriptorSet.descSet = srbD->descSets[descSetIdx];
3861 cmd.args.bindDescriptorSet.dynamicOffsetCount = dynOfs.count();
3862 cmd.args.bindDescriptorSet.dynamicOffsetIndex = cbD->pools.dynamicOffset.count();
3863 cbD->pools.dynamicOffset.append(dynOfs.constData(), dynOfs.count());
3864 cbD->commands.append(cmd);
3865
3866 if (gfxPsD) {
3867 cbD->currentGraphicsSrb = srb;
3868 cbD->currentComputeSrb = nullptr;
3869 } else {
3870 cbD->currentGraphicsSrb = nullptr;
3871 cbD->currentComputeSrb = srb;
3872 }
3873 cbD->currentSrbGeneration = srbD->generation;
3874 cbD->currentDescSetSlot = descSetIdx;
3875 }
3876
3877 srbD->lastActiveFrameSlot = currentFrameSlot;
3878}
3879
3880void QRhiVulkan::setVertexInput(QRhiCommandBuffer *cb,
3881 int startBinding, int bindingCount, const QRhiCommandBuffer::VertexInput *bindings,
3882 QRhiBuffer *indexBuf, quint32 indexOffset, QRhiCommandBuffer::IndexFormat indexFormat)
3883{
3884 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3885 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
3886 QRhiPassResourceTracker &passResTracker(cbD->passResTrackers[cbD->currentPassResTrackerIndex]);
3887
3888 bool needsBindVBuf = false;
3889 for (int i = 0; i < bindingCount; ++i) {
3890 const int inputSlot = startBinding + i;
3891 QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
3892 Q_ASSERT(bufD->m_usage.testFlag(QRhiBuffer::VertexBuffer));
3893 bufD->lastActiveFrameSlot = currentFrameSlot;
3894 if (bufD->m_type == QRhiBuffer::Dynamic)
3895 executeBufferHostWritesForCurrentFrame(bufD);
3896
3897 const VkBuffer vkvertexbuf = bufD->buffers[bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0];
3898 if (cbD->currentVertexBuffers[inputSlot] != vkvertexbuf
3899 || cbD->currentVertexOffsets[inputSlot] != bindings[i].second)
3900 {
3901 needsBindVBuf = true;
3902 cbD->currentVertexBuffers[inputSlot] = vkvertexbuf;
3903 cbD->currentVertexOffsets[inputSlot] = bindings[i].second;
3904 }
3905 }
3906
3907 if (needsBindVBuf) {
3908 QVarLengthArray<VkBuffer, 4> bufs;
3909 QVarLengthArray<VkDeviceSize, 4> ofs;
3910 for (int i = 0; i < bindingCount; ++i) {
3911 QVkBuffer *bufD = QRHI_RES(QVkBuffer, bindings[i].first);
3912 const int slot = bufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
3913 bufs.append(bufD->buffers[slot]);
3914 ofs.append(bindings[i].second);
3915 trackedRegisterBuffer(&passResTracker, bufD, slot,
3916 QRhiPassResourceTracker::BufVertexInput,
3917 QRhiPassResourceTracker::BufVertexInputStage);
3918 }
3919
3920 QVkCommandBuffer::Command cmd;
3921 cmd.cmd = QVkCommandBuffer::Command::BindVertexBuffer;
3922 cmd.args.bindVertexBuffer.startBinding = startBinding;
3923 cmd.args.bindVertexBuffer.count = bufs.count();
3924 cmd.args.bindVertexBuffer.vertexBufferIndex = cbD->pools.vertexBuffer.count();
3925 cbD->pools.vertexBuffer.append(bufs.constData(), bufs.count());
3926 cmd.args.bindVertexBuffer.vertexBufferOffsetIndex = cbD->pools.vertexBufferOffset.count();
3927 cbD->pools.vertexBufferOffset.append(ofs.constData(), ofs.count());
3928 cbD->commands.append(cmd);
3929 }
3930
3931 if (indexBuf) {
3932 QVkBuffer *ibufD = QRHI_RES(QVkBuffer, indexBuf);
3933 Q_ASSERT(ibufD->m_usage.testFlag(QRhiBuffer::IndexBuffer));
3934 ibufD->lastActiveFrameSlot = currentFrameSlot;
3935 if (ibufD->m_type == QRhiBuffer::Dynamic)
3936 executeBufferHostWritesForCurrentFrame(ibufD);
3937
3938 const int slot = ibufD->m_type == QRhiBuffer::Dynamic ? currentFrameSlot : 0;
3939 const VkBuffer vkindexbuf = ibufD->buffers[slot];
3940 const VkIndexType type = indexFormat == QRhiCommandBuffer::IndexUInt16 ? VK_INDEX_TYPE_UINT16
3941 : VK_INDEX_TYPE_UINT32;
3942
3943 if (cbD->currentIndexBuffer != vkindexbuf
3944 || cbD->currentIndexOffset != indexOffset
3945 || cbD->currentIndexFormat != type)
3946 {
3947 cbD->currentIndexBuffer = vkindexbuf;
3948 cbD->currentIndexOffset = indexOffset;
3949 cbD->currentIndexFormat = type;
3950
3951 QVkCommandBuffer::Command cmd;
3952 cmd.cmd = QVkCommandBuffer::Command::BindIndexBuffer;
3953 cmd.args.bindIndexBuffer.buf = vkindexbuf;
3954 cmd.args.bindIndexBuffer.ofs = indexOffset;
3955 cmd.args.bindIndexBuffer.type = type;
3956 cbD->commands.append(cmd);
3957
3958 trackedRegisterBuffer(&passResTracker, ibufD, slot,
3959 QRhiPassResourceTracker::BufIndexRead,
3960 QRhiPassResourceTracker::BufVertexInputStage);
3961 }
3962 }
3963}
3964
3965void QRhiVulkan::setViewport(QRhiCommandBuffer *cb, const QRhiViewport &viewport)
3966{
3967 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
3968 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
3969 const QSize outputSize = cbD->currentTarget->pixelSize();
3970
3971 // x,y is top-left in VkViewport but bottom-left in QRhiViewport
3972 float x, y, w, h;
3973 if (!qrhi_toTopLeftRenderTargetRect(outputSize, viewport.viewport(), &x, &y, &w, &h))
3974 return;
3975
3976 QVkCommandBuffer::Command cmd;
3977 cmd.cmd = QVkCommandBuffer::Command::SetViewport;
3978 VkViewport *vp = &cmd.args.setViewport.viewport;
3979 vp->x = x;
3980 vp->y = y;
3981 vp->width = w;
3982 vp->height = h;
3983 vp->minDepth = viewport.minDepth();
3984 vp->maxDepth = viewport.maxDepth();
3985 cbD->commands.append(cmd);
3986
3987 if (!QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor)) {
3988 cmd.cmd = QVkCommandBuffer::Command::SetScissor;
3989 VkRect2D *s = &cmd.args.setScissor.scissor;
3990 s->offset.x = x;
3991 s->offset.y = y;
3992 s->extent.width = w;
3993 s->extent.height = h;
3994 cbD->commands.append(cmd);
3995 }
3996}
3997
3998void QRhiVulkan::setScissor(QRhiCommandBuffer *cb, const QRhiScissor &scissor)
3999{
4000 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
4001 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
4002 Q_ASSERT(QRHI_RES(QVkGraphicsPipeline, cbD->currentGraphicsPipeline)->m_flags.testFlag(QRhiGraphicsPipeline::UsesScissor));
4003 const QSize outputSize = cbD->currentTarget->pixelSize();
4004
4005 // x,y is top-left in VkRect2D but bottom-left in QRhiScissor
4006 int x, y, w, h;
4007 if (!qrhi_toTopLeftRenderTargetRect(outputSize, scissor.scissor(), &x, &y, &w, &h))
4008 return;
4009
4010 QVkCommandBuffer::Command cmd;
4011 cmd.cmd = QVkCommandBuffer::Command::SetScissor;
4012 VkRect2D *s = &cmd.args.setScissor.scissor;
4013 s->offset.x = x;
4014 s->offset.y = y;
4015 s->extent.width = w;
4016 s->extent.height = h;
4017 cbD->commands.append(cmd);
4018}
4019
4020void QRhiVulkan::setBlendConstants(QRhiCommandBuffer *cb, const QColor &c)
4021{
4022 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
4023 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
4024
4025 QVkCommandBuffer::Command cmd;
4026 cmd.cmd = QVkCommandBuffer::Command::SetBlendConstants;
4027 cmd.args.setBlendConstants.c[0] = c.redF();
4028 cmd.args.setBlendConstants.c[1] = c.greenF();
4029 cmd.args.setBlendConstants.c[2] = c.blueF();
4030 cmd.args.setBlendConstants.c[3] = c.alphaF();
4031 cbD->commands.append(cmd);
4032}
4033
4034void QRhiVulkan::setStencilRef(QRhiCommandBuffer *cb, quint32 refValue)
4035{
4036 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
4037 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
4038
4039 QVkCommandBuffer::Command cmd;
4040 cmd.cmd = QVkCommandBuffer::Command::SetStencilRef;
4041 cmd.args.setStencilRef.ref = refValue;
4042 cbD->commands.append(cmd);
4043}
4044
4045void QRhiVulkan::draw(QRhiCommandBuffer *cb, quint32 vertexCount,
4046 quint32 instanceCount, quint32 firstVertex, quint32 firstInstance)
4047{
4048 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
4049 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
4050
4051 QVkCommandBuffer::Command cmd;
4052 cmd.cmd = QVkCommandBuffer::Command::Draw;
4053 cmd.args.draw.vertexCount = vertexCount;
4054 cmd.args.draw.instanceCount = instanceCount;
4055 cmd.args.draw.firstVertex = firstVertex;
4056 cmd.args.draw.firstInstance = firstInstance;
4057 cbD->commands.append(cmd);
4058}
4059
4060void QRhiVulkan::drawIndexed(QRhiCommandBuffer *cb, quint32 indexCount,
4061 quint32 instanceCount, quint32 firstIndex, qint32 vertexOffset, quint32 firstInstance)
4062{
4063 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
4064 Q_ASSERT(cbD->recordingPass == QVkCommandBuffer::RenderPass);
4065
4066 QVkCommandBuffer::Command cmd;
4067 cmd.cmd = QVkCommandBuffer::Command::DrawIndexed;
4068 cmd.args.drawIndexed.indexCount = indexCount;
4069 cmd.args.drawIndexed.instanceCount = instanceCount;
4070 cmd.args.drawIndexed.firstIndex = firstIndex;
4071 cmd.args.drawIndexed.vertexOffset = vertexOffset;
4072 cmd.args.drawIndexed.firstInstance = firstInstance;
4073 cbD->commands.append(cmd);
4074}
4075
4076void QRhiVulkan::debugMarkBegin(QRhiCommandBuffer *cb, const QByteArray &name)
4077{
4078 if (!debugMarkers || !debugMarkersAvailable)
4079 return;
4080
4081 VkDebugMarkerMarkerInfoEXT marker;
4082 memset(&marker, 0, sizeof(marker));
4083 marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
4084
4085 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
4086 QVkCommandBuffer::Command cmd;
4087 cmd.cmd = QVkCommandBuffer::Command::DebugMarkerBegin;
4088 cmd.args.debugMarkerBegin.marker = marker;
4089 cmd.args.debugMarkerBegin.markerNameIndex = cbD->pools.debugMarkerName.count();
4090 cbD->pools.debugMarkerName.append(name);
4091 cbD->commands.append(cmd);
4092}
4093
4094void QRhiVulkan::debugMarkEnd(QRhiCommandBuffer *cb)
4095{
4096 if (!debugMarkers || !debugMarkersAvailable)
4097 return;
4098
4099 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
4100 QVkCommandBuffer::Command cmd;
4101 cmd.cmd = QVkCommandBuffer::Command::DebugMarkerEnd;
4102 cbD->commands.append(cmd);
4103}
4104
4105void QRhiVulkan::debugMarkMsg(QRhiCommandBuffer *cb, const QByteArray &msg)
4106{
4107 if (!debugMarkers || !debugMarkersAvailable)
4108 return;
4109
4110 VkDebugMarkerMarkerInfoEXT marker;
4111 memset(&marker, 0, sizeof(marker));
4112 marker.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
4113 marker.pMarkerName = msg.constData();
4114
4115 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
4116 QVkCommandBuffer::Command cmd;
4117 cmd.cmd = QVkCommandBuffer::Command::DebugMarkerInsert;
4118 cmd.args.debugMarkerInsert.marker = marker;
4119 cbD->commands.append(cmd);
4120}
4121
4122const QRhiNativeHandles *QRhiVulkan::nativeHandles(QRhiCommandBuffer *cb)
4123{
4124 return QRHI_RES(QVkCommandBuffer, cb)->nativeHandles();
4125}
4126
4127void QRhiVulkan::beginExternal(QRhiCommandBuffer *cb)
4128{
4129 Q_UNUSED(cb);
4130}
4131
4132void QRhiVulkan::endExternal(QRhiCommandBuffer *cb)
4133{
4134 QVkCommandBuffer *cbD = QRHI_RES(QVkCommandBuffer, cb);
4135 cbD->resetCachedState();
4136}
4137
4138void QRhiVulkan::setObjectName(uint64_t object, VkDebugReportObjectTypeEXT type, const QByteArray &name, int slot)
4139{
4140 if (!debugMarkers || !debugMarkersAvailable || name.isEmpty())
4141 return;
4142
4143 VkDebugMarkerObjectNameInfoEXT nameInfo;
4144 memset(&nameInfo, 0, sizeof(nameInfo));
4145 nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
4146 nameInfo.objectType = type;
4147 nameInfo.object = object;
4148 QByteArray decoratedName = name;
4149 if (slot >= 0) {
4150 decoratedName += '/';
4151 decoratedName += QByteArray::number(slot);
4152 }
4153 nameInfo.pObjectName = decoratedName.constData();
4154 vkDebugMarkerSetObjectName(dev, &nameInfo);
4155}
4156
4157static inline VkBufferUsageFlagBits toVkBufferUsage(QRhiBuffer::UsageFlags usage)
4158{
4159 int u = 0;
4160 if (usage.testFlag(QRhiBuffer::VertexBuffer))
4161 u |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
4162 if (usage.testFlag(QRhiBuffer::IndexBuffer))
4163 u |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
4164 if (usage.testFlag(QRhiBuffer::UniformBuffer))
4165 u |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
4166 if (usage.testFlag(QRhiBuffer::StorageBuffer))
4167 u |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
4168 return VkBufferUsageFlagBits(u);
4169}
4170
4171static inline VkFilter toVkFilter(QRhiSampler::Filter f)
4172{
4173 switch (f) {
4174 case QRhiSampler::Nearest:
4175 return VK_FILTER_NEAREST;
4176 case QRhiSampler::Linear:
4177 return VK_FILTER_LINEAR;
4178 default:
4179 Q_UNREACHABLE();
4180 return VK_FILTER_NEAREST;
4181 }
4182}
4183
4184static inline VkSamplerMipmapMode toVkMipmapMode(QRhiSampler::Filter f)
4185{
4186 switch (f) {
4187 case QRhiSampler::None:
4188 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
4189 case QRhiSampler::Nearest:
4190 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
4191 case QRhiSampler::Linear:
4192 return VK_SAMPLER_MIPMAP_MODE_LINEAR;
4193 default:
4194 Q_UNREACHABLE();
4195 return VK_SAMPLER_MIPMAP_MODE_NEAREST;
4196 }
4197}
4198
4199static inline VkSamplerAddressMode toVkAddressMode(QRhiSampler::AddressMode m)
4200{
4201 switch (m) {
4202 case QRhiSampler::Repeat:
4203 return VK_SAMPLER_ADDRESS_MODE_REPEAT;
4204 case QRhiSampler::ClampToEdge:
4205 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
4206 case QRhiSampler::Border:
4207 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;
4208 case QRhiSampler::Mirror:
4209 return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
4210 case QRhiSampler::MirrorOnce:
4211 return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
4212 default:
4213 Q_UNREACHABLE();
4214 return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
4215 }
4216}
4217
4218static inline VkShaderStageFlagBits toVkShaderStage(QRhiShaderStage::Type type)
4219{
4220 switch (type) {
4221 case QRhiShaderStage::Vertex:
4222 return VK_SHADER_STAGE_VERTEX_BIT;
4223 case QRhiShaderStage::Fragment:
4224 return VK_SHADER_STAGE_FRAGMENT_BIT;
4225 case QRhiShaderStage::Compute:
4226 return VK_SHADER_STAGE_COMPUTE_BIT;
4227 default:
4228 Q_UNREACHABLE();
4229 return VK_SHADER_STAGE_VERTEX_BIT;
4230 }
4231}
4232
4233static inline VkFormat toVkAttributeFormat(QRhiVertexInputAttribute::Format format)
4234{
4235 switch (format) {
4236 case QRhiVertexInputAttribute::Float4:
4237 return VK_FORMAT_R32G32B32A32_SFLOAT;
4238 case QRhiVertexInputAttribute::Float3:
4239 return VK_FORMAT_R32G32B32_SFLOAT;
4240 case QRhiVertexInputAttribute::Float2:
4241 return VK_FORMAT_R32G32_SFLOAT;
4242 case QRhiVertexInputAttribute::Float:
4243 return VK_FORMAT_R32_SFLOAT;
4244 case QRhiVertexInputAttribute::UNormByte4:
4245 return VK_FORMAT_R8G8B8A8_UNORM;
4246 case QRhiVertexInputAttribute::UNormByte2:
4247 return VK_FORMAT_R8G8_UNORM;
4248 case QRhiVertexInputAttribute::UNormByte:
4249 return VK_FORMAT_R8_UNORM;
4250 default:
4251 Q_UNREACHABLE();
4252 return VK_FORMAT_R32G32B32A32_SFLOAT;
4253 }
4254}
4255
4256static inline VkPrimitiveTopology toVkTopology(QRhiGraphicsPipeline::Topology t)
4257{
4258 switch (t) {
4259 case QRhiGraphicsPipeline::Triangles:
4260 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
4261 case QRhiGraphicsPipeline::TriangleStrip:
4262 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
4263 case QRhiGraphicsPipeline::Lines:
4264 return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
4265 case QRhiGraphicsPipeline::LineStrip:
4266 return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
4267 case QRhiGraphicsPipeline::Points:
4268 return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
4269 default:
4270 Q_UNREACHABLE();
4271 return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
4272 }
4273}
4274
4275static inline VkCullModeFlags toVkCullMode(QRhiGraphicsPipeline::CullMode c)
4276{
4277 switch (c) {
4278 case QRhiGraphicsPipeline::None:
4279 return VK_CULL_MODE_NONE;
4280 case QRhiGraphicsPipeline::Front:
4281 return VK_CULL_MODE_FRONT_BIT;
4282 case QRhiGraphicsPipeline::Back:
4283 return VK_CULL_MODE_BACK_BIT;
4284 default:
4285 Q_UNREACHABLE();
4286 return VK_CULL_MODE_NONE;
4287 }
4288}
4289
4290static inline VkFrontFace toVkFrontFace(QRhiGraphicsPipeline::FrontFace f)
4291{
4292 switch (f) {
4293 case QRhiGraphicsPipeline::CCW:
4294 return VK_FRONT_FACE_COUNTER_CLOCKWISE;
4295 case QRhiGraphicsPipeline::CW:
4296 return VK_FRONT_FACE_CLOCKWISE;
4297 default:
4298 Q_UNREACHABLE();
4299 return VK_FRONT_FACE_COUNTER_CLOCKWISE;
4300 }
4301}
4302
4303static inline VkColorComponentFlags toVkColorComponents(QRhiGraphicsPipeline::ColorMask c)
4304{
4305 int f = 0;
4306 if (c.testFlag(QRhiGraphicsPipeline::R))
4307 f |= VK_COLOR_COMPONENT_R_BIT;
4308 if (c.testFlag(QRhiGraphicsPipeline::G))
4309 f |= VK_COLOR_COMPONENT_G_BIT;
4310 if (c.testFlag(QRhiGraphicsPipeline::B))
4311 f |= VK_COLOR_COMPONENT_B_BIT;
4312 if (c.testFlag(QRhiGraphicsPipeline::A))
4313 f |= VK_COLOR_COMPONENT_A_BIT;
4314 return VkColorComponentFlags(f);
4315}
4316
4317static inline VkBlendFactor toVkBlendFactor(QRhiGraphicsPipeline::BlendFactor f)
4318{
4319 switch (f) {
4320 case QRhiGraphicsPipeline::Zero:
4321 return VK_BLEND_FACTOR_ZERO;
4322 case QRhiGraphicsPipeline::One:
4323 return VK_BLEND_FACTOR_ONE;
4324 case QRhiGraphicsPipeline::SrcColor:
4325 return VK_BLEND_FACTOR_SRC_COLOR;
4326 case QRhiGraphicsPipeline::OneMinusSrcColor:
4327 return VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
4328 case QRhiGraphicsPipeline::DstColor:
4329 return VK_BLEND_FACTOR_DST_COLOR;
4330 case QRhiGraphicsPipeline::OneMinusDstColor:
4331 return VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
4332 case QRhiGraphicsPipeline::SrcAlpha:
4333 return VK_BLEND_FACTOR_SRC_ALPHA;
4334 case QRhiGraphicsPipeline::OneMinusSrcAlpha:
4335 return VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
4336 case QRhiGraphicsPipeline::DstAlpha:
4337 return VK_BLEND_FACTOR_DST_ALPHA;
4338 case QRhiGraphicsPipeline::OneMinusDstAlpha:
4339 return VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
4340 case QRhiGraphicsPipeline::ConstantColor:
4341 return VK_BLEND_FACTOR_CONSTANT_COLOR;
4342 case QRhiGraphicsPipeline::OneMinusConstantColor:
4343 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR;
4344 case QRhiGraphicsPipeline::ConstantAlpha:
4345 return VK_BLEND_FACTOR_CONSTANT_ALPHA;
4346 case QRhiGraphicsPipeline::OneMinusConstantAlpha:
4347 return VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
4348 case QRhiGraphicsPipeline::SrcAlphaSaturate:
4349 return VK_BLEND_FACTOR_SRC_ALPHA_SATURATE;
4350 case QRhiGraphicsPipeline::Src1Color:
4351 return VK_BLEND_FACTOR_SRC1_COLOR;
4352 case QRhiGraphicsPipeline::OneMinusSrc1Color:
4353 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR;
4354 case QRhiGraphicsPipeline::Src1Alpha:
4355 return VK_BLEND_FACTOR_SRC1_ALPHA;
4356 case QRhiGraphicsPipeline::OneMinusSrc1Alpha:
4357 return VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
4358 default:
4359 Q_UNREACHABLE();
4360 return VK_BLEND_FACTOR_ZERO;
4361 }
4362}
4363
4364static inline VkBlendOp toVkBlendOp(QRhiGraphicsPipeline::BlendOp op)
4365{
4366 switch (op) {
4367 case QRhiGraphicsPipeline::Add:
4368 return VK_BLEND_OP_ADD;
4369 case QRhiGraphicsPipeline::Subtract:
4370 return VK_BLEND_OP_SUBTRACT;
4371 case QRhiGraphicsPipeline::ReverseSubtract:
4372 return VK_BLEND_OP_REVERSE_SUBTRACT;
4373 case QRhiGraphicsPipeline::Min:
4374 return VK_BLEND_OP_MIN;
4375 case QRhiGraphicsPipeline::Max:
4376 return VK_BLEND_OP_MAX;
4377 default:
4378 Q_UNREACHABLE();
4379 return VK_BLEND_OP_ADD;
4380 }
4381}
4382
4383static inline VkCompareOp toVkCompareOp(QRhiGraphicsPipeline::CompareOp op)
4384{
4385 switch (op) {
4386 case QRhiGraphicsPipeline::Never:
4387 return VK_COMPARE_OP_NEVER;
4388 case QRhiGraphicsPipeline::Less:
4389 return VK_COMPARE_OP_LESS;
4390 case QRhiGraphicsPipeline::Equal:
4391 return VK_COMPARE_OP_EQUAL;
4392 case QRhiGraphicsPipeline::LessOrEqual:
4393 return VK_COMPARE_OP_LESS_OR_EQUAL;
4394 case QRhiGraphicsPipeline::Greater:
4395 return VK_COMPARE_OP_GREATER;
4396 case QRhiGraphicsPipeline::NotEqual:
4397 return VK_COMPARE_OP_NOT_EQUAL;
4398 case QRhiGraphicsPipeline::GreaterOrEqual:
4399 return VK_COMPARE_OP_GREATER_OR_EQUAL;
4400 case QRhiGraphicsPipeline::Always:
4401 return VK_COMPARE_OP_ALWAYS;
4402 default:
4403 Q_UNREACHABLE();
4404 return VK_COMPARE_OP_ALWAYS;
4405 }
4406}
4407
4408static inline VkStencilOp toVkStencilOp(QRhiGraphicsPipeline::StencilOp op)
4409{
4410 switch (op) {
4411 case QRhiGraphicsPipeline::StencilZero:
4412 return VK_STENCIL_OP_ZERO;
4413 case QRhiGraphicsPipeline::Keep:
4414 return VK_STENCIL_OP_KEEP;
4415 case QRhiGraphicsPipeline::Replace:
4416 return VK_STENCIL_OP_REPLACE;
4417 case QRhiGraphicsPipeline::IncrementAndClamp:
4418 return VK_STENCIL_OP_INCREMENT_AND_CLAMP;
4419 case QRhiGraphicsPipeline::DecrementAndClamp:
4420 return VK_STENCIL_OP_DECREMENT_AND_CLAMP;
4421 case QRhiGraphicsPipeline::Invert:
4422 return VK_STENCIL_OP_INVERT;
4423 case QRhiGraphicsPipeline::IncrementAndWrap:
4424 return VK_STENCIL_OP_INCREMENT_AND_WRAP;
4425 case QRhiGraphicsPipeline::DecrementAndWrap:
4426 return VK_STENCIL_OP_DECREMENT_AND_WRAP;
4427 default:
4428 Q_UNREACHABLE();
4429 return VK_STENCIL_OP_KEEP;
4430 }
4431}
4432
4433static inline void fillVkStencilOpState(VkStencilOpState *dst, const QRhiGraphicsPipeline::StencilOpState &src)
4434{
4435 dst->failOp = toVkStencilOp(src.failOp);
4436 dst->passOp = toVkStencilOp(src.passOp);
4437 dst->depthFailOp = toVkStencilOp(src.depthFailOp);
4438 dst->compareOp = toVkCompareOp(src.compareOp);
4439}
4440
4441static inline VkDescriptorType toVkDescriptorType(const QRhiShaderResourceBindingPrivate *b)
4442{
4443 switch (b->type) {
4444 case QRhiShaderResourceBinding::UniformBuffer:
4445 return b->u.ubuf.hasDynamicOffset ? VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC
4446 : VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
4447
4448 case QRhiShaderResourceBinding::SampledTexture:
4449 return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
4450
4451 case QRhiShaderResourceBinding::ImageLoad:
4452 Q_FALLTHROUGH();
4453 case QRhiShaderResourceBinding::ImageStore:
4454 Q_FALLTHROUGH();
4455 case QRhiShaderResourceBinding::ImageLoadStore:
4456 return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
4457
4458 case QRhiShaderResourceBinding::BufferLoad:
4459 Q_FALLTHROUGH();
4460 case QRhiShaderResourceBinding::BufferStore:
4461 Q_FALLTHROUGH();
4462 case QRhiShaderResourceBinding::BufferLoadStore:
4463 return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
4464
4465 default:
4466 Q_UNREACHABLE();
4467 return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
4468 }
4469}
4470
4471static inline VkShaderStageFlags toVkShaderStageFlags(QRhiShaderResourceBinding::StageFlags stage)
4472{
4473 int s = 0;
4474 if (stage.testFlag(QRhiShaderResourceBinding::VertexStage))
4475 s |= VK_SHADER_STAGE_VERTEX_BIT;
4476 if (stage.testFlag(QRhiShaderResourceBinding::FragmentStage))
4477 s |= VK_SHADER_STAGE_FRAGMENT_BIT;
4478 if (stage.testFlag(QRhiShaderResourceBinding::ComputeStage))
4479 s |= VK_SHADER_STAGE_COMPUTE_BIT;
4480 return VkShaderStageFlags(s);
4481}
4482
4483static inline VkCompareOp toVkTextureCompareOp(QRhiSampler::CompareOp op)
4484{
4485 switch (op) {
4486 case QRhiSampler::Never:
4487 return VK_COMPARE_OP_NEVER;
4488 case QRhiSampler::Less:
4489 return VK_COMPARE_OP_LESS;
4490 case QRhiSampler::Equal:
4491 return VK_COMPARE_OP_EQUAL;
4492 case QRhiSampler::LessOrEqual:
4493 return VK_COMPARE_OP_LESS_OR_EQUAL;
4494 case QRhiSampler::Greater:
4495 return VK_COMPARE_OP_GREATER;
4496 case QRhiSampler::NotEqual:
4497 return VK_COMPARE_OP_NOT_EQUAL;
4498 case QRhiSampler::GreaterOrEqual:
4499 return VK_COMPARE_OP_GREATER_OR_EQUAL;
4500 case QRhiSampler::Always:
4501 return VK_COMPARE_OP_ALWAYS;
4502 default:
4503 Q_UNREACHABLE();
4504 return VK_COMPARE_OP_NEVER;
4505 }
4506}
4507
4508QVkBuffer::QVkBuffer(QRhiImplementation *rhi, Type type, UsageFlags usage, int size)
4509 : QRhiBuffer(rhi, type, usage, size)
4510{
4511 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
4512 buffers[i] = stagingBuffers[i] = VK_NULL_HANDLE;
4513 allocations[i] = stagingAllocations[i] = nullptr;
4514 }
4515}
4516
4517QVkBuffer::~QVkBuffer()
4518{
4519 release();
4520}
4521
4522void QVkBuffer::release()
4523{
4524 if (!buffers[0])
4525 return;
4526
4527 QRhiVulkan::DeferredReleaseEntry e;
4528 e.type = QRhiVulkan::DeferredReleaseEntry::Buffer;
4529 e.lastActiveFrameSlot = lastActiveFrameSlot;
4530
4531 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
4532 e.buffer.buffers[i] = buffers[i];
4533 e.buffer.allocations[i] = allocations[i];
4534 e.buffer.stagingBuffers[i] = stagingBuffers[i];
4535 e.buffer.stagingAllocations[i] = stagingAllocations[i];
4536
4537 buffers[i] = VK_NULL_HANDLE;
4538 allocations[i] = nullptr;
4539 stagingBuffers[i] = VK_NULL_HANDLE;
4540 stagingAllocations[i] = nullptr;
4541 pendingDynamicUpdates[i].clear();
4542 }
4543
4544 QRHI_RES_RHI(QRhiVulkan);
4545 rhiD->releaseQueue.append(e);
4546
4547 QRHI_PROF;
4548 QRHI_PROF_F(releaseBuffer(this));
4549
4550 rhiD->unregisterResource(this);
4551}
4552
4553bool QVkBuffer::build()
4554{
4555 if (buffers[0])
4556 release();
4557
4558 if (m_usage.testFlag(QRhiBuffer::StorageBuffer) && m_type == Dynamic) {
4559 qWarning("StorageBuffer cannot be combined with Dynamic");
4560 return false;
4561 }
4562
4563 const int nonZeroSize = m_size <= 0 ? 256 : m_size;
4564
4565 VkBufferCreateInfo bufferInfo;
4566 memset(&bufferInfo, 0, sizeof(bufferInfo));
4567 bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
4568 bufferInfo.size = nonZeroSize;
4569 bufferInfo.usage = toVkBufferUsage(m_usage);
4570
4571 VmaAllocationCreateInfo allocInfo;
4572 memset(&allocInfo, 0, sizeof(allocInfo));
4573
4574 if (m_type == Dynamic) {
4575#ifndef Q_OS_DARWIN // not for MoltenVK
4576 // Keep mapped all the time. Essential f.ex. with some mobile GPUs,
4577 // where mapping and unmapping an entire allocation every time updating
4578 // a suballocated buffer presents a significant perf. hit.
4579 allocInfo.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT;
4580#endif
4581 // host visible, frequent changes
4582 allocInfo.usage = VMA_MEMORY_USAGE_CPU_TO_GPU;
4583 } else {
4584 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
4585 bufferInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
4586 }
4587
4588 QRHI_RES_RHI(QRhiVulkan);
4589 VkResult err = VK_SUCCESS;
4590 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
4591 buffers[i] = VK_NULL_HANDLE;
4592 allocations[i] = nullptr;
4593 usageState[i].access = usageState[i].stage = 0;
4594 if (i == 0 || m_type == Dynamic) {
4595 VmaAllocation allocation;
4596 err = vmaCreateBuffer(toVmaAllocator(rhiD->allocator), &bufferInfo, &allocInfo, &buffers[i], &allocation, nullptr);
4597 if (err != VK_SUCCESS)
4598 break;
4599
4600 allocations[i] = allocation;
4601 if (m_type == Dynamic)
4602 pendingDynamicUpdates[i].reserve(16);
4603
4604 rhiD->setObjectName(uint64_t(buffers[i]), VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT, m_objectName,
4605 m_type == Dynamic ? i : -1);
4606 }
4607 }
4608
4609 if (err != VK_SUCCESS) {
4610 qWarning("Failed to create buffer: %d", err);
4611 return false;
4612 }
4613
4614 QRHI_PROF;
4615 QRHI_PROF_F(newBuffer(this, nonZeroSize, m_type != Dynamic ? 1 : QVK_FRAMES_IN_FLIGHT, 0));
4616
4617 lastActiveFrameSlot = -1;
4618 generation += 1;
4619 rhiD->registerResource(this);
4620 return true;
4621}
4622
4623QVkRenderBuffer::QVkRenderBuffer(QRhiImplementation *rhi, Type type, const QSize &pixelSize,
4624 int sampleCount, Flags flags)
4625 : QRhiRenderBuffer(rhi, type, pixelSize, sampleCount, flags)
4626{
4627}
4628
4629QVkRenderBuffer::~QVkRenderBuffer()
4630{
4631 release();
4632 delete backingTexture;
4633}
4634
4635void QVkRenderBuffer::release()
4636{
4637 if (!memory && !backingTexture)
4638 return;
4639
4640 QRhiVulkan::DeferredReleaseEntry e;
4641 e.type = QRhiVulkan::DeferredReleaseEntry::RenderBuffer;
4642 e.lastActiveFrameSlot = lastActiveFrameSlot;
4643
4644 e.renderBuffer.memory = memory;
4645 e.renderBuffer.image = image;
4646 e.renderBuffer.imageView = imageView;
4647
4648 memory = VK_NULL_HANDLE;
4649 image = VK_NULL_HANDLE;
4650 imageView = VK_NULL_HANDLE;
4651
4652 if (backingTexture) {
4653 Q_ASSERT(backingTexture->lastActiveFrameSlot == -1);
4654 backingTexture->lastActiveFrameSlot = e.lastActiveFrameSlot;
4655 backingTexture->release();
4656 }
4657
4658 QRHI_RES_RHI(QRhiVulkan);
4659 rhiD->releaseQueue.append(e);
4660
4661 QRHI_PROF;
4662 QRHI_PROF_F(releaseRenderBuffer(this));
4663
4664 rhiD->unregisterResource(this);
4665}
4666
4667bool QVkRenderBuffer::build()
4668{
4669 if (memory || backingTexture)
4670 release();
4671
4672 if (m_pixelSize.isEmpty())
4673 return false;
4674
4675 QRHI_RES_RHI(QRhiVulkan);
4676 QRHI_PROF;
4677 samples = rhiD->effectiveSampleCount(m_sampleCount);
4678
4679 switch (m_type) {
4680 case QRhiRenderBuffer::Color:
4681 {
4682 if (!backingTexture) {
4683 backingTexture = QRHI_RES(QVkTexture, rhiD->createTexture(QRhiTexture::RGBA8,
4684 m_pixelSize,
4685 m_sampleCount,
4686 QRhiTexture::RenderTarget | QRhiTexture::UsedAsTransferSource));
4687 } else {
4688 backingTexture->setPixelSize(m_pixelSize);
4689 backingTexture->setSampleCount(m_sampleCount);
4690 }
4691 backingTexture->setName(m_objectName);
4692 if (!backingTexture->build())
4693 return false;
4694 vkformat = backingTexture->vkformat;
4695 QRHI_PROF_F(newRenderBuffer(this, false, false, samples));
4696 }
4697 break;
4698 case QRhiRenderBuffer::DepthStencil:
4699 vkformat = rhiD->optimalDepthStencilFormat();
4700 if (!rhiD->createTransientImage(vkformat,
4701 m_pixelSize,
4702 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
4703 VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT,
4704 samples,
4705 &memory,
4706 &image,
4707 &imageView,
4708 1))
4709 {
4710 return false;
4711 }
4712 rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
4713 QRHI_PROF_F(newRenderBuffer(this, true, false, samples));
4714 break;
4715 default:
4716 Q_UNREACHABLE();
4717 break;
4718 }
4719
4720 lastActiveFrameSlot = -1;
4721 rhiD->registerResource(this);
4722 return true;
4723}
4724
4725QRhiTexture::Format QVkRenderBuffer::backingFormat() const
4726{
4727 return m_type == Color ? QRhiTexture::RGBA8 : QRhiTexture::UnknownFormat;
4728}
4729
4730QVkTexture::QVkTexture(QRhiImplementation *rhi, Format format, const QSize &pixelSize,
4731 int sampleCount, Flags flags)
4732 : QRhiTexture(rhi, format, pixelSize, sampleCount, flags)
4733{
4734 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
4735 stagingBuffers[i] = VK_NULL_HANDLE;
4736 stagingAllocations[i] = nullptr;
4737 }
4738 for (int i = 0; i < QRhi::MAX_LEVELS; ++i)
4739 perLevelImageViews[i] = VK_NULL_HANDLE;
4740}
4741
4742QVkTexture::~QVkTexture()
4743{
4744 release();
4745}
4746
4747void QVkTexture::release()
4748{
4749 if (!image)
4750 return;
4751
4752 QRhiVulkan::DeferredReleaseEntry e;
4753 e.type = QRhiVulkan::DeferredReleaseEntry::Texture;
4754 e.lastActiveFrameSlot = lastActiveFrameSlot;
4755
4756 e.texture.image = owns ? image : VK_NULL_HANDLE;
4757 e.texture.imageView = imageView;
4758 e.texture.allocation = owns ? imageAlloc : nullptr;
4759
4760 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i) {
4761 e.texture.stagingBuffers[i] = stagingBuffers[i];
4762 e.texture.stagingAllocations[i] = stagingAllocations[i];
4763
4764 stagingBuffers[i] = VK_NULL_HANDLE;
4765 stagingAllocations[i] = nullptr;
4766 }
4767
4768 for (int i = 0; i < QRhi::MAX_LEVELS; ++i) {
4769 e.texture.extraImageViews[i] = perLevelImageViews[i];
4770 perLevelImageViews[i] = VK_NULL_HANDLE;
4771 }
4772
4773 image = VK_NULL_HANDLE;
4774 imageView = VK_NULL_HANDLE;
4775 imageAlloc = nullptr;
4776 nativeHandlesStruct.image = VK_NULL_HANDLE;
4777
4778 QRHI_RES_RHI(QRhiVulkan);
4779 rhiD->releaseQueue.append(e);
4780
4781 QRHI_PROF;
4782 QRHI_PROF_F(releaseTexture(this));
4783
4784 rhiD->unregisterResource(this);
4785}
4786
4787bool QVkTexture::prepareBuild(QSize *adjustedSize)
4788{
4789 if (image)
4790 release();
4791
4792 QRHI_RES_RHI(QRhiVulkan);
4793 vkformat = toVkTextureFormat(m_format, m_flags);
4794 VkFormatProperties props;
4795 rhiD->f->vkGetPhysicalDeviceFormatProperties(rhiD->physDev, vkformat, &props);
4796 const bool canSampleOptimal = (props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);
4797 if (!canSampleOptimal) {
4798 qWarning("Texture sampling with optimal tiling for format %d not supported", vkformat);
4799 return false;
4800 }
4801
4802 const QSize size = m_pixelSize.isEmpty() ? QSize(1, 1) : m_pixelSize;
4803 const bool isCube = m_flags.testFlag(CubeMap);
4804 const bool hasMipMaps = m_flags.testFlag(MipMapped);
4805
4806 mipLevelCount = hasMipMaps ? rhiD->q->mipLevelsForSize(size) : 1;
4807 const int maxLevels = QRhi::MAX_LEVELS;
4808 if (mipLevelCount > maxLevels) {
4809 qWarning("Too many mip levels (%d, max is %d), truncating mip chain", mipLevelCount, maxLevels);
4810 mipLevelCount = maxLevels;
4811 }
4812 samples = rhiD->effectiveSampleCount(m_sampleCount);
4813 if (samples > VK_SAMPLE_COUNT_1_BIT) {
4814 if (isCube) {
4815 qWarning("Cubemap texture cannot be multisample");
4816 return false;
4817 }
4818 if (hasMipMaps) {
4819 qWarning("Multisample texture cannot have mipmaps");
4820 return false;
4821 }
4822 }
4823
4824 usageState.layout = VK_IMAGE_LAYOUT_PREINITIALIZED;
4825 usageState.access = 0;
4826 usageState.stage = 0;
4827
4828 if (adjustedSize)
4829 *adjustedSize = size;
4830
4831 return true;
4832}
4833
4834bool QVkTexture::finishBuild()
4835{
4836 QRHI_RES_RHI(QRhiVulkan);
4837
4838 const bool isDepth = isDepthTextureFormat(m_format);
4839 const bool isCube = m_flags.testFlag(CubeMap);
4840
4841 VkImageViewCreateInfo viewInfo;
4842 memset(&viewInfo, 0, sizeof(viewInfo));
4843 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
4844 viewInfo.image = image;
4845 viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D;
4846 viewInfo.format = vkformat;
4847 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
4848 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
4849 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
4850 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
4851 viewInfo.subresourceRange.aspectMask = isDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
4852 viewInfo.subresourceRange.levelCount = mipLevelCount;
4853 viewInfo.subresourceRange.layerCount = isCube ? 6 : 1;
4854
4855 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &imageView);
4856 if (err != VK_SUCCESS) {
4857 qWarning("Failed to create image view: %d", err);
4858 return false;
4859 }
4860
4861 nativeHandlesStruct.image = image;
4862
4863 lastActiveFrameSlot = -1;
4864 generation += 1;
4865
4866 return true;
4867}
4868
4869bool QVkTexture::build()
4870{
4871 QSize size;
4872 if (!prepareBuild(&size))
4873 return false;
4874
4875 const bool isRenderTarget = m_flags.testFlag(QRhiTexture::RenderTarget);
4876 const bool isDepth = isDepthTextureFormat(m_format);
4877 const bool isCube = m_flags.testFlag(CubeMap);
4878
4879 VkImageCreateInfo imageInfo;
4880 memset(&imageInfo, 0, sizeof(imageInfo));
4881 imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
4882 imageInfo.flags = isCube ? VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT : 0;
4883 imageInfo.imageType = VK_IMAGE_TYPE_2D;
4884 imageInfo.format = vkformat;
4885 imageInfo.extent.width = size.width();
4886 imageInfo.extent.height = size.height();
4887 imageInfo.extent.depth = 1;
4888 imageInfo.mipLevels = mipLevelCount;
4889 imageInfo.arrayLayers = isCube ? 6 : 1;
4890 imageInfo.samples = samples;
4891 imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
4892 imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
4893
4894 imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
4895 if (isRenderTarget) {
4896 if (isDepth)
4897 imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
4898 else
4899 imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
4900 }
4901 if (m_flags.testFlag(QRhiTexture::UsedAsTransferSource))
4902 imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
4903 if (m_flags.testFlag(QRhiTexture::UsedWithGenerateMips))
4904 imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
4905 if (m_flags.testFlag(QRhiTexture::UsedWithLoadStore))
4906 imageInfo.usage |= VK_IMAGE_USAGE_STORAGE_BIT;
4907
4908 VmaAllocationCreateInfo allocInfo;
4909 memset(&allocInfo, 0, sizeof(allocInfo));
4910 allocInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY;
4911
4912 QRHI_RES_RHI(QRhiVulkan);
4913 VmaAllocation allocation;
4914 VkResult err = vmaCreateImage(toVmaAllocator(rhiD->allocator), &imageInfo, &allocInfo, &image, &allocation, nullptr);
4915 if (err != VK_SUCCESS) {
4916 qWarning("Failed to create image: %d", err);
4917 return false;
4918 }
4919 imageAlloc = allocation;
4920
4921 if (!finishBuild())
4922 return false;
4923
4924 rhiD->setObjectName(uint64_t(image), VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT, m_objectName);
4925
4926 QRHI_PROF;
4927 QRHI_PROF_F(newTexture(this, true, mipLevelCount, isCube ? 6 : 1, samples));
4928
4929 owns = true;
4930 rhiD->registerResource(this);
4931 return true;
4932}
4933
4934bool QVkTexture::buildFrom(const QRhiNativeHandles *src)
4935{
4936 const QRhiVulkanTextureNativeHandles *h = static_cast<const QRhiVulkanTextureNativeHandles *>(src);
4937 if (!h || !h->image)
4938 return false;
4939
4940 if (!prepareBuild())
4941 return false;
4942
4943 image = h->image;
4944
4945 if (!finishBuild())
4946 return false;
4947
4948 QRHI_PROF;
4949 QRHI_PROF_F(newTexture(this, false, mipLevelCount, m_flags.testFlag(CubeMap) ? 6 : 1, samples));
4950
4951 usageState.layout = h->layout;
4952
4953 owns = false;
4954 QRHI_RES_RHI(QRhiVulkan);
4955 rhiD->registerResource(this);
4956 return true;
4957}
4958
4959const QRhiNativeHandles *QVkTexture::nativeHandles()
4960{
4961 nativeHandlesStruct.layout = usageState.layout;
4962 return &nativeHandlesStruct;
4963}
4964
4965VkImageView QVkTexture::imageViewForLevel(int level)
4966{
4967 Q_ASSERT(level >= 0 && level < int(mipLevelCount));
4968 if (perLevelImageViews[level] != VK_NULL_HANDLE)
4969 return perLevelImageViews[level];
4970
4971 const bool isDepth = isDepthTextureFormat(m_format);
4972 const bool isCube = m_flags.testFlag(CubeMap);
4973
4974 VkImageViewCreateInfo viewInfo;
4975 memset(&viewInfo, 0, sizeof(viewInfo));
4976 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
4977 viewInfo.image = image;
4978 viewInfo.viewType = isCube ? VK_IMAGE_VIEW_TYPE_CUBE : VK_IMAGE_VIEW_TYPE_2D;
4979 viewInfo.format = vkformat;
4980 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
4981 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
4982 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
4983 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
4984 viewInfo.subresourceRange.aspectMask = isDepth ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT;
4985 viewInfo.subresourceRange.baseMipLevel = level;
4986 viewInfo.subresourceRange.levelCount = 1;
4987 viewInfo.subresourceRange.baseArrayLayer = 0;
4988 viewInfo.subresourceRange.layerCount = isCube ? 6 : 1;
4989
4990 VkImageView v = VK_NULL_HANDLE;
4991 QRHI_RES_RHI(QRhiVulkan);
4992 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &v);
4993 if (err != VK_SUCCESS) {
4994 qWarning("Failed to create image view: %d", err);
4995 return VK_NULL_HANDLE;
4996 }
4997
4998 perLevelImageViews[level] = v;
4999 return v;
5000}
5001
5002QVkSampler::QVkSampler(QRhiImplementation *rhi, Filter magFilter, Filter minFilter, Filter mipmapMode,
5003 AddressMode u, AddressMode v)
5004 : QRhiSampler(rhi, magFilter, minFilter, mipmapMode, u, v)
5005{
5006}
5007
5008QVkSampler::~QVkSampler()
5009{
5010 release();
5011}
5012
5013void QVkSampler::release()
5014{
5015 if (!sampler)
5016 return;
5017
5018 QRhiVulkan::DeferredReleaseEntry e;
5019 e.type = QRhiVulkan::DeferredReleaseEntry::Sampler;
5020 e.lastActiveFrameSlot = lastActiveFrameSlot;
5021
5022 e.sampler.sampler = sampler;
5023 sampler = VK_NULL_HANDLE;
5024
5025 QRHI_RES_RHI(QRhiVulkan);
5026 rhiD->releaseQueue.append(e);
5027 rhiD->unregisterResource(this);
5028}
5029
5030bool QVkSampler::build()
5031{
5032 if (sampler)
5033 release();
5034
5035 VkSamplerCreateInfo samplerInfo;
5036 memset(&samplerInfo, 0, sizeof(samplerInfo));
5037 samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
5038 samplerInfo.magFilter = toVkFilter(m_magFilter);
5039 samplerInfo.minFilter = toVkFilter(m_minFilter);
5040 samplerInfo.mipmapMode = toVkMipmapMode(m_mipmapMode);
5041 samplerInfo.addressModeU = toVkAddressMode(m_addressU);
5042 samplerInfo.addressModeV = toVkAddressMode(m_addressV);
5043 samplerInfo.addressModeW = toVkAddressMode(m_addressW);
5044 samplerInfo.maxAnisotropy = 1.0f;
5045 samplerInfo.compareEnable = m_compareOp != Never;
5046 samplerInfo.compareOp = toVkTextureCompareOp(m_compareOp);
5047 samplerInfo.maxLod = m_mipmapMode == None ? 0.25f : 1000.0f;
5048
5049 QRHI_RES_RHI(QRhiVulkan);
5050 VkResult err = rhiD->df->vkCreateSampler(rhiD->dev, &samplerInfo, nullptr, &sampler);
5051 if (err != VK_SUCCESS) {
5052 qWarning("Failed to create sampler: %d", err);
5053 return false;
5054 }
5055
5056 lastActiveFrameSlot = -1;
5057 generation += 1;
5058 rhiD->registerResource(this);
5059 return true;
5060}
5061
5062QVkRenderPassDescriptor::QVkRenderPassDescriptor(QRhiImplementation *rhi)
5063 : QRhiRenderPassDescriptor(rhi)
5064{
5065}
5066
5067QVkRenderPassDescriptor::~QVkRenderPassDescriptor()
5068{
5069 release();
5070}
5071
5072void QVkRenderPassDescriptor::release()
5073{
5074 if (!rp)
5075 return;
5076
5077 if (!ownsRp) {
5078 rp = VK_NULL_HANDLE;
5079 return;
5080 }
5081
5082 QRhiVulkan::DeferredReleaseEntry e;
5083 e.type = QRhiVulkan::DeferredReleaseEntry::RenderPass;
5084 e.lastActiveFrameSlot = lastActiveFrameSlot;
5085
5086 e.renderPass.rp = rp;
5087
5088 rp = VK_NULL_HANDLE;
5089
5090 QRHI_RES_RHI(QRhiVulkan);
5091 rhiD->releaseQueue.append(e);
5092
5093 rhiD->unregisterResource(this);
5094}
5095
5096QVkReferenceRenderTarget::QVkReferenceRenderTarget(QRhiImplementation *rhi)
5097 : QRhiRenderTarget(rhi)
5098{
5099}
5100
5101QVkReferenceRenderTarget::~QVkReferenceRenderTarget()
5102{
5103 release();
5104}
5105
5106void QVkReferenceRenderTarget::release()
5107{
5108 // nothing to do here
5109}
5110
5111QSize QVkReferenceRenderTarget::pixelSize() const
5112{
5113 return d.pixelSize;
5114}
5115
5116float QVkReferenceRenderTarget::devicePixelRatio() const
5117{
5118 return d.dpr;
5119}
5120
5121int QVkReferenceRenderTarget::sampleCount() const
5122{
5123 return d.sampleCount;
5124}
5125
5126QVkTextureRenderTarget::QVkTextureRenderTarget(QRhiImplementation *rhi,
5127 const QRhiTextureRenderTargetDescription &desc,
5128 Flags flags)
5129 : QRhiTextureRenderTarget(rhi, desc, flags)
5130{
5131 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
5132 rtv[att] = VK_NULL_HANDLE;
5133 resrtv[att] = VK_NULL_HANDLE;
5134 }
5135}
5136
5137QVkTextureRenderTarget::~QVkTextureRenderTarget()
5138{
5139 release();
5140}
5141
5142void QVkTextureRenderTarget::release()
5143{
5144 if (!d.fb)
5145 return;
5146
5147 QRhiVulkan::DeferredReleaseEntry e;
5148 e.type = QRhiVulkan::DeferredReleaseEntry::TextureRenderTarget;
5149 e.lastActiveFrameSlot = lastActiveFrameSlot;
5150
5151 e.textureRenderTarget.fb = d.fb;
5152 d.fb = VK_NULL_HANDLE;
5153
5154 for (int att = 0; att < QVkRenderTargetData::MAX_COLOR_ATTACHMENTS; ++att) {
5155 e.textureRenderTarget.rtv[att] = rtv[att];
5156 e.textureRenderTarget.resrtv[att] = resrtv[att];
5157 rtv[att] = VK_NULL_HANDLE;
5158 resrtv[att] = VK_NULL_HANDLE;
5159 }
5160
5161 QRHI_RES_RHI(QRhiVulkan);
5162 rhiD->releaseQueue.append(e);
5163
5164 rhiD->unregisterResource(this);
5165}
5166
5167QRhiRenderPassDescriptor *QVkTextureRenderTarget::newCompatibleRenderPassDescriptor()
5168{
5169 // not yet built so cannot rely on data computed in build()
5170
5171 QRHI_RES_RHI(QRhiVulkan);
5172 QVkRenderPassDescriptor *rp = new QVkRenderPassDescriptor(m_rhi);
5173 if (!rhiD->createOffscreenRenderPass(&rp->rp,
5174 m_desc.colorAttachments(),
5175 m_flags.testFlag(QRhiTextureRenderTarget::PreserveColorContents),
5176 m_flags.testFlag(QRhiTextureRenderTarget::PreserveDepthStencilContents),
5177 m_desc.depthStencilBuffer(),
5178 m_desc.depthTexture()))
5179 {
5180 delete rp;
5181 return nullptr;
5182 }
5183
5184 rp->ownsRp = true;
5185 rhiD->registerResource(rp);
5186 return rp;
5187}
5188
5189bool QVkTextureRenderTarget::build()
5190{
5191 if (d.fb)
5192 release();
5193
5194 const QVector<QRhiColorAttachment> colorAttachments = m_desc.colorAttachments();
5195 Q_ASSERT(!colorAttachments.isEmpty() || m_desc.depthTexture());
5196 Q_ASSERT(!m_desc.depthStencilBuffer() || !m_desc.depthTexture());
5197 const bool hasDepthStencil = m_desc.depthStencilBuffer() || m_desc.depthTexture();
5198
5199 QRHI_RES_RHI(QRhiVulkan);
5200 QVarLengthArray<VkImageView, 8> views;
5201
5202 d.colorAttCount = colorAttachments.count();
5203 for (int i = 0; i < d.colorAttCount; ++i) {
5204 QVkTexture *texD = QRHI_RES(QVkTexture, colorAttachments[i].texture());
5205 QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, colorAttachments[i].renderBuffer());
5206 Q_ASSERT(texD || rbD);
5207 if (texD) {
5208 Q_ASSERT(texD->flags().testFlag(QRhiTexture::RenderTarget));
5209 VkImageViewCreateInfo viewInfo;
5210 memset(&viewInfo, 0, sizeof(viewInfo));
5211 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
5212 viewInfo.image = texD->image;
5213 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
5214 viewInfo.format = texD->vkformat;
5215 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
5216 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
5217 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
5218 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
5219 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
5220 viewInfo.subresourceRange.baseMipLevel = colorAttachments[i].level();
5221 viewInfo.subresourceRange.levelCount = 1;
5222 viewInfo.subresourceRange.baseArrayLayer = colorAttachments[i].layer();
5223 viewInfo.subresourceRange.layerCount = 1;
5224 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &rtv[i]);
5225 if (err != VK_SUCCESS) {
5226 qWarning("Failed to create render target image view: %d", err);
5227 return false;
5228 }
5229 views.append(rtv[i]);
5230 if (i == 0) {
5231 d.pixelSize = texD->pixelSize();
5232 d.sampleCount = texD->samples;
5233 }
5234 } else if (rbD) {
5235 Q_ASSERT(rbD->backingTexture);
5236 views.append(rbD->backingTexture->imageView);
5237 if (i == 0) {
5238 d.pixelSize = rbD->pixelSize();
5239 d.sampleCount = rbD->samples;
5240 }
5241 }
5242 }
5243 d.dpr = 1;
5244
5245 if (hasDepthStencil) {
5246 if (m_desc.depthTexture()) {
5247 QVkTexture *depthTexD = QRHI_RES(QVkTexture, m_desc.depthTexture());
5248 views.append(depthTexD->imageView);
5249 if (d.colorAttCount == 0) {
5250 d.pixelSize = depthTexD->pixelSize();
5251 d.sampleCount = depthTexD->samples;
5252 }
5253 } else {
5254 QVkRenderBuffer *depthRbD = QRHI_RES(QVkRenderBuffer, m_desc.depthStencilBuffer());
5255 views.append(depthRbD->imageView);
5256 if (d.colorAttCount == 0) {
5257 d.pixelSize = depthRbD->pixelSize();
5258 d.sampleCount = depthRbD->samples;
5259 }
5260 }
5261 d.dsAttCount = 1;
5262 } else {
5263 d.dsAttCount = 0;
5264 }
5265
5266 d.resolveAttCount = 0;
5267 for (int i = 0; i < d.colorAttCount; ++i) {
5268 if (colorAttachments[i].resolveTexture()) {
5269 QVkTexture *resTexD = QRHI_RES(QVkTexture, colorAttachments[i].resolveTexture());
5270 Q_ASSERT(resTexD->flags().testFlag(QRhiTexture::RenderTarget));
5271 d.resolveAttCount += 1;
5272
5273 VkImageViewCreateInfo viewInfo;
5274 memset(&viewInfo, 0, sizeof(viewInfo));
5275 viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
5276 viewInfo.image = resTexD->image;
5277 viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
5278 viewInfo.format = resTexD->vkformat;
5279 viewInfo.components.r = VK_COMPONENT_SWIZZLE_R;
5280 viewInfo.components.g = VK_COMPONENT_SWIZZLE_G;
5281 viewInfo.components.b = VK_COMPONENT_SWIZZLE_B;
5282 viewInfo.components.a = VK_COMPONENT_SWIZZLE_A;
5283 viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
5284 viewInfo.subresourceRange.baseMipLevel = colorAttachments[i].resolveLevel();
5285 viewInfo.subresourceRange.levelCount = 1;
5286 viewInfo.subresourceRange.baseArrayLayer = colorAttachments[i].resolveLayer();
5287 viewInfo.subresourceRange.layerCount = 1;
5288 VkResult err = rhiD->df->vkCreateImageView(rhiD->dev, &viewInfo, nullptr, &resrtv[i]);
5289 if (err != VK_SUCCESS) {
5290 qWarning("Failed to create render target resolve image view: %d", err);
5291 return false;
5292 }
5293 views.append(resrtv[i]);
5294 }
5295 }
5296
5297 if (!m_renderPassDesc)
5298 qWarning("QVkTextureRenderTarget: No renderpass descriptor set. See newCompatibleRenderPassDescriptor() and setRenderPassDescriptor().");
5299
5300 d.rp = QRHI_RES(QVkRenderPassDescriptor, m_renderPassDesc);
5301 Q_ASSERT(d.rp && d.rp->rp);
5302
5303 VkFramebufferCreateInfo fbInfo;
5304 memset(&fbInfo, 0, sizeof(fbInfo));
5305 fbInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
5306 fbInfo.renderPass = d.rp->rp;
5307 fbInfo.attachmentCount = d.colorAttCount + d.dsAttCount + d.resolveAttCount;
5308 fbInfo.pAttachments = views.constData();
5309 fbInfo.width = d.pixelSize.width();
5310 fbInfo.height = d.pixelSize.height();
5311 fbInfo.layers = 1;
5312
5313 VkResult err = rhiD->df->vkCreateFramebuffer(rhiD->dev, &fbInfo, nullptr, &d.fb);
5314 if (err != VK_SUCCESS) {
5315 qWarning("Failed to create framebuffer: %d", err);
5316 return false;
5317 }
5318
5319 lastActiveFrameSlot = -1;
5320 rhiD->registerResource(this);
5321 return true;
5322}
5323
5324QSize QVkTextureRenderTarget::pixelSize() const
5325{
5326 return d.pixelSize;
5327}
5328
5329float QVkTextureRenderTarget::devicePixelRatio() const
5330{
5331 return d.dpr;
5332}
5333
5334int QVkTextureRenderTarget::sampleCount() const
5335{
5336 return d.sampleCount;
5337}
5338
5339QVkShaderResourceBindings::QVkShaderResourceBindings(QRhiImplementation *rhi)
5340 : QRhiShaderResourceBindings(rhi)
5341{
5342}
5343
5344QVkShaderResourceBindings::~QVkShaderResourceBindings()
5345{
5346 release();
5347}
5348
5349void QVkShaderResourceBindings::release()
5350{
5351 if (!layout)
5352 return;
5353
5354 sortedBindings.clear();
5355
5356 QRhiVulkan::DeferredReleaseEntry e;
5357 e.type = QRhiVulkan::DeferredReleaseEntry::ShaderResourceBindings;
5358 e.lastActiveFrameSlot = lastActiveFrameSlot;
5359
5360 e.shaderResourceBindings.poolIndex = poolIndex;
5361 e.shaderResourceBindings.layout = layout;
5362
5363 poolIndex = -1;
5364 layout = VK_NULL_HANDLE;
5365 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
5366 descSets[i] = VK_NULL_HANDLE;
5367
5368 QRHI_RES_RHI(QRhiVulkan);
5369 rhiD->releaseQueue.append(e);
5370
5371 rhiD->unregisterResource(this);
5372}
5373
5374bool QVkShaderResourceBindings::build()
5375{
5376 if (layout)
5377 release();
5378
5379 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
5380 descSets[i] = VK_NULL_HANDLE;
5381
5382 sortedBindings = m_bindings;
5383 std::sort(sortedBindings.begin(), sortedBindings.end(),
5384 [](const QRhiShaderResourceBinding &a, const QRhiShaderResourceBinding &b)
5385 {
5386 return QRhiShaderResourceBindingPrivate::get(&a)->binding < QRhiShaderResourceBindingPrivate::get(&b)->binding;
5387 });
5388
5389 QVarLengthArray<VkDescriptorSetLayoutBinding, 4> vkbindings;
5390 for (const QRhiShaderResourceBinding &binding : qAsConst(sortedBindings)) {
5391 const QRhiShaderResourceBindingPrivate *b = QRhiShaderResourceBindingPrivate::get(&binding);
5392 VkDescriptorSetLayoutBinding vkbinding;
5393 memset(&vkbinding, 0, sizeof(vkbinding));
5394 vkbinding.binding = b->binding;
5395 vkbinding.descriptorType = toVkDescriptorType(b);
5396 vkbinding.descriptorCount = 1; // no array support yet
5397 vkbinding.stageFlags = toVkShaderStageFlags(b->stage);
5398 vkbindings.append(vkbinding);
5399 }
5400
5401 VkDescriptorSetLayoutCreateInfo layoutInfo;
5402 memset(&layoutInfo, 0, sizeof(layoutInfo));
5403 layoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
5404 layoutInfo.bindingCount = uint32_t(vkbindings.count());
5405 layoutInfo.pBindings = vkbindings.constData();
5406
5407 QRHI_RES_RHI(QRhiVulkan);
5408 VkResult err = rhiD->df->vkCreateDescriptorSetLayout(rhiD->dev, &layoutInfo, nullptr, &layout);
5409 if (err != VK_SUCCESS) {
5410 qWarning("Failed to create descriptor set layout: %d", err);
5411 return false;
5412 }
5413
5414 VkDescriptorSetAllocateInfo allocInfo;
5415 memset(&allocInfo, 0, sizeof(allocInfo));
5416 allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
5417 allocInfo.descriptorSetCount = QVK_FRAMES_IN_FLIGHT;
5418 VkDescriptorSetLayout layouts[QVK_FRAMES_IN_FLIGHT];
5419 for (int i = 0; i < QVK_FRAMES_IN_FLIGHT; ++i)
5420 layouts[i] = layout;
5421 allocInfo.pSetLayouts = layouts;
5422 if (!rhiD->allocateDescriptorSet(&allocInfo, descSets, &poolIndex))
5423 return false;
5424
5425 rhiD->updateShaderResourceBindings(this);
5426
5427 lastActiveFrameSlot = -1;
5428 generation += 1;
5429 rhiD->registerResource(this);
5430 return true;
5431}
5432
5433QVkGraphicsPipeline::QVkGraphicsPipeline(QRhiImplementation *rhi)
5434 : QRhiGraphicsPipeline(rhi)
5435{
5436}
5437
5438QVkGraphicsPipeline::~QVkGraphicsPipeline()
5439{
5440 release();
5441}
5442
5443void QVkGraphicsPipeline::release()
5444{
5445 if (!pipeline && !layout)
5446 return;
5447
5448 QRhiVulkan::DeferredReleaseEntry e;
5449 e.type = QRhiVulkan::DeferredReleaseEntry::Pipeline;
5450 e.lastActiveFrameSlot = lastActiveFrameSlot;
5451
5452 e.pipelineState.pipeline = pipeline;
5453 e.pipelineState.layout = layout;
5454
5455 pipeline = VK_NULL_HANDLE;
5456 layout = VK_NULL_HANDLE;
5457
5458 QRHI_RES_RHI(QRhiVulkan);
5459 rhiD->releaseQueue.append(e);
5460
5461 rhiD->unregisterResource(this);
5462}
5463
5464bool QVkGraphicsPipeline::build()
5465{
5466 if (pipeline)
5467 release();
5468
5469 QRHI_RES_RHI(QRhiVulkan);
5470 if (!rhiD->ensurePipelineCache())
5471 return false;
5472
5473 VkPipelineLayoutCreateInfo pipelineLayoutInfo;
5474 memset(&pipelineLayoutInfo, 0, sizeof(pipelineLayoutInfo));
5475 pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
5476 pipelineLayoutInfo.setLayoutCount = 1;
5477 QVkShaderResourceBindings *srbD = QRHI_RES(QVkShaderResourceBindings, m_shaderResourceBindings);
5478 Q_ASSERT(m_shaderResourceBindings && srbD->layout);
5479 pipelineLayoutInfo.pSetLayouts = &srbD->layout;
5480 VkResult err = rhiD->df->vkCreatePipelineLayout(rhiD->dev, &pipelineLayoutInfo, nullptr, &layout);
5481 if (err != VK_SUCCESS) {
5482 qWarning("Failed to create pipeline layout: %d", err);
5483 return false;
5484 }
5485
5486 VkGraphicsPipelineCreateInfo pipelineInfo;
5487 memset(&pipelineInfo, 0, sizeof(pipelineInfo));
5488 pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
5489
5490 QVarLengthArray<VkShaderModule, 4> shaders;
5491 QVarLengthArray<VkPipelineShaderStageCreateInfo, 4> shaderStageCreateInfos;
5492 for (const QRhiShaderStage &shaderStage : m_shaderStages) {
5493 const QShader bakedShader = shaderStage.shader();
5494 const QShaderCode spirv = bakedShader.shader({ QShader::SpirvShader, 100, shaderStage.shaderVariant() });
5495 if (spirv.shader().isEmpty()) {
5496 qWarning() << "No SPIR-V 1.0 shader code found in baked shader" << bakedShader;
5497 return false;
5498 }
5499 VkShaderModule shader = rhiD->createShader(spirv.shader());
5500 if (shader) {
5501 shaders.append(shader);
5502 VkPipelineShaderStageCreateInfo shaderInfo;
5503 memset(&shaderInfo, 0, sizeof(shaderInfo));
5504 shaderInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
5505 shaderInfo.stage = toVkShaderStage(shaderStage.type());
5506 shaderInfo.module = shader;
5507 shaderInfo.pName = spirv.entryPoint().constData();
5508 shaderStageCreateInfos.append(shaderInfo);
5509 }
5510