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