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