1#if defined(CONF_BACKEND_VULKAN)
2
3#include <engine/client/backend/vulkan/backend_vulkan.h>
4
5#include <engine/client/backend/backend_base.h>
6#include <engine/client/backend_sdl.h>
7#include <engine/client/graphics_threaded.h>
8#include <engine/gfx/image_manipulation.h>
9#include <engine/graphics.h>
10#include <engine/shared/config.h>
11#include <engine/shared/localization.h>
12#include <engine/storage.h>
13
14#include <base/log.h>
15#include <base/math.h>
16#include <base/system.h>
17
18#include <algorithm>
19#include <array>
20#include <condition_variable>
21#include <cstddef>
22#include <cstdlib>
23#include <functional>
24#include <limits>
25#include <map>
26#include <memory>
27#include <mutex>
28#include <optional>
29#include <set>
30#include <string>
31#include <thread>
32#include <unordered_map>
33#include <utility>
34#include <vector>
35
36#include <SDL_video.h>
37#include <SDL_vulkan.h>
38
39#include <vulkan/vk_platform.h>
40#include <vulkan/vulkan_core.h>
41
42#ifndef VK_API_VERSION_MAJOR
43#define VK_API_VERSION_MAJOR VK_VERSION_MAJOR
44#define VK_API_VERSION_MINOR VK_VERSION_MINOR
45#define VK_API_VERSION_PATCH VK_VERSION_PATCH
46#endif
47
48using namespace std::chrono_literals;
49
50class CCommandProcessorFragment_Vulkan : public CCommandProcessorFragment_GLBase
51{
52 enum EMemoryBlockUsage
53 {
54 MEMORY_BLOCK_USAGE_TEXTURE = 0,
55 MEMORY_BLOCK_USAGE_BUFFER,
56 MEMORY_BLOCK_USAGE_STREAM,
57 MEMORY_BLOCK_USAGE_STAGING,
58
59 // whenever dummy is used, make sure to deallocate all memory
60 MEMORY_BLOCK_USAGE_DUMMY,
61 };
62
63 [[nodiscard]] bool IsVerbose()
64 {
65 return g_Config.m_DbgGfx == DEBUG_GFX_MODE_VERBOSE || g_Config.m_DbgGfx == DEBUG_GFX_MODE_ALL;
66 }
67
68 void VerboseAllocatedMemory(VkDeviceSize Size, size_t FrameImageIndex, EMemoryBlockUsage MemUsage) const
69 {
70 const char *pUsage = "unknown";
71 switch(MemUsage)
72 {
73 case MEMORY_BLOCK_USAGE_TEXTURE:
74 pUsage = "texture";
75 break;
76 case MEMORY_BLOCK_USAGE_BUFFER:
77 pUsage = "buffer";
78 break;
79 case MEMORY_BLOCK_USAGE_STREAM:
80 pUsage = "stream";
81 break;
82 case MEMORY_BLOCK_USAGE_STAGING:
83 pUsage = "staging buffer";
84 break;
85 default: break;
86 }
87 dbg_msg(sys: "vulkan", fmt: "allocated chunk of memory with size: %" PRIzu " for frame %" PRIzu " (%s)", (size_t)Size, (size_t)m_CurImageIndex, pUsage);
88 }
89
90 void VerboseDeallocatedMemory(VkDeviceSize Size, size_t FrameImageIndex, EMemoryBlockUsage MemUsage) const
91 {
92 const char *pUsage = "unknown";
93 switch(MemUsage)
94 {
95 case MEMORY_BLOCK_USAGE_TEXTURE:
96 pUsage = "texture";
97 break;
98 case MEMORY_BLOCK_USAGE_BUFFER:
99 pUsage = "buffer";
100 break;
101 case MEMORY_BLOCK_USAGE_STREAM:
102 pUsage = "stream";
103 break;
104 case MEMORY_BLOCK_USAGE_STAGING:
105 pUsage = "staging buffer";
106 break;
107 default: break;
108 }
109 dbg_msg(sys: "vulkan", fmt: "deallocated chunk of memory with size: %" PRIzu " for frame %" PRIzu " (%s)", (size_t)Size, (size_t)m_CurImageIndex, pUsage);
110 }
111
112 /************************
113 * STRUCT DEFINITIONS
114 ************************/
115
116 static constexpr size_t s_StagingBufferCacheId = 0;
117 static constexpr size_t s_StagingBufferImageCacheId = 1;
118 static constexpr size_t s_VertexBufferCacheId = 2;
119 static constexpr size_t s_ImageBufferCacheId = 3;
120
121 struct SDeviceMemoryBlock
122 {
123 VkDeviceMemory m_Mem = VK_NULL_HANDLE;
124 VkDeviceSize m_Size = 0;
125 EMemoryBlockUsage m_UsageType;
126 };
127
128 struct SDeviceDescriptorPools;
129
130 struct SDeviceDescriptorSet
131 {
132 VkDescriptorSet m_Descriptor = VK_NULL_HANDLE;
133 SDeviceDescriptorPools *m_pPools = nullptr;
134 size_t m_PoolIndex = std::numeric_limits<size_t>::max();
135 };
136
137 struct SDeviceDescriptorPool
138 {
139 VkDescriptorPool m_Pool;
140 VkDeviceSize m_Size = 0;
141 VkDeviceSize m_CurSize = 0;
142 };
143
144 struct SDeviceDescriptorPools
145 {
146 std::vector<SDeviceDescriptorPool> m_vPools;
147 VkDeviceSize m_DefaultAllocSize = 0;
148 bool m_IsUniformPool = false;
149 };
150
151 // some mix of queue and binary tree
152 struct SMemoryHeap
153 {
154 struct SMemoryHeapElement;
155 struct SMemoryHeapQueueElement
156 {
157 size_t m_AllocationSize;
158 // only useful information for the heap
159 size_t m_OffsetInHeap;
160 // useful for the user of this element
161 size_t m_OffsetToAlign;
162 SMemoryHeapElement *m_pElementInHeap;
163 [[nodiscard]] bool operator>(const SMemoryHeapQueueElement &Other) const { return m_AllocationSize > Other.m_AllocationSize; }
164 struct SMemoryHeapQueueElementFind
165 {
166 // respects alignment requirements
167 constexpr bool operator()(const SMemoryHeapQueueElement &Val, const std::pair<size_t, size_t> &Other) const
168 {
169 auto AllocSize = Other.first;
170 auto AllocAlignment = Other.second;
171 size_t ExtraSizeAlign = Val.m_OffsetInHeap % AllocAlignment;
172 if(ExtraSizeAlign != 0)
173 ExtraSizeAlign = AllocAlignment - ExtraSizeAlign;
174 size_t RealAllocSize = AllocSize + ExtraSizeAlign;
175 return Val.m_AllocationSize < RealAllocSize;
176 }
177 };
178 };
179
180 typedef std::multiset<SMemoryHeapQueueElement, std::greater<>> TMemoryHeapQueue;
181
182 struct SMemoryHeapElement
183 {
184 size_t m_AllocationSize;
185 size_t m_Offset;
186 SMemoryHeapElement *m_pParent;
187 std::unique_ptr<SMemoryHeapElement> m_pLeft;
188 std::unique_ptr<SMemoryHeapElement> m_pRight;
189
190 bool m_InUse;
191 TMemoryHeapQueue::iterator m_InQueue;
192 };
193
194 SMemoryHeapElement m_Root;
195 TMemoryHeapQueue m_Elements;
196
197 void Init(size_t Size, size_t Offset)
198 {
199 m_Root.m_AllocationSize = Size;
200 m_Root.m_Offset = Offset;
201 m_Root.m_pParent = nullptr;
202 m_Root.m_InUse = false;
203
204 SMemoryHeapQueueElement QueueEl;
205 QueueEl.m_AllocationSize = Size;
206 QueueEl.m_OffsetInHeap = Offset;
207 QueueEl.m_OffsetToAlign = Offset;
208 QueueEl.m_pElementInHeap = &m_Root;
209 m_Root.m_InQueue = m_Elements.insert(x: QueueEl);
210 }
211
212 [[nodiscard]] bool Allocate(size_t AllocSize, size_t AllocAlignment, SMemoryHeapQueueElement &AllocatedMemory)
213 {
214 if(m_Elements.empty())
215 {
216 return false;
217 }
218 else
219 {
220 // check if there is enough space in this instance
221 if(bool(SMemoryHeapQueueElement::SMemoryHeapQueueElementFind{}(*m_Elements.begin(), std::make_pair(x&: AllocSize, y&: AllocAlignment))))
222 {
223 return false;
224 }
225 else
226 {
227 // see SMemoryHeapQueueElement::operator>
228 SMemoryHeapQueueElement FindAllocSize;
229 FindAllocSize.m_AllocationSize = AllocSize;
230 // find upper bound for a allocation size
231 auto Upper = m_Elements.upper_bound(x: FindAllocSize);
232 // then find the first entry that respects alignment, this is a linear search!
233 auto FoundEl = std::lower_bound(first: std::make_reverse_iterator(i: Upper), last: m_Elements.rend(), val: std::make_pair(x&: AllocSize, y&: AllocAlignment), comp: SMemoryHeapQueueElement::SMemoryHeapQueueElementFind{});
234
235 auto TopEl = *FoundEl;
236 m_Elements.erase(position: TopEl.m_pElementInHeap->m_InQueue);
237
238 TopEl.m_pElementInHeap->m_InUse = true;
239
240 // calculate the real alloc size + alignment offset
241 size_t ExtraSizeAlign = TopEl.m_OffsetInHeap % AllocAlignment;
242 if(ExtraSizeAlign != 0)
243 ExtraSizeAlign = AllocAlignment - ExtraSizeAlign;
244 size_t RealAllocSize = AllocSize + ExtraSizeAlign;
245
246 // the heap element gets children
247 TopEl.m_pElementInHeap->m_pLeft = std::make_unique<SMemoryHeapElement>();
248 TopEl.m_pElementInHeap->m_pLeft->m_AllocationSize = RealAllocSize;
249 TopEl.m_pElementInHeap->m_pLeft->m_Offset = TopEl.m_OffsetInHeap;
250 TopEl.m_pElementInHeap->m_pLeft->m_pParent = TopEl.m_pElementInHeap;
251 TopEl.m_pElementInHeap->m_pLeft->m_InUse = true;
252
253 if(RealAllocSize < TopEl.m_AllocationSize)
254 {
255 SMemoryHeapQueueElement RemainingEl;
256 RemainingEl.m_OffsetInHeap = TopEl.m_OffsetInHeap + RealAllocSize;
257 RemainingEl.m_AllocationSize = TopEl.m_AllocationSize - RealAllocSize;
258
259 TopEl.m_pElementInHeap->m_pRight = std::make_unique<SMemoryHeapElement>();
260 TopEl.m_pElementInHeap->m_pRight->m_AllocationSize = RemainingEl.m_AllocationSize;
261 TopEl.m_pElementInHeap->m_pRight->m_Offset = RemainingEl.m_OffsetInHeap;
262 TopEl.m_pElementInHeap->m_pRight->m_pParent = TopEl.m_pElementInHeap;
263 TopEl.m_pElementInHeap->m_pRight->m_InUse = false;
264
265 RemainingEl.m_pElementInHeap = TopEl.m_pElementInHeap->m_pRight.get();
266 RemainingEl.m_pElementInHeap->m_InQueue = m_Elements.insert(x: RemainingEl);
267 }
268
269 AllocatedMemory.m_pElementInHeap = TopEl.m_pElementInHeap->m_pLeft.get();
270 AllocatedMemory.m_AllocationSize = RealAllocSize;
271 AllocatedMemory.m_OffsetInHeap = TopEl.m_OffsetInHeap;
272 AllocatedMemory.m_OffsetToAlign = TopEl.m_OffsetInHeap + ExtraSizeAlign;
273 return true;
274 }
275 }
276 }
277
278 void Free(const SMemoryHeapQueueElement &AllocatedMemory)
279 {
280 bool ContinueFree = true;
281 SMemoryHeapQueueElement ThisEl = AllocatedMemory;
282 while(ContinueFree)
283 {
284 // first check if the other block is in use, if not merge them again
285 SMemoryHeapElement *pThisHeapObj = ThisEl.m_pElementInHeap;
286 SMemoryHeapElement *pThisParent = pThisHeapObj->m_pParent;
287 pThisHeapObj->m_InUse = false;
288 SMemoryHeapElement *pOtherHeapObj = nullptr;
289 if(pThisParent != nullptr && pThisHeapObj == pThisParent->m_pLeft.get())
290 pOtherHeapObj = pThisHeapObj->m_pParent->m_pRight.get();
291 else if(pThisParent != nullptr)
292 pOtherHeapObj = pThisHeapObj->m_pParent->m_pLeft.get();
293
294 if((pThisParent != nullptr && pOtherHeapObj == nullptr) || (pOtherHeapObj != nullptr && !pOtherHeapObj->m_InUse))
295 {
296 // merge them
297 if(pOtherHeapObj != nullptr)
298 {
299 m_Elements.erase(position: pOtherHeapObj->m_InQueue);
300 pOtherHeapObj->m_InUse = false;
301 }
302
303 SMemoryHeapQueueElement ParentEl;
304 ParentEl.m_OffsetInHeap = pThisParent->m_Offset;
305 ParentEl.m_AllocationSize = pThisParent->m_AllocationSize;
306 ParentEl.m_pElementInHeap = pThisParent;
307
308 pThisParent->m_pLeft = nullptr;
309 pThisParent->m_pRight = nullptr;
310
311 ThisEl = ParentEl;
312 }
313 else
314 {
315 // else just put this back into queue
316 ThisEl.m_pElementInHeap->m_InQueue = m_Elements.insert(x: ThisEl);
317 ContinueFree = false;
318 }
319 }
320 }
321
322 [[nodiscard]] bool IsUnused() const
323 {
324 return !m_Root.m_InUse;
325 }
326 };
327
328 template<size_t Id>
329 struct SMemoryBlock
330 {
331 SMemoryHeap::SMemoryHeapQueueElement m_HeapData;
332
333 VkDeviceSize m_UsedSize;
334
335 // optional
336 VkBuffer m_Buffer;
337
338 SDeviceMemoryBlock m_BufferMem;
339 void *m_pMappedBuffer;
340
341 bool m_IsCached;
342 SMemoryHeap *m_pHeap;
343 };
344
345 template<size_t Id>
346 struct SMemoryImageBlock : public SMemoryBlock<Id>
347 {
348 uint32_t m_ImageMemoryBits;
349 };
350
351 template<size_t Id>
352 struct SMemoryBlockCache
353 {
354 struct SMemoryCacheType
355 {
356 struct SMemoryCacheHeap
357 {
358 SMemoryHeap m_Heap;
359 VkBuffer m_Buffer;
360
361 SDeviceMemoryBlock m_BufferMem;
362 void *m_pMappedBuffer;
363 };
364 std::vector<SMemoryCacheHeap *> m_vpMemoryHeaps;
365 };
366 SMemoryCacheType m_MemoryCaches;
367 std::vector<std::vector<SMemoryBlock<Id>>> m_vvFrameDelayedCachedBufferCleanup;
368
369 bool m_CanShrink = false;
370
371 void Init(size_t SwapChainImageCount)
372 {
373 m_vvFrameDelayedCachedBufferCleanup.resize(SwapChainImageCount);
374 }
375
376 void DestroyFrameData(size_t ImageCount)
377 {
378 for(size_t i = 0; i < ImageCount; ++i)
379 Cleanup(ImgIndex: i);
380 m_vvFrameDelayedCachedBufferCleanup.clear();
381 }
382
383 void Destroy(VkDevice &Device)
384 {
385 for(auto it = m_MemoryCaches.m_vpMemoryHeaps.begin(); it != m_MemoryCaches.m_vpMemoryHeaps.end();)
386 {
387 auto *pHeap = *it;
388 if(pHeap->m_pMappedBuffer != nullptr)
389 vkUnmapMemory(Device, pHeap->m_BufferMem.m_Mem);
390 if(pHeap->m_Buffer != VK_NULL_HANDLE)
391 vkDestroyBuffer(Device, pHeap->m_Buffer, nullptr);
392 vkFreeMemory(Device, pHeap->m_BufferMem.m_Mem, nullptr);
393
394 delete pHeap;
395 it = m_MemoryCaches.m_vpMemoryHeaps.erase(it);
396 }
397
398 m_MemoryCaches.m_vpMemoryHeaps.clear();
399 m_vvFrameDelayedCachedBufferCleanup.clear();
400 }
401
402 void Cleanup(size_t ImgIndex)
403 {
404 for(auto &MemBlock : m_vvFrameDelayedCachedBufferCleanup[ImgIndex])
405 {
406 MemBlock.m_UsedSize = 0;
407 MemBlock.m_pHeap->Free(MemBlock.m_HeapData);
408
409 m_CanShrink = true;
410 }
411 m_vvFrameDelayedCachedBufferCleanup[ImgIndex].clear();
412 }
413
414 void FreeMemBlock(SMemoryBlock<Id> &Block, size_t ImgIndex)
415 {
416 m_vvFrameDelayedCachedBufferCleanup[ImgIndex].push_back(Block);
417 }
418
419 // returns the total free'd memory
420 size_t Shrink(VkDevice &Device)
421 {
422 size_t FreeedMemory = 0;
423 if(m_CanShrink)
424 {
425 m_CanShrink = false;
426 if(m_MemoryCaches.m_vpMemoryHeaps.size() > 1)
427 {
428 for(auto it = m_MemoryCaches.m_vpMemoryHeaps.begin(); it != m_MemoryCaches.m_vpMemoryHeaps.end();)
429 {
430 auto *pHeap = *it;
431 if(pHeap->m_Heap.IsUnused())
432 {
433 if(pHeap->m_pMappedBuffer != nullptr)
434 vkUnmapMemory(Device, pHeap->m_BufferMem.m_Mem);
435 if(pHeap->m_Buffer != VK_NULL_HANDLE)
436 vkDestroyBuffer(Device, pHeap->m_Buffer, nullptr);
437 vkFreeMemory(Device, pHeap->m_BufferMem.m_Mem, nullptr);
438 FreeedMemory += pHeap->m_BufferMem.m_Size;
439
440 delete pHeap;
441 it = m_MemoryCaches.m_vpMemoryHeaps.erase(it);
442 if(m_MemoryCaches.m_vpMemoryHeaps.size() == 1)
443 break;
444 }
445 else
446 ++it;
447 }
448 }
449 }
450
451 return FreeedMemory;
452 }
453 };
454
455 struct CTexture
456 {
457 VkImage m_Img = VK_NULL_HANDLE;
458 SMemoryImageBlock<s_ImageBufferCacheId> m_ImgMem;
459 VkImageView m_ImgView = VK_NULL_HANDLE;
460 VkSampler m_aSamplers[2] = {VK_NULL_HANDLE, VK_NULL_HANDLE};
461
462 VkImage m_Img3D = VK_NULL_HANDLE;
463 SMemoryImageBlock<s_ImageBufferCacheId> m_Img3DMem;
464 VkImageView m_Img3DView = VK_NULL_HANDLE;
465 VkSampler m_Sampler3D = VK_NULL_HANDLE;
466
467 uint32_t m_Width = 0;
468 uint32_t m_Height = 0;
469 uint32_t m_RescaleCount = 0;
470
471 uint32_t m_MipMapCount = 1;
472
473 std::array<SDeviceDescriptorSet, 2> m_aVKStandardTexturedDescrSets;
474 SDeviceDescriptorSet m_VKStandard3DTexturedDescrSet;
475 SDeviceDescriptorSet m_VKTextDescrSet;
476 };
477
478 struct SBufferObject
479 {
480 SMemoryBlock<s_VertexBufferCacheId> m_Mem;
481 };
482
483 struct SBufferObjectFrame
484 {
485 SBufferObject m_BufferObject;
486
487 // since stream buffers can be used the cur buffer should always be used for rendering
488 bool m_IsStreamedBuffer = false;
489 VkBuffer m_CurBuffer = VK_NULL_HANDLE;
490 size_t m_CurBufferOffset = 0;
491 };
492
493 struct SBufferContainer
494 {
495 int m_BufferObjectIndex;
496 };
497
498 struct SFrameBuffers
499 {
500 VkBuffer m_Buffer;
501 SDeviceMemoryBlock m_BufferMem;
502 size_t m_OffsetInBuffer = 0;
503 size_t m_Size;
504 size_t m_UsedSize;
505 uint8_t *m_pMappedBufferData;
506
507 SFrameBuffers(VkBuffer Buffer, SDeviceMemoryBlock BufferMem, size_t OffsetInBuffer, size_t Size, size_t UsedSize, uint8_t *pMappedBufferData) :
508 m_Buffer(Buffer), m_BufferMem(BufferMem), m_OffsetInBuffer(OffsetInBuffer), m_Size(Size), m_UsedSize(UsedSize), m_pMappedBufferData(pMappedBufferData)
509 {
510 }
511 };
512
513 struct SFrameUniformBuffers : public SFrameBuffers
514 {
515 std::array<SDeviceDescriptorSet, 2> m_aUniformSets;
516
517 SFrameUniformBuffers(VkBuffer Buffer, SDeviceMemoryBlock BufferMem, size_t OffsetInBuffer, size_t Size, size_t UsedSize, uint8_t *pMappedBufferData) :
518 SFrameBuffers(Buffer, BufferMem, OffsetInBuffer, Size, UsedSize, pMappedBufferData) {}
519 };
520
521 template<typename TName>
522 struct SStreamMemory
523 {
524 typedef std::vector<std::vector<TName>> TBufferObjectsOfFrame;
525 typedef std::vector<std::vector<VkMappedMemoryRange>> TMemoryMapRangesOfFrame;
526 typedef std::vector<size_t> TStreamUseCount;
527 TBufferObjectsOfFrame m_vvBufferObjectsOfFrame;
528 TMemoryMapRangesOfFrame m_vvBufferObjectsOfFrameRangeData;
529 TStreamUseCount m_vCurrentUsedCount;
530
531 std::vector<TName> &GetBuffers(size_t FrameImageIndex)
532 {
533 return m_vvBufferObjectsOfFrame[FrameImageIndex];
534 }
535
536 std::vector<VkMappedMemoryRange> &GetRanges(size_t FrameImageIndex)
537 {
538 return m_vvBufferObjectsOfFrameRangeData[FrameImageIndex];
539 }
540
541 size_t GetUsedCount(size_t FrameImageIndex)
542 {
543 return m_vCurrentUsedCount[FrameImageIndex];
544 }
545
546 void IncreaseUsedCount(size_t FrameImageIndex)
547 {
548 ++m_vCurrentUsedCount[FrameImageIndex];
549 }
550
551 [[nodiscard]] bool IsUsed(size_t FrameImageIndex)
552 {
553 return GetUsedCount(FrameImageIndex) > 0;
554 }
555
556 void ResetFrame(size_t FrameImageIndex)
557 {
558 m_vCurrentUsedCount[FrameImageIndex] = 0;
559 }
560
561 void Init(size_t FrameImageCount)
562 {
563 m_vvBufferObjectsOfFrame.resize(FrameImageCount);
564 m_vvBufferObjectsOfFrameRangeData.resize(new_size: FrameImageCount);
565 m_vCurrentUsedCount.resize(new_size: FrameImageCount);
566 }
567
568 typedef std::function<void(size_t, TName &)> TDestroyBufferFunc;
569
570 void Destroy(TDestroyBufferFunc &&DestroyBuffer)
571 {
572 size_t ImageIndex = 0;
573 for(auto &vBuffersOfFrame : m_vvBufferObjectsOfFrame)
574 {
575 for(auto &BufferOfFrame : vBuffersOfFrame)
576 {
577 VkDeviceMemory BufferMem = BufferOfFrame.m_BufferMem.m_Mem;
578 DestroyBuffer(ImageIndex, BufferOfFrame);
579
580 // delete similar buffers
581 for(auto &BufferOfFrameDel : vBuffersOfFrame)
582 {
583 if(BufferOfFrameDel.m_BufferMem.m_Mem == BufferMem)
584 {
585 BufferOfFrameDel.m_Buffer = VK_NULL_HANDLE;
586 BufferOfFrameDel.m_BufferMem.m_Mem = VK_NULL_HANDLE;
587 }
588 }
589 }
590 ++ImageIndex;
591 }
592 m_vvBufferObjectsOfFrame.clear();
593 m_vvBufferObjectsOfFrameRangeData.clear();
594 m_vCurrentUsedCount.clear();
595 }
596 };
597
598 struct SShaderModule
599 {
600 VkShaderModule m_VertShaderModule = VK_NULL_HANDLE;
601 VkShaderModule m_FragShaderModule = VK_NULL_HANDLE;
602
603 VkDevice m_VKDevice = VK_NULL_HANDLE;
604
605 ~SShaderModule()
606 {
607 if(m_VKDevice != VK_NULL_HANDLE)
608 {
609 if(m_VertShaderModule != VK_NULL_HANDLE)
610 vkDestroyShaderModule(device: m_VKDevice, shaderModule: m_VertShaderModule, pAllocator: nullptr);
611
612 if(m_FragShaderModule != VK_NULL_HANDLE)
613 vkDestroyShaderModule(device: m_VKDevice, shaderModule: m_FragShaderModule, pAllocator: nullptr);
614 }
615 }
616 };
617
618 enum EVulkanBackendAddressModes
619 {
620 VULKAN_BACKEND_ADDRESS_MODE_REPEAT = 0,
621 VULKAN_BACKEND_ADDRESS_MODE_CLAMP_EDGES,
622
623 VULKAN_BACKEND_ADDRESS_MODE_COUNT,
624 };
625
626 enum EVulkanBackendBlendModes
627 {
628 VULKAN_BACKEND_BLEND_MODE_ALPHA = 0,
629 VULKAN_BACKEND_BLEND_MODE_NONE,
630 VULKAN_BACKEND_BLEND_MODE_ADDITATIVE,
631
632 VULKAN_BACKEND_BLEND_MODE_COUNT,
633 };
634
635 enum EVulkanBackendClipModes
636 {
637 VULKAN_BACKEND_CLIP_MODE_NONE = 0,
638 VULKAN_BACKEND_CLIP_MODE_DYNAMIC_SCISSOR_AND_VIEWPORT,
639
640 VULKAN_BACKEND_CLIP_MODE_COUNT,
641 };
642
643 enum EVulkanBackendTextureModes
644 {
645 VULKAN_BACKEND_TEXTURE_MODE_NOT_TEXTURED = 0,
646 VULKAN_BACKEND_TEXTURE_MODE_TEXTURED,
647
648 VULKAN_BACKEND_TEXTURE_MODE_COUNT,
649 };
650
651 struct SPipelineContainer
652 {
653 // 3 blend modes - 2 viewport & scissor modes - 2 texture modes
654 std::array<std::array<std::array<VkPipelineLayout, VULKAN_BACKEND_TEXTURE_MODE_COUNT>, VULKAN_BACKEND_CLIP_MODE_COUNT>, VULKAN_BACKEND_BLEND_MODE_COUNT> m_aaaPipelineLayouts;
655 std::array<std::array<std::array<VkPipeline, VULKAN_BACKEND_TEXTURE_MODE_COUNT>, VULKAN_BACKEND_CLIP_MODE_COUNT>, VULKAN_BACKEND_BLEND_MODE_COUNT> m_aaaPipelines;
656
657 SPipelineContainer()
658 {
659 for(auto &aaPipeLayouts : m_aaaPipelineLayouts)
660 {
661 for(auto &aPipeLayouts : aaPipeLayouts)
662 {
663 for(auto &PipeLayout : aPipeLayouts)
664 {
665 PipeLayout = VK_NULL_HANDLE;
666 }
667 }
668 }
669 for(auto &aaPipe : m_aaaPipelines)
670 {
671 for(auto &aPipe : aaPipe)
672 {
673 for(auto &Pipe : aPipe)
674 {
675 Pipe = VK_NULL_HANDLE;
676 }
677 }
678 }
679 }
680
681 void Destroy(VkDevice &Device)
682 {
683 for(auto &aaPipeLayouts : m_aaaPipelineLayouts)
684 {
685 for(auto &aPipeLayouts : aaPipeLayouts)
686 {
687 for(auto &PipeLayout : aPipeLayouts)
688 {
689 if(PipeLayout != VK_NULL_HANDLE)
690 vkDestroyPipelineLayout(device: Device, pipelineLayout: PipeLayout, pAllocator: nullptr);
691 PipeLayout = VK_NULL_HANDLE;
692 }
693 }
694 }
695 for(auto &aaPipe : m_aaaPipelines)
696 {
697 for(auto &aPipe : aaPipe)
698 {
699 for(auto &Pipe : aPipe)
700 {
701 if(Pipe != VK_NULL_HANDLE)
702 vkDestroyPipeline(device: Device, pipeline: Pipe, pAllocator: nullptr);
703 Pipe = VK_NULL_HANDLE;
704 }
705 }
706 }
707 }
708 };
709
710 /*******************************
711 * UNIFORM PUSH CONSTANT LAYOUTS
712 ********************************/
713
714 struct SUniformGPos
715 {
716 float m_aPos[4 * 2];
717 };
718
719 struct SUniformGTextPos
720 {
721 float m_aPos[4 * 2];
722 float m_TextureSize;
723 };
724
725 typedef vec3 SUniformTextGFragmentOffset;
726
727 struct SUniformTextGFragmentConstants
728 {
729 ColorRGBA m_TextColor;
730 ColorRGBA m_TextOutlineColor;
731 };
732
733 struct SUniformTextFragment
734 {
735 SUniformTextGFragmentConstants m_Constants;
736 };
737
738 struct SUniformTileGPos
739 {
740 float m_aPos[4 * 2];
741 };
742
743 struct SUniformTileGPosBorder : public SUniformTileGPos
744 {
745 vec2 m_Offset;
746 vec2 m_Scale;
747 };
748
749 typedef ColorRGBA SUniformTileGVertColor;
750
751 struct SUniformTileGVertColorAlign
752 {
753 float m_aPad[(64 - 48) / 4];
754 };
755
756 struct SUniformPrimExGPosRotationless
757 {
758 float m_aPos[4 * 2];
759 };
760
761 struct SUniformPrimExGPos : public SUniformPrimExGPosRotationless
762 {
763 vec2 m_Center;
764 float m_Rotation;
765 };
766
767 typedef ColorRGBA SUniformPrimExGVertColor;
768
769 struct SUniformPrimExGVertColorAlign
770 {
771 float m_aPad[(48 - 44) / 4];
772 };
773
774 struct SUniformSpriteMultiGPos
775 {
776 float m_aPos[4 * 2];
777 vec2 m_Center;
778 };
779
780 typedef ColorRGBA SUniformSpriteMultiGVertColor;
781
782 struct SUniformSpriteMultiGVertColorAlign
783 {
784 float m_aPad[(48 - 40) / 4];
785 };
786
787 struct SUniformSpriteMultiPushGPosBase
788 {
789 float m_aPos[4 * 2];
790 vec2 m_Center;
791 vec2 m_Padding;
792 };
793
794 struct SUniformSpriteMultiPushGPos : public SUniformSpriteMultiPushGPosBase
795 {
796 vec4 m_aPSR[1];
797 };
798
799 typedef ColorRGBA SUniformSpriteMultiPushGVertColor;
800
801 struct SUniformQuadGPosBase
802 {
803 float m_aPos[4 * 2];
804 int32_t m_QuadOffset;
805 };
806
807 struct SUniformQuadPushGBufferObject
808 {
809 vec4 m_VertColor;
810 vec2 m_Offset;
811 float m_Rotation;
812 float m_Padding;
813 };
814
815 struct SUniformQuadPushGPos
816 {
817 float m_aPos[4 * 2];
818 SUniformQuadPushGBufferObject m_BOPush;
819 int32_t m_QuadOffset;
820 };
821
822 struct SUniformQuadGPos
823 {
824 float m_aPos[4 * 2];
825 int32_t m_QuadOffset;
826 };
827
828 enum ESupportedSamplerTypes
829 {
830 SUPPORTED_SAMPLER_TYPE_REPEAT = 0,
831 SUPPORTED_SAMPLER_TYPE_CLAMP_TO_EDGE,
832 SUPPORTED_SAMPLER_TYPE_2D_TEXTURE_ARRAY,
833
834 SUPPORTED_SAMPLER_TYPE_COUNT,
835 };
836
837 struct SShaderFileCache
838 {
839 std::vector<uint8_t> m_vBinary;
840 };
841
842 struct SSwapImgViewportExtent
843 {
844 VkExtent2D m_SwapImageViewport;
845 bool m_HasForcedViewport = false;
846 VkExtent2D m_ForcedViewport;
847
848 // the viewport of the resulting presented image on the screen
849 // if there is a forced viewport the resulting image is smaller
850 // than the full swap image size
851 VkExtent2D GetPresentedImageViewport() const
852 {
853 uint32_t ViewportWidth = m_SwapImageViewport.width;
854 uint32_t ViewportHeight = m_SwapImageViewport.height;
855 if(m_HasForcedViewport)
856 {
857 ViewportWidth = m_ForcedViewport.width;
858 ViewportHeight = m_ForcedViewport.height;
859 }
860
861 return {.width: ViewportWidth, .height: ViewportHeight};
862 }
863 };
864
865 struct SSwapChainMultiSampleImage
866 {
867 VkImage m_Image = VK_NULL_HANDLE;
868 SMemoryImageBlock<s_ImageBufferCacheId> m_ImgMem;
869 VkImageView m_ImgView = VK_NULL_HANDLE;
870 };
871
872 /************************
873 * MEMBER VARIABLES
874 ************************/
875
876 std::unordered_map<std::string, SShaderFileCache> m_ShaderFiles;
877
878 SMemoryBlockCache<s_StagingBufferCacheId> m_StagingBufferCache;
879 SMemoryBlockCache<s_StagingBufferImageCacheId> m_StagingBufferCacheImage;
880 SMemoryBlockCache<s_VertexBufferCacheId> m_VertexBufferCache;
881 std::map<uint32_t, SMemoryBlockCache<s_ImageBufferCacheId>> m_ImageBufferCaches;
882
883 std::vector<VkMappedMemoryRange> m_vNonFlushedStagingBufferRange;
884
885 std::vector<CTexture> m_vTextures;
886
887 std::atomic<uint64_t> *m_pTextureMemoryUsage;
888 std::atomic<uint64_t> *m_pBufferMemoryUsage;
889 std::atomic<uint64_t> *m_pStreamMemoryUsage;
890 std::atomic<uint64_t> *m_pStagingMemoryUsage;
891
892 TTwGraphicsGpuList *m_pGpuList;
893
894 int m_GlobalTextureLodBIAS;
895 uint32_t m_MultiSamplingCount = 1;
896
897 uint32_t m_NextMultiSamplingCount = std::numeric_limits<uint32_t>::max();
898
899 bool m_RecreateSwapChain = false;
900 bool m_SwapchainCreated = false;
901 bool m_RenderingPaused = false;
902 bool m_HasDynamicViewport = false;
903 VkOffset2D m_DynamicViewportOffset;
904 VkExtent2D m_DynamicViewportSize;
905
906 bool m_AllowsLinearBlitting = false;
907 bool m_OptimalSwapChainImageBlitting = false;
908 bool m_OptimalRGBAImageBlitting = false;
909 bool m_LinearRGBAImageBlitting = false;
910
911 VkBuffer m_IndexBuffer;
912 SDeviceMemoryBlock m_IndexBufferMemory;
913
914 VkBuffer m_RenderIndexBuffer;
915 SDeviceMemoryBlock m_RenderIndexBufferMemory;
916 size_t m_CurRenderIndexPrimitiveCount;
917
918 VkDeviceSize m_NonCoherentMemAlignment;
919 VkDeviceSize m_OptimalImageCopyMemAlignment;
920 uint32_t m_MaxTextureSize;
921 uint32_t m_MaxSamplerAnisotropy;
922 VkSampleCountFlags m_MaxMultiSample;
923
924 uint32_t m_MinUniformAlign;
925
926 std::vector<uint8_t> m_vReadPixelHelper;
927 std::vector<uint8_t> m_vScreenshotHelper;
928
929 SDeviceMemoryBlock m_GetPresentedImgDataHelperMem;
930 VkImage m_GetPresentedImgDataHelperImage = VK_NULL_HANDLE;
931 uint8_t *m_pGetPresentedImgDataHelperMappedMemory = nullptr;
932 VkDeviceSize m_GetPresentedImgDataHelperMappedLayoutOffset = 0;
933 VkDeviceSize m_GetPresentedImgDataHelperMappedLayoutPitch = 0;
934 uint32_t m_GetPresentedImgDataHelperWidth = 0;
935 uint32_t m_GetPresentedImgDataHelperHeight = 0;
936 VkFence m_GetPresentedImgDataHelperFence = VK_NULL_HANDLE;
937
938 std::array<VkSampler, SUPPORTED_SAMPLER_TYPE_COUNT> m_aSamplers;
939
940 class IStorage *m_pStorage;
941
942 struct SDelayedBufferCleanupItem
943 {
944 VkBuffer m_Buffer;
945 SDeviceMemoryBlock m_Mem;
946 void *m_pMappedData = nullptr;
947 };
948
949 std::vector<std::vector<SDelayedBufferCleanupItem>> m_vvFrameDelayedBufferCleanup;
950 std::vector<std::vector<CTexture>> m_vvFrameDelayedTextureCleanup;
951 std::vector<std::vector<std::pair<CTexture, CTexture>>> m_vvFrameDelayedTextTexturesCleanup;
952
953 size_t m_ThreadCount = 1;
954 static constexpr size_t ms_MainThreadIndex = 0;
955 size_t m_CurCommandInPipe = 0;
956 size_t m_CurRenderCallCountInPipe = 0;
957 size_t m_CommandsInPipe = 0;
958 size_t m_RenderCallsInPipe = 0;
959 size_t m_LastCommandsInPipeThreadIndex = 0;
960
961 struct SRenderThread
962 {
963 bool m_IsRendering = false;
964 std::thread m_Thread;
965 std::mutex m_Mutex;
966 std::condition_variable m_Cond;
967 bool m_Finished = false;
968 bool m_Started = false;
969 };
970 std::vector<std::unique_ptr<SRenderThread>> m_vpRenderThreads;
971
972private:
973 std::vector<VkImageView> m_vSwapChainImageViewList;
974 std::vector<SSwapChainMultiSampleImage> m_vSwapChainMultiSamplingImages;
975 std::vector<VkFramebuffer> m_vFramebufferList;
976 std::vector<VkCommandBuffer> m_vMainDrawCommandBuffers;
977
978 std::vector<std::vector<VkCommandBuffer>> m_vvThreadDrawCommandBuffers;
979 std::vector<VkCommandBuffer> m_vHelperThreadDrawCommandBuffers;
980 std::vector<std::vector<bool>> m_vvUsedThreadDrawCommandBuffer;
981
982 std::vector<VkCommandBuffer> m_vMemoryCommandBuffers;
983 std::vector<bool> m_vUsedMemoryCommandBuffer;
984
985 // swapped by use case
986 std::vector<VkSemaphore> m_vWaitSemaphores;
987 std::vector<VkSemaphore> m_vSigSemaphores;
988
989 std::vector<VkFence> m_vFrameFences;
990 std::vector<VkFence> m_vImagesFences;
991
992 uint64_t m_CurFrame = 0;
993 std::vector<uint64_t> m_vImageLastFrameCheck;
994
995 uint32_t m_LastPresentedSwapChainImageIndex;
996
997 std::vector<SBufferObjectFrame> m_vBufferObjects;
998
999 std::vector<SBufferContainer> m_vBufferContainers;
1000
1001 VkInstance m_VKInstance;
1002 VkPhysicalDevice m_VKGPU;
1003 uint32_t m_VKGraphicsQueueIndex = std::numeric_limits<uint32_t>::max();
1004 VkDevice m_VKDevice;
1005 VkQueue m_VKGraphicsQueue, m_VKPresentQueue;
1006 VkSurfaceKHR m_VKPresentSurface;
1007 SSwapImgViewportExtent m_VKSwapImgAndViewportExtent;
1008
1009#ifdef VK_EXT_debug_utils
1010 VkDebugUtilsMessengerEXT m_DebugMessenger;
1011#endif
1012
1013 VkDescriptorSetLayout m_StandardTexturedDescriptorSetLayout;
1014 VkDescriptorSetLayout m_Standard3DTexturedDescriptorSetLayout;
1015
1016 VkDescriptorSetLayout m_TextDescriptorSetLayout;
1017
1018 VkDescriptorSetLayout m_SpriteMultiUniformDescriptorSetLayout;
1019 VkDescriptorSetLayout m_QuadUniformDescriptorSetLayout;
1020
1021 SPipelineContainer m_StandardPipeline;
1022 SPipelineContainer m_StandardLinePipeline;
1023 SPipelineContainer m_Standard3DPipeline;
1024 SPipelineContainer m_TextPipeline;
1025 SPipelineContainer m_TilePipeline;
1026 SPipelineContainer m_TileBorderPipeline;
1027 SPipelineContainer m_PrimExPipeline;
1028 SPipelineContainer m_PrimExRotationlessPipeline;
1029 SPipelineContainer m_SpriteMultiPipeline;
1030 SPipelineContainer m_SpriteMultiPushPipeline;
1031 SPipelineContainer m_QuadPipeline;
1032 SPipelineContainer m_QuadPushPipeline;
1033
1034 std::vector<VkPipeline> m_vLastPipeline;
1035
1036 std::vector<VkCommandPool> m_vCommandPools;
1037
1038 VkRenderPass m_VKRenderPass;
1039
1040 VkSurfaceFormatKHR m_VKSurfFormat;
1041
1042 SDeviceDescriptorPools m_StandardTextureDescrPool;
1043 SDeviceDescriptorPools m_TextTextureDescrPool;
1044
1045 std::vector<SDeviceDescriptorPools> m_vUniformBufferDescrPools;
1046
1047 VkSwapchainKHR m_VKSwapChain = VK_NULL_HANDLE;
1048 std::vector<VkImage> m_vSwapChainImages;
1049 uint32_t m_SwapChainImageCount = 0;
1050
1051 std::vector<SStreamMemory<SFrameBuffers>> m_vStreamedVertexBuffers;
1052 std::vector<SStreamMemory<SFrameUniformBuffers>> m_vStreamedUniformBuffers;
1053
1054 uint32_t m_CurFrameSyncObject = 0;
1055 uint32_t m_CurImageIndex = 0;
1056
1057 uint32_t m_CanvasWidth;
1058 uint32_t m_CanvasHeight;
1059
1060 SDL_Window *m_pWindow;
1061
1062 std::array<float, 4> m_aClearColor = {0, 0, 0, 0};
1063
1064 struct SRenderCommandExecuteBuffer
1065 {
1066 CCommandBuffer::ECommandBufferCMD m_Command;
1067 const CCommandBuffer::SCommand *m_pRawCommand;
1068 uint32_t m_ThreadIndex;
1069
1070 // must be calculated when the buffer gets filled
1071 size_t m_EstimatedRenderCallCount = 0;
1072
1073 // useful data
1074 VkBuffer m_Buffer;
1075 size_t m_BufferOff;
1076 std::array<SDeviceDescriptorSet, 2> m_aDescriptors;
1077
1078 VkBuffer m_IndexBuffer;
1079
1080 bool m_ClearColorInRenderThread = false;
1081
1082 bool m_HasDynamicState = false;
1083 VkViewport m_Viewport;
1084 VkRect2D m_Scissor;
1085 };
1086
1087 typedef std::vector<SRenderCommandExecuteBuffer> TCommandList;
1088 typedef std::vector<TCommandList> TThreadCommandList;
1089
1090 TThreadCommandList m_vvThreadCommandLists;
1091 std::vector<bool> m_vThreadHelperHadCommands;
1092
1093 typedef std::function<bool(const CCommandBuffer::SCommand *, SRenderCommandExecuteBuffer &)> TCommandBufferCommandCallback;
1094 typedef std::function<void(SRenderCommandExecuteBuffer &, const CCommandBuffer::SCommand *)> TCommandBufferFillExecuteBufferFunc;
1095
1096 struct SCommandCallback
1097 {
1098 bool m_IsRenderCommand;
1099 TCommandBufferFillExecuteBufferFunc m_FillExecuteBuffer;
1100 TCommandBufferCommandCallback m_CommandCB;
1101 // command should be considered handled after it executed
1102 bool m_CMDIsHandled = true;
1103 };
1104 std::array<SCommandCallback, CCommandBuffer::CMD_COUNT - CCommandBuffer::CMD_FIRST> m_aCommandCallbacks;
1105
1106protected:
1107 /************************
1108 * ERROR MANAGEMENT
1109 ************************/
1110 std::mutex m_ErrWarnMutex;
1111 std::string m_ErrorHelper;
1112
1113 bool m_HasError = false;
1114 bool m_CanAssert = false;
1115
1116 /**
1117 * After an error occurred, the rendering stop as soon as possible
1118 * Always stop the current code execution after a call to this function (e.g. return false)
1119 */
1120 void SetError(EGfxErrorType ErrType, const char *pErr, const char *pErrStrExtra = nullptr)
1121 {
1122 std::unique_lock<std::mutex> Lock(m_ErrWarnMutex);
1123 SGfxErrorContainer::SError Err = {.m_RequiresTranslation: false, .m_Err: pErr};
1124 if(std::find(first: m_Error.m_vErrors.begin(), last: m_Error.m_vErrors.end(), val: Err) == m_Error.m_vErrors.end())
1125 m_Error.m_vErrors.emplace_back(args&: Err);
1126 if(pErrStrExtra != nullptr)
1127 {
1128 SGfxErrorContainer::SError ErrExtra = {.m_RequiresTranslation: false, .m_Err: pErrStrExtra};
1129 if(std::find(first: m_Error.m_vErrors.begin(), last: m_Error.m_vErrors.end(), val: ErrExtra) == m_Error.m_vErrors.end())
1130 m_Error.m_vErrors.emplace_back(args&: ErrExtra);
1131 }
1132 if(m_CanAssert)
1133 {
1134 if(pErrStrExtra != nullptr)
1135 dbg_msg(sys: "vulkan", fmt: "vulkan error: %s: %s", pErr, pErrStrExtra);
1136 else
1137 dbg_msg(sys: "vulkan", fmt: "vulkan error: %s", pErr);
1138 m_HasError = true;
1139 m_Error.m_ErrorType = ErrType;
1140 }
1141 else
1142 {
1143 Lock.unlock();
1144 // during initialization vulkan should not throw any errors but warnings instead
1145 // since most code in the swapchain is shared with runtime code, add this extra code path
1146 SetWarning(WarningType: EGfxWarningType::GFX_WARNING_TYPE_INIT_FAILED, pWarning: pErr);
1147 }
1148 }
1149
1150 void SetWarningPreMsg(const char *pWarningPre)
1151 {
1152 std::unique_lock<std::mutex> Lock(m_ErrWarnMutex);
1153 if(std::find(first: m_Warning.m_vWarnings.begin(), last: m_Warning.m_vWarnings.end(), val: pWarningPre) == m_Warning.m_vWarnings.end())
1154 m_Warning.m_vWarnings.emplace(position: m_Warning.m_vWarnings.begin(), args&: pWarningPre);
1155 }
1156
1157 void SetWarning(EGfxWarningType WarningType, const char *pWarning)
1158 {
1159 std::unique_lock<std::mutex> Lock(m_ErrWarnMutex);
1160 dbg_msg(sys: "vulkan", fmt: "vulkan warning: %s", pWarning);
1161 if(std::find(first: m_Warning.m_vWarnings.begin(), last: m_Warning.m_vWarnings.end(), val: pWarning) == m_Warning.m_vWarnings.end())
1162 m_Warning.m_vWarnings.emplace_back(args&: pWarning);
1163 m_Warning.m_WarningType = WarningType;
1164 }
1165
1166 const char *GetMemoryUsageShort()
1167 {
1168 m_ErrorHelper = std::string("Staging: ") +
1169 std::to_string(val: m_pStagingMemoryUsage->load(m: std::memory_order_relaxed) / 1024) +
1170 " KB, Buffer: " +
1171 std::to_string(val: m_pBufferMemoryUsage->load(m: std::memory_order_relaxed) / 1024) +
1172 " KB, Texture: " +
1173 std::to_string(val: m_pTextureMemoryUsage->load(m: std::memory_order_relaxed) / 1024) +
1174 " KB, Stream: " +
1175 std::to_string(val: m_pStreamMemoryUsage->load(m: std::memory_order_relaxed) / 1024) +
1176 " KB";
1177 return m_ErrorHelper.c_str();
1178 }
1179
1180 const char *CheckVulkanCriticalError(VkResult CallResult)
1181 {
1182 const char *pCriticalError = nullptr;
1183 switch(CallResult)
1184 {
1185 case VK_ERROR_OUT_OF_HOST_MEMORY:
1186 pCriticalError = "host ran out of memory";
1187 dbg_msg(sys: "vulkan", fmt: "%s", pCriticalError);
1188 break;
1189 case VK_ERROR_OUT_OF_DEVICE_MEMORY:
1190 pCriticalError = "device ran out of memory";
1191 dbg_msg(sys: "vulkan", fmt: "%s", pCriticalError);
1192 break;
1193 case VK_ERROR_DEVICE_LOST:
1194 pCriticalError = "device lost";
1195 dbg_msg(sys: "vulkan", fmt: "%s", pCriticalError);
1196 break;
1197 case VK_ERROR_OUT_OF_DATE_KHR:
1198 {
1199 if(IsVerbose())
1200 {
1201 dbg_msg(sys: "vulkan", fmt: "queueing swap chain recreation because the current is out of date");
1202 }
1203 m_RecreateSwapChain = true;
1204 break;
1205 }
1206 case VK_ERROR_SURFACE_LOST_KHR:
1207 dbg_msg(sys: "vulkan", fmt: "surface lost");
1208 break;
1209 /*case VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
1210 dbg_msg("vulkan", "fullscreen exclusive mode lost");
1211 break;*/
1212 case VK_ERROR_INCOMPATIBLE_DRIVER:
1213 pCriticalError = "no compatible driver found. Vulkan 1.1 is required.";
1214 dbg_msg(sys: "vulkan", fmt: "%s", pCriticalError);
1215 break;
1216 case VK_ERROR_INITIALIZATION_FAILED:
1217 pCriticalError = "initialization failed for unknown reason.";
1218 dbg_msg(sys: "vulkan", fmt: "%s", pCriticalError);
1219 break;
1220 case VK_ERROR_LAYER_NOT_PRESENT:
1221 SetWarning(WarningType: EGfxWarningType::GFX_WARNING_MISSING_EXTENSION, pWarning: "One Vulkan layer was not present. (try to disable them)");
1222 break;
1223 case VK_ERROR_EXTENSION_NOT_PRESENT:
1224 SetWarning(WarningType: EGfxWarningType::GFX_WARNING_MISSING_EXTENSION, pWarning: "One Vulkan extension was not present. (try to disable them)");
1225 break;
1226 case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR:
1227 dbg_msg(sys: "vulkan", fmt: "native window in use");
1228 break;
1229 case VK_SUCCESS:
1230 break;
1231 case VK_SUBOPTIMAL_KHR:
1232 if(IsVerbose())
1233 {
1234 dbg_msg(sys: "vulkan", fmt: "queueing swap chain recreation because the current is sub optimal");
1235 }
1236 m_RecreateSwapChain = true;
1237 break;
1238 default:
1239 m_ErrorHelper = "unknown error: ";
1240 m_ErrorHelper.append(str: std::to_string(val: CallResult));
1241 pCriticalError = m_ErrorHelper.c_str();
1242 break;
1243 }
1244
1245 return pCriticalError;
1246 }
1247
1248 void ErroneousCleanup() override
1249 {
1250 CleanupVulkanSDL();
1251 }
1252
1253 /************************
1254 * COMMAND CALLBACKS
1255 ************************/
1256
1257 size_t CommandBufferCMDOff(CCommandBuffer::ECommandBufferCMD CommandBufferCMD)
1258 {
1259 return (size_t)CommandBufferCMD - CCommandBuffer::ECommandBufferCMD::CMD_FIRST;
1260 }
1261
1262 void RegisterCommands()
1263 {
1264 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_TEXTURE_CREATE)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Texture_Create(pCommand: static_cast<const CCommandBuffer::SCommand_Texture_Create *>(pBaseCommand)); }};
1265 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_TEXTURE_DESTROY)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Texture_Destroy(pCommand: static_cast<const CCommandBuffer::SCommand_Texture_Destroy *>(pBaseCommand)); }};
1266 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_TEXTURE_UPDATE)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Texture_Update(pCommand: static_cast<const CCommandBuffer::SCommand_Texture_Update *>(pBaseCommand)); }};
1267 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_TEXT_TEXTURES_CREATE)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_TextTextures_Create(pCommand: static_cast<const CCommandBuffer::SCommand_TextTextures_Create *>(pBaseCommand)); }};
1268 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_TEXT_TEXTURES_DESTROY)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_TextTextures_Destroy(pCommand: static_cast<const CCommandBuffer::SCommand_TextTextures_Destroy *>(pBaseCommand)); }};
1269 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_TEXT_TEXTURE_UPDATE)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_TextTexture_Update(pCommand: static_cast<const CCommandBuffer::SCommand_TextTexture_Update *>(pBaseCommand)); }};
1270
1271 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_CLEAR)] = {.m_IsRenderCommand: true, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_Clear_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_Clear *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Clear(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_Clear *>(pBaseCommand)); }};
1272 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_RENDER)] = {.m_IsRenderCommand: true, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_Render_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_Render *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Render(pCommand: static_cast<const CCommandBuffer::SCommand_Render *>(pBaseCommand), ExecBuffer); }};
1273 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_RENDER_TEX3D)] = {.m_IsRenderCommand: true, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_RenderTex3D_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_RenderTex3D *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_RenderTex3D(pCommand: static_cast<const CCommandBuffer::SCommand_RenderTex3D *>(pBaseCommand), ExecBuffer); }};
1274
1275 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_CREATE_BUFFER_OBJECT)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_CreateBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_CreateBufferObject *>(pBaseCommand)); }};
1276 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_RECREATE_BUFFER_OBJECT)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_RecreateBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_RecreateBufferObject *>(pBaseCommand)); }};
1277 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_UPDATE_BUFFER_OBJECT)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_UpdateBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_UpdateBufferObject *>(pBaseCommand)); }};
1278 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_COPY_BUFFER_OBJECT)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_CopyBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_CopyBufferObject *>(pBaseCommand)); }};
1279 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_DELETE_BUFFER_OBJECT)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_DeleteBufferObject(pCommand: static_cast<const CCommandBuffer::SCommand_DeleteBufferObject *>(pBaseCommand)); }};
1280
1281 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_CREATE_BUFFER_CONTAINER)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_CreateBufferContainer(pCommand: static_cast<const CCommandBuffer::SCommand_CreateBufferContainer *>(pBaseCommand)); }};
1282 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_DELETE_BUFFER_CONTAINER)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_DeleteBufferContainer(pCommand: static_cast<const CCommandBuffer::SCommand_DeleteBufferContainer *>(pBaseCommand)); }};
1283 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_UPDATE_BUFFER_CONTAINER)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_UpdateBufferContainer(pCommand: static_cast<const CCommandBuffer::SCommand_UpdateBufferContainer *>(pBaseCommand)); }};
1284
1285 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_INDICES_REQUIRED_NUM_NOTIFY)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_IndicesRequiredNumNotify(pCommand: static_cast<const CCommandBuffer::SCommand_IndicesRequiredNumNotify *>(pBaseCommand)); }};
1286
1287 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_RENDER_TILE_LAYER)] = {.m_IsRenderCommand: true, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_RenderTileLayer_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_RenderTileLayer *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_RenderTileLayer(pCommand: static_cast<const CCommandBuffer::SCommand_RenderTileLayer *>(pBaseCommand), ExecBuffer); }};
1288 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_RENDER_BORDER_TILE)] = {.m_IsRenderCommand: true, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_RenderBorderTile_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_RenderBorderTile *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_RenderBorderTile(pCommand: static_cast<const CCommandBuffer::SCommand_RenderBorderTile *>(pBaseCommand), ExecBuffer); }};
1289 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_RENDER_QUAD_LAYER)] = {.m_IsRenderCommand: true, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_RenderQuadLayer_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadLayer *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_RenderQuadLayer(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadLayer *>(pBaseCommand), ExecBuffer); }};
1290 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_RENDER_TEXT)] = {.m_IsRenderCommand: true, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_RenderText_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_RenderText *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_RenderText(pCommand: static_cast<const CCommandBuffer::SCommand_RenderText *>(pBaseCommand), ExecBuffer); }};
1291 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_RENDER_QUAD_CONTAINER)] = {.m_IsRenderCommand: true, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_RenderQuadContainer_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainer *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_RenderQuadContainer(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainer *>(pBaseCommand), ExecBuffer); }};
1292 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_RENDER_QUAD_CONTAINER_EX)] = {.m_IsRenderCommand: true, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_RenderQuadContainerEx_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainerEx *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_RenderQuadContainerEx(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainerEx *>(pBaseCommand), ExecBuffer); }};
1293 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_RENDER_QUAD_CONTAINER_SPRITE_MULTIPLE)] = {.m_IsRenderCommand: true, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_RenderQuadContainerAsSpriteMultiple_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_RenderQuadContainerAsSpriteMultiple(pCommand: static_cast<const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *>(pBaseCommand), ExecBuffer); }};
1294
1295 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_SWAP)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Swap(pCommand: static_cast<const CCommandBuffer::SCommand_Swap *>(pBaseCommand)); }};
1296
1297 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_VSYNC)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_VSync(pCommand: static_cast<const CCommandBuffer::SCommand_VSync *>(pBaseCommand)); }};
1298 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_MULTISAMPLING)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_MultiSampling(pCommand: static_cast<const CCommandBuffer::SCommand_MultiSampling *>(pBaseCommand)); }};
1299 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_TRY_SWAP_AND_READ_PIXEL)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_ReadPixel(pCommand: static_cast<const CCommandBuffer::SCommand_TrySwapAndReadPixel *>(pBaseCommand)); }};
1300 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_TRY_SWAP_AND_SCREENSHOT)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Screenshot(pCommand: static_cast<const CCommandBuffer::SCommand_TrySwapAndScreenshot *>(pBaseCommand)); }};
1301
1302 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_UPDATE_VIEWPORT)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [this](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) { Cmd_Update_Viewport_FillExecuteBuffer(ExecBuffer, pCommand: static_cast<const CCommandBuffer::SCommand_Update_Viewport *>(pBaseCommand)); }, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_Update_Viewport(pCommand: static_cast<const CCommandBuffer::SCommand_Update_Viewport *>(pBaseCommand)); }};
1303
1304 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_WINDOW_CREATE_NTF)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_WindowCreateNtf(pCommand: static_cast<const CCommandBuffer::SCommand_WindowCreateNtf *>(pBaseCommand)); }, .m_CMDIsHandled: false};
1305 m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::CMD_WINDOW_DESTROY_NTF)] = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [this](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return Cmd_WindowDestroyNtf(pCommand: static_cast<const CCommandBuffer::SCommand_WindowDestroyNtf *>(pBaseCommand)); }, .m_CMDIsHandled: false};
1306
1307 for(auto &Callback : m_aCommandCallbacks)
1308 {
1309 if(!(bool)Callback.m_CommandCB)
1310 Callback = {.m_IsRenderCommand: false, .m_FillExecuteBuffer: [](SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand *pBaseCommand) {}, .m_CommandCB: [](const CCommandBuffer::SCommand *pBaseCommand, SRenderCommandExecuteBuffer &ExecBuffer) { return true; }};
1311 }
1312 }
1313
1314 /*****************************
1315 * VIDEO AND SCREENSHOT HELPER
1316 ******************************/
1317
1318 [[nodiscard]] bool PreparePresentedImageDataImage(uint8_t *&pResImageData, uint32_t Width, uint32_t Height)
1319 {
1320 bool NeedsNewImg = Width != m_GetPresentedImgDataHelperWidth || Height != m_GetPresentedImgDataHelperHeight;
1321 if(m_GetPresentedImgDataHelperImage == VK_NULL_HANDLE || NeedsNewImg)
1322 {
1323 if(m_GetPresentedImgDataHelperImage != VK_NULL_HANDLE)
1324 {
1325 DeletePresentedImageDataImage();
1326 }
1327 m_GetPresentedImgDataHelperWidth = Width;
1328 m_GetPresentedImgDataHelperHeight = Height;
1329
1330 VkImageCreateInfo ImageInfo{};
1331 ImageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
1332 ImageInfo.imageType = VK_IMAGE_TYPE_2D;
1333 ImageInfo.extent.width = Width;
1334 ImageInfo.extent.height = Height;
1335 ImageInfo.extent.depth = 1;
1336 ImageInfo.mipLevels = 1;
1337 ImageInfo.arrayLayers = 1;
1338 ImageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
1339 ImageInfo.tiling = VK_IMAGE_TILING_LINEAR;
1340 ImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
1341 ImageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
1342 ImageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
1343 ImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
1344
1345 vkCreateImage(device: m_VKDevice, pCreateInfo: &ImageInfo, pAllocator: nullptr, pImage: &m_GetPresentedImgDataHelperImage);
1346 // Create memory to back up the image
1347 VkMemoryRequirements MemRequirements;
1348 vkGetImageMemoryRequirements(device: m_VKDevice, image: m_GetPresentedImgDataHelperImage, pMemoryRequirements: &MemRequirements);
1349
1350 VkMemoryAllocateInfo MemAllocInfo{};
1351 MemAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1352 MemAllocInfo.allocationSize = MemRequirements.size;
1353 MemAllocInfo.memoryTypeIndex = FindMemoryType(PhyDevice: m_VKGPU, TypeFilter: MemRequirements.memoryTypeBits, Properties: VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT);
1354
1355 vkAllocateMemory(device: m_VKDevice, pAllocateInfo: &MemAllocInfo, pAllocator: nullptr, pMemory: &m_GetPresentedImgDataHelperMem.m_Mem);
1356 vkBindImageMemory(device: m_VKDevice, image: m_GetPresentedImgDataHelperImage, memory: m_GetPresentedImgDataHelperMem.m_Mem, memoryOffset: 0);
1357
1358 if(!ImageBarrier(Image: m_GetPresentedImgDataHelperImage, MipMapBase: 0, MipMapCount: 1, LayerBase: 0, LayerCount: 1, Format: VK_FORMAT_R8G8B8A8_UNORM, OldLayout: VK_IMAGE_LAYOUT_UNDEFINED, NewLayout: VK_IMAGE_LAYOUT_GENERAL))
1359 return false;
1360
1361 VkImageSubresource SubResource{.aspectMask: VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel: 0, .arrayLayer: 0};
1362 VkSubresourceLayout SubResourceLayout;
1363 vkGetImageSubresourceLayout(device: m_VKDevice, image: m_GetPresentedImgDataHelperImage, pSubresource: &SubResource, pLayout: &SubResourceLayout);
1364
1365 vkMapMemory(device: m_VKDevice, memory: m_GetPresentedImgDataHelperMem.m_Mem, offset: 0, VK_WHOLE_SIZE, flags: 0, ppData: (void **)&m_pGetPresentedImgDataHelperMappedMemory);
1366 m_GetPresentedImgDataHelperMappedLayoutOffset = SubResourceLayout.offset;
1367 m_GetPresentedImgDataHelperMappedLayoutPitch = SubResourceLayout.rowPitch;
1368 m_pGetPresentedImgDataHelperMappedMemory += m_GetPresentedImgDataHelperMappedLayoutOffset;
1369
1370 VkFenceCreateInfo FenceInfo{};
1371 FenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
1372 FenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
1373 vkCreateFence(device: m_VKDevice, pCreateInfo: &FenceInfo, pAllocator: nullptr, pFence: &m_GetPresentedImgDataHelperFence);
1374 }
1375 pResImageData = m_pGetPresentedImgDataHelperMappedMemory;
1376 return true;
1377 }
1378
1379 void DeletePresentedImageDataImage()
1380 {
1381 if(m_GetPresentedImgDataHelperImage != VK_NULL_HANDLE)
1382 {
1383 vkDestroyFence(device: m_VKDevice, fence: m_GetPresentedImgDataHelperFence, pAllocator: nullptr);
1384
1385 m_GetPresentedImgDataHelperFence = VK_NULL_HANDLE;
1386
1387 vkDestroyImage(device: m_VKDevice, image: m_GetPresentedImgDataHelperImage, pAllocator: nullptr);
1388 vkUnmapMemory(device: m_VKDevice, memory: m_GetPresentedImgDataHelperMem.m_Mem);
1389 vkFreeMemory(device: m_VKDevice, memory: m_GetPresentedImgDataHelperMem.m_Mem, pAllocator: nullptr);
1390
1391 m_GetPresentedImgDataHelperImage = VK_NULL_HANDLE;
1392 m_GetPresentedImgDataHelperMem = {};
1393 m_pGetPresentedImgDataHelperMappedMemory = nullptr;
1394
1395 m_GetPresentedImgDataHelperWidth = 0;
1396 m_GetPresentedImgDataHelperHeight = 0;
1397 }
1398 }
1399
1400 [[nodiscard]] bool GetPresentedImageDataImpl(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData, bool ResetAlpha, std::optional<ivec2> PixelOffset)
1401 {
1402 bool IsB8G8R8A8 = m_VKSurfFormat.format == VK_FORMAT_B8G8R8A8_UNORM;
1403 bool UsesRGBALikeFormat = m_VKSurfFormat.format == VK_FORMAT_R8G8B8A8_UNORM || IsB8G8R8A8;
1404 if(UsesRGBALikeFormat && m_LastPresentedSwapChainImageIndex != std::numeric_limits<decltype(m_LastPresentedSwapChainImageIndex)>::max())
1405 {
1406 auto Viewport = m_VKSwapImgAndViewportExtent.GetPresentedImageViewport();
1407 VkOffset3D SrcOffset;
1408 if(PixelOffset.has_value())
1409 {
1410 SrcOffset.x = PixelOffset.value().x;
1411 SrcOffset.y = PixelOffset.value().y;
1412 Width = 1;
1413 Height = 1;
1414 }
1415 else
1416 {
1417 SrcOffset.x = 0;
1418 SrcOffset.y = 0;
1419 Width = Viewport.width;
1420 Height = Viewport.height;
1421 }
1422 SrcOffset.z = 0;
1423 Format = CImageInfo::FORMAT_RGBA;
1424
1425 const size_t ImageTotalSize = (size_t)Width * Height * CImageInfo::PixelSize(Format);
1426
1427 uint8_t *pResImageData;
1428 if(!PreparePresentedImageDataImage(pResImageData, Width, Height))
1429 return false;
1430
1431 VkCommandBuffer *pCommandBuffer;
1432 if(!GetMemoryCommandBuffer(pMemCommandBuffer&: pCommandBuffer))
1433 return false;
1434 VkCommandBuffer &CommandBuffer = *pCommandBuffer;
1435
1436 auto &SwapImg = m_vSwapChainImages[m_LastPresentedSwapChainImageIndex];
1437
1438 if(!ImageBarrier(Image: m_GetPresentedImgDataHelperImage, MipMapBase: 0, MipMapCount: 1, LayerBase: 0, LayerCount: 1, Format: VK_FORMAT_R8G8B8A8_UNORM, OldLayout: VK_IMAGE_LAYOUT_GENERAL, NewLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL))
1439 return false;
1440 if(!ImageBarrier(Image: SwapImg, MipMapBase: 0, MipMapCount: 1, LayerBase: 0, LayerCount: 1, Format: m_VKSurfFormat.format, OldLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, NewLayout: VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL))
1441 return false;
1442
1443 // If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB)
1444 if(m_OptimalSwapChainImageBlitting && m_LinearRGBAImageBlitting)
1445 {
1446 VkOffset3D BlitSize;
1447 BlitSize.x = Width;
1448 BlitSize.y = Height;
1449 BlitSize.z = 1;
1450
1451 VkImageBlit ImageBlitRegion{};
1452 ImageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1453 ImageBlitRegion.srcSubresource.layerCount = 1;
1454 ImageBlitRegion.srcOffsets[0] = SrcOffset;
1455 ImageBlitRegion.srcOffsets[1] = {.x: SrcOffset.x + BlitSize.x, .y: SrcOffset.y + BlitSize.y, .z: SrcOffset.z + BlitSize.z};
1456 ImageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1457 ImageBlitRegion.dstSubresource.layerCount = 1;
1458 ImageBlitRegion.dstOffsets[1] = BlitSize;
1459
1460 // Issue the blit command
1461 vkCmdBlitImage(commandBuffer: CommandBuffer, srcImage: SwapImg, srcImageLayout: VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1462 dstImage: m_GetPresentedImgDataHelperImage, dstImageLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1463 regionCount: 1, pRegions: &ImageBlitRegion, filter: VK_FILTER_NEAREST);
1464
1465 // transformed to RGBA
1466 IsB8G8R8A8 = false;
1467 }
1468 else
1469 {
1470 // Otherwise use image copy (requires us to manually flip components)
1471 VkImageCopy ImageCopyRegion{};
1472 ImageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1473 ImageCopyRegion.srcSubresource.layerCount = 1;
1474 ImageCopyRegion.srcOffset = SrcOffset;
1475 ImageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
1476 ImageCopyRegion.dstSubresource.layerCount = 1;
1477 ImageCopyRegion.extent.width = Width;
1478 ImageCopyRegion.extent.height = Height;
1479 ImageCopyRegion.extent.depth = 1;
1480
1481 // Issue the copy command
1482 vkCmdCopyImage(commandBuffer: CommandBuffer, srcImage: SwapImg, srcImageLayout: VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1483 dstImage: m_GetPresentedImgDataHelperImage, dstImageLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1484 regionCount: 1, pRegions: &ImageCopyRegion);
1485 }
1486
1487 if(!ImageBarrier(Image: m_GetPresentedImgDataHelperImage, MipMapBase: 0, MipMapCount: 1, LayerBase: 0, LayerCount: 1, Format: VK_FORMAT_R8G8B8A8_UNORM, OldLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, NewLayout: VK_IMAGE_LAYOUT_GENERAL))
1488 return false;
1489 if(!ImageBarrier(Image: SwapImg, MipMapBase: 0, MipMapCount: 1, LayerBase: 0, LayerCount: 1, Format: m_VKSurfFormat.format, OldLayout: VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, NewLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR))
1490 return false;
1491
1492 vkEndCommandBuffer(commandBuffer: CommandBuffer);
1493 m_vUsedMemoryCommandBuffer[m_CurImageIndex] = false;
1494
1495 VkSubmitInfo SubmitInfo{};
1496 SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1497 SubmitInfo.commandBufferCount = 1;
1498 SubmitInfo.pCommandBuffers = &CommandBuffer;
1499
1500 vkResetFences(device: m_VKDevice, fenceCount: 1, pFences: &m_GetPresentedImgDataHelperFence);
1501 vkQueueSubmit(queue: m_VKGraphicsQueue, submitCount: 1, pSubmits: &SubmitInfo, fence: m_GetPresentedImgDataHelperFence);
1502 vkWaitForFences(device: m_VKDevice, fenceCount: 1, pFences: &m_GetPresentedImgDataHelperFence, VK_TRUE, timeout: std::numeric_limits<uint64_t>::max());
1503
1504 VkMappedMemoryRange MemRange{};
1505 MemRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
1506 MemRange.memory = m_GetPresentedImgDataHelperMem.m_Mem;
1507 MemRange.offset = m_GetPresentedImgDataHelperMappedLayoutOffset;
1508 MemRange.size = VK_WHOLE_SIZE;
1509 vkInvalidateMappedMemoryRanges(device: m_VKDevice, memoryRangeCount: 1, pMemoryRanges: &MemRange);
1510
1511 size_t RealFullImageSize = maximum(a: ImageTotalSize, b: (size_t)(Height * m_GetPresentedImgDataHelperMappedLayoutPitch));
1512 if(vDstData.size() < RealFullImageSize)
1513 vDstData.resize(new_size: RealFullImageSize);
1514
1515 mem_copy(dest: vDstData.data(), source: pResImageData, size: RealFullImageSize);
1516
1517 // pack image data together without any offset that the driver might require
1518 if(Width * 4 < m_GetPresentedImgDataHelperMappedLayoutPitch)
1519 {
1520 for(uint32_t Y = 0; Y < Height; ++Y)
1521 {
1522 size_t OffsetImagePacked = (Y * Width * 4);
1523 size_t OffsetImageUnpacked = (Y * m_GetPresentedImgDataHelperMappedLayoutPitch);
1524 mem_copy(dest: vDstData.data() + OffsetImagePacked, source: vDstData.data() + OffsetImageUnpacked, size: Width * 4);
1525 }
1526 }
1527
1528 if(IsB8G8R8A8 || ResetAlpha)
1529 {
1530 // swizzle
1531 for(uint32_t Y = 0; Y < Height; ++Y)
1532 {
1533 for(uint32_t X = 0; X < Width; ++X)
1534 {
1535 size_t ImgOff = (Y * Width * 4) + (X * 4);
1536 if(IsB8G8R8A8)
1537 {
1538 std::swap(a&: vDstData[ImgOff], b&: vDstData[ImgOff + 2]);
1539 }
1540 vDstData[ImgOff + 3] = 255;
1541 }
1542 }
1543 }
1544
1545 return true;
1546 }
1547 else
1548 {
1549 if(!UsesRGBALikeFormat)
1550 {
1551 dbg_msg(sys: "vulkan", fmt: "swap chain image was not in a RGBA like format.");
1552 }
1553 else
1554 {
1555 dbg_msg(sys: "vulkan", fmt: "swap chain image was not ready to be copied.");
1556 }
1557 return false;
1558 }
1559 }
1560
1561 [[nodiscard]] bool GetPresentedImageData(uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData) override
1562 {
1563 return GetPresentedImageDataImpl(Width, Height, Format, vDstData, ResetAlpha: false, PixelOffset: {});
1564 }
1565
1566 /************************
1567 * MEMORY MANAGEMENT
1568 ************************/
1569
1570 [[nodiscard]] bool AllocateVulkanMemory(const VkMemoryAllocateInfo *pAllocateInfo, VkDeviceMemory *pMemory)
1571 {
1572 VkResult Res = vkAllocateMemory(device: m_VKDevice, pAllocateInfo, pAllocator: nullptr, pMemory);
1573 if(Res != VK_SUCCESS)
1574 {
1575 dbg_msg(sys: "vulkan", fmt: "vulkan memory allocation failed, trying to recover.");
1576 if(Res == VK_ERROR_OUT_OF_HOST_MEMORY || Res == VK_ERROR_OUT_OF_DEVICE_MEMORY)
1577 {
1578 // aggressivly try to get more memory
1579 vkDeviceWaitIdle(device: m_VKDevice);
1580 for(size_t i = 0; i < m_SwapChainImageCount + 1; ++i)
1581 {
1582 if(!NextFrame())
1583 return false;
1584 }
1585 Res = vkAllocateMemory(device: m_VKDevice, pAllocateInfo, pAllocator: nullptr, pMemory);
1586 }
1587 if(Res != VK_SUCCESS)
1588 {
1589 dbg_msg(sys: "vulkan", fmt: "vulkan memory allocation failed.");
1590 return false;
1591 }
1592 }
1593 return true;
1594 }
1595
1596 [[nodiscard]] bool GetBufferImpl(VkDeviceSize RequiredSize, EMemoryBlockUsage MemUsage, VkBuffer &Buffer, SDeviceMemoryBlock &BufferMemory, VkBufferUsageFlags BufferUsage, VkMemoryPropertyFlags BufferProperties)
1597 {
1598 return CreateBuffer(BufferSize: RequiredSize, MemUsage, BufferUsage, MemoryProperties: BufferProperties, VKBuffer&: Buffer, VKBufferMemory&: BufferMemory);
1599 }
1600
1601 template<size_t Id,
1602 int64_t MemoryBlockSize, size_t BlockCount,
1603 bool RequiresMapping>
1604 [[nodiscard]] bool GetBufferBlockImpl(SMemoryBlock<Id> &RetBlock, SMemoryBlockCache<Id> &MemoryCache, VkBufferUsageFlags BufferUsage, VkMemoryPropertyFlags BufferProperties, const void *pBufferData, VkDeviceSize RequiredSize, VkDeviceSize TargetAlignment)
1605 {
1606 bool Res = true;
1607
1608 auto &&CreateCacheBlock = [&]() -> bool {
1609 bool FoundAllocation = false;
1610 SMemoryHeap::SMemoryHeapQueueElement AllocatedMem;
1611 SDeviceMemoryBlock TmpBufferMemory;
1612 typename SMemoryBlockCache<Id>::SMemoryCacheType::SMemoryCacheHeap *pCacheHeap = nullptr;
1613 auto &Heaps = MemoryCache.m_MemoryCaches.m_vpMemoryHeaps;
1614 for(size_t i = 0; i < Heaps.size(); ++i)
1615 {
1616 auto *pHeap = Heaps[i];
1617 if(pHeap->m_Heap.Allocate(RequiredSize, TargetAlignment, AllocatedMem))
1618 {
1619 TmpBufferMemory = pHeap->m_BufferMem;
1620 FoundAllocation = true;
1621 pCacheHeap = pHeap;
1622 break;
1623 }
1624 }
1625 if(!FoundAllocation)
1626 {
1627 typename SMemoryBlockCache<Id>::SMemoryCacheType::SMemoryCacheHeap *pNewHeap = new typename SMemoryBlockCache<Id>::SMemoryCacheType::SMemoryCacheHeap();
1628
1629 VkBuffer TmpBuffer;
1630 if(!GetBufferImpl(RequiredSize: MemoryBlockSize * BlockCount, MemUsage: RequiresMapping ? MEMORY_BLOCK_USAGE_STAGING : MEMORY_BLOCK_USAGE_BUFFER, Buffer&: TmpBuffer, BufferMemory&: TmpBufferMemory, BufferUsage, BufferProperties))
1631 {
1632 delete pNewHeap;
1633 return false;
1634 }
1635
1636 void *pMapData = nullptr;
1637
1638 if(RequiresMapping)
1639 {
1640 if(vkMapMemory(device: m_VKDevice, memory: TmpBufferMemory.m_Mem, offset: 0, VK_WHOLE_SIZE, flags: 0, ppData: &pMapData) != VK_SUCCESS)
1641 {
1642 SetError(ErrType: RequiresMapping ? EGfxErrorType::GFX_ERROR_TYPE_OUT_OF_MEMORY_STAGING : EGfxErrorType::GFX_ERROR_TYPE_OUT_OF_MEMORY_BUFFER, pErr: "Failed to map buffer block memory.",
1643 pErrStrExtra: GetMemoryUsageShort());
1644 delete pNewHeap;
1645 return false;
1646 }
1647 }
1648
1649 pNewHeap->m_Buffer = TmpBuffer;
1650
1651 pNewHeap->m_BufferMem = TmpBufferMemory;
1652 pNewHeap->m_pMappedBuffer = pMapData;
1653
1654 pCacheHeap = pNewHeap;
1655 Heaps.emplace_back(pNewHeap);
1656 Heaps.back()->m_Heap.Init(MemoryBlockSize * BlockCount, 0);
1657 if(!Heaps.back()->m_Heap.Allocate(RequiredSize, TargetAlignment, AllocatedMem))
1658 {
1659 SetError(ErrType: RequiresMapping ? EGfxErrorType::GFX_ERROR_TYPE_OUT_OF_MEMORY_STAGING : EGfxErrorType::GFX_ERROR_TYPE_OUT_OF_MEMORY_BUFFER, pErr: "Heap allocation failed directly after creating fresh heap.",
1660 pErrStrExtra: GetMemoryUsageShort());
1661 return false;
1662 }
1663 }
1664
1665 RetBlock.m_Buffer = pCacheHeap->m_Buffer;
1666 RetBlock.m_BufferMem = TmpBufferMemory;
1667 if(RequiresMapping)
1668 RetBlock.m_pMappedBuffer = ((uint8_t *)pCacheHeap->m_pMappedBuffer) + AllocatedMem.m_OffsetToAlign;
1669 else
1670 RetBlock.m_pMappedBuffer = nullptr;
1671 RetBlock.m_IsCached = true;
1672 RetBlock.m_pHeap = &pCacheHeap->m_Heap;
1673 RetBlock.m_HeapData = AllocatedMem;
1674 RetBlock.m_UsedSize = RequiredSize;
1675
1676 if(RequiresMapping)
1677 mem_copy(RetBlock.m_pMappedBuffer, pBufferData, RequiredSize);
1678
1679 return true;
1680 };
1681
1682 if(RequiredSize < (VkDeviceSize)MemoryBlockSize)
1683 {
1684 Res = CreateCacheBlock();
1685 }
1686 else
1687 {
1688 VkBuffer TmpBuffer;
1689 SDeviceMemoryBlock TmpBufferMemory;
1690 if(!GetBufferImpl(RequiredSize, MemUsage: RequiresMapping ? MEMORY_BLOCK_USAGE_STAGING : MEMORY_BLOCK_USAGE_BUFFER, Buffer&: TmpBuffer, BufferMemory&: TmpBufferMemory, BufferUsage, BufferProperties))
1691 return false;
1692
1693 void *pMapData = nullptr;
1694 if(RequiresMapping)
1695 {
1696 vkMapMemory(device: m_VKDevice, memory: TmpBufferMemory.m_Mem, offset: 0, VK_WHOLE_SIZE, flags: 0, ppData: &pMapData);
1697 mem_copy(dest: pMapData, source: pBufferData, size: static_cast<size_t>(RequiredSize));
1698 }
1699
1700 RetBlock.m_Buffer = TmpBuffer;
1701 RetBlock.m_BufferMem = TmpBufferMemory;
1702 RetBlock.m_pMappedBuffer = pMapData;
1703 RetBlock.m_pHeap = nullptr;
1704 RetBlock.m_IsCached = false;
1705 RetBlock.m_HeapData.m_OffsetToAlign = 0;
1706 RetBlock.m_HeapData.m_AllocationSize = RequiredSize;
1707 RetBlock.m_UsedSize = RequiredSize;
1708 }
1709
1710 return Res;
1711 }
1712
1713 [[nodiscard]] bool GetStagingBuffer(SMemoryBlock<s_StagingBufferCacheId> &ResBlock, const void *pBufferData, VkDeviceSize RequiredSize)
1714 {
1715 return GetBufferBlockImpl<s_StagingBufferCacheId, 8 * 1024 * 1024, 3, true>(RetBlock&: ResBlock, MemoryCache&: m_StagingBufferCache, BufferUsage: VK_BUFFER_USAGE_TRANSFER_SRC_BIT, BufferProperties: VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, pBufferData, RequiredSize, TargetAlignment: maximum<VkDeviceSize>(a: m_NonCoherentMemAlignment, b: 16));
1716 }
1717
1718 [[nodiscard]] bool GetStagingBufferImage(SMemoryBlock<s_StagingBufferImageCacheId> &ResBlock, const void *pBufferData, VkDeviceSize RequiredSize)
1719 {
1720 return GetBufferBlockImpl<s_StagingBufferImageCacheId, 8 * 1024 * 1024, 3, true>(RetBlock&: ResBlock, MemoryCache&: m_StagingBufferCacheImage, BufferUsage: VK_BUFFER_USAGE_TRANSFER_SRC_BIT, BufferProperties: VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, pBufferData, RequiredSize, TargetAlignment: maximum<VkDeviceSize>(a: m_OptimalImageCopyMemAlignment, b: maximum<VkDeviceSize>(a: m_NonCoherentMemAlignment, b: 16)));
1721 }
1722
1723 template<size_t Id>
1724 void PrepareStagingMemRange(SMemoryBlock<Id> &Block)
1725 {
1726 VkMappedMemoryRange UploadRange{};
1727 UploadRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
1728 UploadRange.memory = Block.m_BufferMem.m_Mem;
1729 UploadRange.offset = Block.m_HeapData.m_OffsetToAlign;
1730
1731 auto AlignmentMod = ((VkDeviceSize)Block.m_HeapData.m_AllocationSize % m_NonCoherentMemAlignment);
1732 auto AlignmentReq = (m_NonCoherentMemAlignment - AlignmentMod);
1733 if(AlignmentMod == 0)
1734 AlignmentReq = 0;
1735 UploadRange.size = Block.m_HeapData.m_AllocationSize + AlignmentReq;
1736
1737 if(UploadRange.offset + UploadRange.size > Block.m_BufferMem.m_Size)
1738 UploadRange.size = VK_WHOLE_SIZE;
1739
1740 m_vNonFlushedStagingBufferRange.push_back(x: UploadRange);
1741 }
1742
1743 void UploadAndFreeStagingMemBlock(SMemoryBlock<s_StagingBufferCacheId> &Block)
1744 {
1745 PrepareStagingMemRange(Block);
1746 if(!Block.m_IsCached)
1747 {
1748 m_vvFrameDelayedBufferCleanup[m_CurImageIndex].push_back(x: {.m_Buffer: Block.m_Buffer, .m_Mem: Block.m_BufferMem, .m_pMappedData: Block.m_pMappedBuffer});
1749 }
1750 else
1751 {
1752 m_StagingBufferCache.FreeMemBlock(Block, ImgIndex: m_CurImageIndex);
1753 }
1754 }
1755
1756 void UploadAndFreeStagingImageMemBlock(SMemoryBlock<s_StagingBufferImageCacheId> &Block)
1757 {
1758 PrepareStagingMemRange(Block);
1759 if(!Block.m_IsCached)
1760 {
1761 m_vvFrameDelayedBufferCleanup[m_CurImageIndex].push_back(x: {.m_Buffer: Block.m_Buffer, .m_Mem: Block.m_BufferMem, .m_pMappedData: Block.m_pMappedBuffer});
1762 }
1763 else
1764 {
1765 m_StagingBufferCacheImage.FreeMemBlock(Block, ImgIndex: m_CurImageIndex);
1766 }
1767 }
1768
1769 [[nodiscard]] bool GetVertexBuffer(SMemoryBlock<s_VertexBufferCacheId> &ResBlock, VkDeviceSize RequiredSize)
1770 {
1771 return GetBufferBlockImpl<s_VertexBufferCacheId, 8 * 1024 * 1024, 3, false>(RetBlock&: ResBlock, MemoryCache&: m_VertexBufferCache, BufferUsage: VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, BufferProperties: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, pBufferData: nullptr, RequiredSize, TargetAlignment: 16);
1772 }
1773
1774 void FreeVertexMemBlock(SMemoryBlock<s_VertexBufferCacheId> &Block)
1775 {
1776 if(!Block.m_IsCached)
1777 {
1778 m_vvFrameDelayedBufferCleanup[m_CurImageIndex].push_back(x: {.m_Buffer: Block.m_Buffer, .m_Mem: Block.m_BufferMem, .m_pMappedData: nullptr});
1779 }
1780 else
1781 {
1782 m_VertexBufferCache.FreeMemBlock(Block, ImgIndex: m_CurImageIndex);
1783 }
1784 }
1785
1786 static size_t ImageMipLevelCount(size_t Width, size_t Height, size_t Depth)
1787 {
1788 return std::floor(x: std::log2(x: maximum(a: Width, b: maximum(a: Height, b: Depth)))) + 1;
1789 }
1790
1791 static size_t ImageMipLevelCount(const VkExtent3D &ImgExtent)
1792 {
1793 return ImageMipLevelCount(Width: ImgExtent.width, Height: ImgExtent.height, Depth: ImgExtent.depth);
1794 }
1795
1796 // good approximation of 1024x1024 image with mipmaps
1797 static constexpr int64_t s_1024x1024ImgSize = (1024 * 1024 * 4) * 2;
1798
1799 [[nodiscard]] bool GetImageMemoryImpl(VkDeviceSize RequiredSize, uint32_t RequiredMemoryTypeBits, SDeviceMemoryBlock &BufferMemory, VkMemoryPropertyFlags BufferProperties)
1800 {
1801 VkMemoryAllocateInfo MemAllocInfo{};
1802 MemAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
1803 MemAllocInfo.allocationSize = RequiredSize;
1804 MemAllocInfo.memoryTypeIndex = FindMemoryType(PhyDevice: m_VKGPU, TypeFilter: RequiredMemoryTypeBits, Properties: BufferProperties);
1805
1806 BufferMemory.m_Size = RequiredSize;
1807 m_pTextureMemoryUsage->store(i: m_pTextureMemoryUsage->load(m: std::memory_order_relaxed) + RequiredSize, m: std::memory_order_relaxed);
1808
1809 if(IsVerbose())
1810 {
1811 VerboseAllocatedMemory(Size: RequiredSize, FrameImageIndex: m_CurImageIndex, MemUsage: MEMORY_BLOCK_USAGE_TEXTURE);
1812 }
1813
1814 if(!AllocateVulkanMemory(pAllocateInfo: &MemAllocInfo, pMemory: &BufferMemory.m_Mem))
1815 {
1816 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_OUT_OF_MEMORY_IMAGE, pErr: "Allocation for image memory failed.",
1817 pErrStrExtra: GetMemoryUsageShort());
1818 return false;
1819 }
1820
1821 BufferMemory.m_UsageType = MEMORY_BLOCK_USAGE_TEXTURE;
1822
1823 return true;
1824 }
1825
1826 template<size_t Id,
1827 int64_t MemoryBlockSize, size_t BlockCount>
1828 [[nodiscard]] bool GetImageMemoryBlockImpl(SMemoryImageBlock<Id> &RetBlock, SMemoryBlockCache<Id> &MemoryCache, VkMemoryPropertyFlags BufferProperties, VkDeviceSize RequiredSize, VkDeviceSize RequiredAlignment, uint32_t RequiredMemoryTypeBits)
1829 {
1830 auto &&CreateCacheBlock = [&]() -> bool {
1831 bool FoundAllocation = false;
1832 SMemoryHeap::SMemoryHeapQueueElement AllocatedMem;
1833 SDeviceMemoryBlock TmpBufferMemory;
1834 typename SMemoryBlockCache<Id>::SMemoryCacheType::SMemoryCacheHeap *pCacheHeap = nullptr;
1835 for(size_t i = 0; i < MemoryCache.m_MemoryCaches.m_vpMemoryHeaps.size(); ++i)
1836 {
1837 auto *pHeap = MemoryCache.m_MemoryCaches.m_vpMemoryHeaps[i];
1838 if(pHeap->m_Heap.Allocate(RequiredSize, RequiredAlignment, AllocatedMem))
1839 {
1840 TmpBufferMemory = pHeap->m_BufferMem;
1841 FoundAllocation = true;
1842 pCacheHeap = pHeap;
1843 break;
1844 }
1845 }
1846 if(!FoundAllocation)
1847 {
1848 typename SMemoryBlockCache<Id>::SMemoryCacheType::SMemoryCacheHeap *pNewHeap = new typename SMemoryBlockCache<Id>::SMemoryCacheType::SMemoryCacheHeap();
1849
1850 if(!GetImageMemoryImpl(RequiredSize: MemoryBlockSize * BlockCount, RequiredMemoryTypeBits, BufferMemory&: TmpBufferMemory, BufferProperties))
1851 {
1852 delete pNewHeap;
1853 return false;
1854 }
1855
1856 pNewHeap->m_Buffer = VK_NULL_HANDLE;
1857
1858 pNewHeap->m_BufferMem = TmpBufferMemory;
1859 pNewHeap->m_pMappedBuffer = nullptr;
1860
1861 auto &Heaps = MemoryCache.m_MemoryCaches.m_vpMemoryHeaps;
1862 pCacheHeap = pNewHeap;
1863 Heaps.emplace_back(pNewHeap);
1864 Heaps.back()->m_Heap.Init(MemoryBlockSize * BlockCount, 0);
1865 if(!Heaps.back()->m_Heap.Allocate(RequiredSize, RequiredAlignment, AllocatedMem))
1866 {
1867 dbg_assert(false, "Heap allocation failed directly after creating fresh heap for image");
1868 }
1869 }
1870
1871 RetBlock.m_Buffer = VK_NULL_HANDLE;
1872 RetBlock.m_BufferMem = TmpBufferMemory;
1873 RetBlock.m_pMappedBuffer = nullptr;
1874 RetBlock.m_IsCached = true;
1875 RetBlock.m_pHeap = &pCacheHeap->m_Heap;
1876 RetBlock.m_HeapData = AllocatedMem;
1877 RetBlock.m_UsedSize = RequiredSize;
1878
1879 return true;
1880 };
1881
1882 if(RequiredSize < (VkDeviceSize)MemoryBlockSize)
1883 {
1884 if(!CreateCacheBlock())
1885 return false;
1886 }
1887 else
1888 {
1889 SDeviceMemoryBlock TmpBufferMemory;
1890 if(!GetImageMemoryImpl(RequiredSize, RequiredMemoryTypeBits, BufferMemory&: TmpBufferMemory, BufferProperties))
1891 return false;
1892
1893 RetBlock.m_Buffer = VK_NULL_HANDLE;
1894 RetBlock.m_BufferMem = TmpBufferMemory;
1895 RetBlock.m_pMappedBuffer = nullptr;
1896 RetBlock.m_IsCached = false;
1897 RetBlock.m_pHeap = nullptr;
1898 RetBlock.m_HeapData.m_OffsetToAlign = 0;
1899 RetBlock.m_HeapData.m_AllocationSize = RequiredSize;
1900 RetBlock.m_UsedSize = RequiredSize;
1901 }
1902
1903 RetBlock.m_ImageMemoryBits = RequiredMemoryTypeBits;
1904
1905 return true;
1906 }
1907
1908 [[nodiscard]] bool GetImageMemory(SMemoryImageBlock<s_ImageBufferCacheId> &RetBlock, VkDeviceSize RequiredSize, VkDeviceSize RequiredAlignment, uint32_t RequiredMemoryTypeBits)
1909 {
1910 auto it = m_ImageBufferCaches.find(x: RequiredMemoryTypeBits);
1911 if(it == m_ImageBufferCaches.end())
1912 {
1913 it = m_ImageBufferCaches.insert(x: {RequiredMemoryTypeBits, {}}).first;
1914
1915 it->second.Init(SwapChainImageCount: m_SwapChainImageCount);
1916 }
1917 return GetImageMemoryBlockImpl<s_ImageBufferCacheId, s_1024x1024ImgSize, 2>(RetBlock, MemoryCache&: it->second, BufferProperties: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, RequiredSize, RequiredAlignment, RequiredMemoryTypeBits);
1918 }
1919
1920 void FreeImageMemBlock(SMemoryImageBlock<s_ImageBufferCacheId> &Block)
1921 {
1922 if(!Block.m_IsCached)
1923 {
1924 m_vvFrameDelayedBufferCleanup[m_CurImageIndex].push_back(x: {.m_Buffer: Block.m_Buffer, .m_Mem: Block.m_BufferMem, .m_pMappedData: nullptr});
1925 }
1926 else
1927 {
1928 m_ImageBufferCaches[Block.m_ImageMemoryBits].FreeMemBlock(Block, ImgIndex: m_CurImageIndex);
1929 }
1930 }
1931
1932 template<bool FlushForRendering, typename TName>
1933 void UploadStreamedBuffer(SStreamMemory<TName> &StreamedBuffer)
1934 {
1935 size_t RangeUpdateCount = 0;
1936 if(StreamedBuffer.IsUsed(m_CurImageIndex))
1937 {
1938 for(size_t i = 0; i < StreamedBuffer.GetUsedCount(m_CurImageIndex); ++i)
1939 {
1940 auto &BufferOfFrame = StreamedBuffer.GetBuffers(m_CurImageIndex)[i];
1941 auto &MemRange = StreamedBuffer.GetRanges(m_CurImageIndex)[RangeUpdateCount++];
1942 MemRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
1943 MemRange.memory = BufferOfFrame.m_BufferMem.m_Mem;
1944 MemRange.offset = BufferOfFrame.m_OffsetInBuffer;
1945 auto AlignmentMod = ((VkDeviceSize)BufferOfFrame.m_UsedSize % m_NonCoherentMemAlignment);
1946 auto AlignmentReq = (m_NonCoherentMemAlignment - AlignmentMod);
1947 if(AlignmentMod == 0)
1948 AlignmentReq = 0;
1949 MemRange.size = BufferOfFrame.m_UsedSize + AlignmentReq;
1950
1951 if(MemRange.offset + MemRange.size > BufferOfFrame.m_BufferMem.m_Size)
1952 MemRange.size = VK_WHOLE_SIZE;
1953
1954 BufferOfFrame.m_UsedSize = 0;
1955 }
1956 if(RangeUpdateCount > 0 && FlushForRendering)
1957 {
1958 vkFlushMappedMemoryRanges(m_VKDevice, RangeUpdateCount, StreamedBuffer.GetRanges(m_CurImageIndex).data());
1959 }
1960 }
1961 StreamedBuffer.ResetFrame(m_CurImageIndex);
1962 }
1963
1964 void CleanBufferPair(size_t ImageIndex, VkBuffer &Buffer, SDeviceMemoryBlock &BufferMem)
1965 {
1966 bool IsBuffer = Buffer != VK_NULL_HANDLE;
1967 if(IsBuffer)
1968 {
1969 vkDestroyBuffer(device: m_VKDevice, buffer: Buffer, pAllocator: nullptr);
1970
1971 Buffer = VK_NULL_HANDLE;
1972 }
1973 if(BufferMem.m_Mem != VK_NULL_HANDLE)
1974 {
1975 vkFreeMemory(device: m_VKDevice, memory: BufferMem.m_Mem, pAllocator: nullptr);
1976 if(BufferMem.m_UsageType == MEMORY_BLOCK_USAGE_BUFFER)
1977 m_pBufferMemoryUsage->store(i: m_pBufferMemoryUsage->load(m: std::memory_order_relaxed) - BufferMem.m_Size, m: std::memory_order_relaxed);
1978 else if(BufferMem.m_UsageType == MEMORY_BLOCK_USAGE_TEXTURE)
1979 m_pTextureMemoryUsage->store(i: m_pTextureMemoryUsage->load(m: std::memory_order_relaxed) - BufferMem.m_Size, m: std::memory_order_relaxed);
1980 else if(BufferMem.m_UsageType == MEMORY_BLOCK_USAGE_STREAM)
1981 m_pStreamMemoryUsage->store(i: m_pStreamMemoryUsage->load(m: std::memory_order_relaxed) - BufferMem.m_Size, m: std::memory_order_relaxed);
1982 else if(BufferMem.m_UsageType == MEMORY_BLOCK_USAGE_STAGING)
1983 m_pStagingMemoryUsage->store(i: m_pStagingMemoryUsage->load(m: std::memory_order_relaxed) - BufferMem.m_Size, m: std::memory_order_relaxed);
1984
1985 if(IsVerbose())
1986 {
1987 VerboseDeallocatedMemory(Size: BufferMem.m_Size, FrameImageIndex: (size_t)ImageIndex, MemUsage: BufferMem.m_UsageType);
1988 }
1989
1990 BufferMem.m_Mem = VK_NULL_HANDLE;
1991 }
1992 }
1993
1994 void DestroyTexture(CTexture &Texture)
1995 {
1996 if(Texture.m_Img != VK_NULL_HANDLE)
1997 {
1998 FreeImageMemBlock(Block&: Texture.m_ImgMem);
1999 vkDestroyImage(device: m_VKDevice, image: Texture.m_Img, pAllocator: nullptr);
2000
2001 vkDestroyImageView(device: m_VKDevice, imageView: Texture.m_ImgView, pAllocator: nullptr);
2002 }
2003
2004 if(Texture.m_Img3D != VK_NULL_HANDLE)
2005 {
2006 FreeImageMemBlock(Block&: Texture.m_Img3DMem);
2007 vkDestroyImage(device: m_VKDevice, image: Texture.m_Img3D, pAllocator: nullptr);
2008
2009 vkDestroyImageView(device: m_VKDevice, imageView: Texture.m_Img3DView, pAllocator: nullptr);
2010 }
2011
2012 DestroyTexturedStandardDescriptorSets(Texture, DescrIndex: 0);
2013 DestroyTexturedStandardDescriptorSets(Texture, DescrIndex: 1);
2014
2015 DestroyTextured3DStandardDescriptorSets(Texture);
2016 }
2017
2018 void DestroyTextTexture(CTexture &Texture, CTexture &TextureOutline)
2019 {
2020 if(Texture.m_Img != VK_NULL_HANDLE)
2021 {
2022 FreeImageMemBlock(Block&: Texture.m_ImgMem);
2023 vkDestroyImage(device: m_VKDevice, image: Texture.m_Img, pAllocator: nullptr);
2024
2025 vkDestroyImageView(device: m_VKDevice, imageView: Texture.m_ImgView, pAllocator: nullptr);
2026 }
2027
2028 if(TextureOutline.m_Img != VK_NULL_HANDLE)
2029 {
2030 FreeImageMemBlock(Block&: TextureOutline.m_ImgMem);
2031 vkDestroyImage(device: m_VKDevice, image: TextureOutline.m_Img, pAllocator: nullptr);
2032
2033 vkDestroyImageView(device: m_VKDevice, imageView: TextureOutline.m_ImgView, pAllocator: nullptr);
2034 }
2035
2036 DestroyTextDescriptorSets(Texture, TextureOutline);
2037 }
2038
2039 void ClearFrameData(size_t FrameImageIndex)
2040 {
2041 UploadStagingBuffers();
2042
2043 // clear pending buffers, that require deletion
2044 for(auto &BufferPair : m_vvFrameDelayedBufferCleanup[FrameImageIndex])
2045 {
2046 if(BufferPair.m_pMappedData != nullptr)
2047 {
2048 vkUnmapMemory(device: m_VKDevice, memory: BufferPair.m_Mem.m_Mem);
2049 }
2050 CleanBufferPair(ImageIndex: FrameImageIndex, Buffer&: BufferPair.m_Buffer, BufferMem&: BufferPair.m_Mem);
2051 }
2052 m_vvFrameDelayedBufferCleanup[FrameImageIndex].clear();
2053
2054 // clear pending textures, that require deletion
2055 for(auto &Texture : m_vvFrameDelayedTextureCleanup[FrameImageIndex])
2056 {
2057 DestroyTexture(Texture);
2058 }
2059 m_vvFrameDelayedTextureCleanup[FrameImageIndex].clear();
2060
2061 for(auto &TexturePair : m_vvFrameDelayedTextTexturesCleanup[FrameImageIndex])
2062 {
2063 DestroyTextTexture(Texture&: TexturePair.first, TextureOutline&: TexturePair.second);
2064 }
2065 m_vvFrameDelayedTextTexturesCleanup[FrameImageIndex].clear();
2066
2067 m_StagingBufferCache.Cleanup(ImgIndex: FrameImageIndex);
2068 m_StagingBufferCacheImage.Cleanup(ImgIndex: FrameImageIndex);
2069 m_VertexBufferCache.Cleanup(ImgIndex: FrameImageIndex);
2070 for(auto &ImageBufferCache : m_ImageBufferCaches)
2071 ImageBufferCache.second.Cleanup(ImgIndex: FrameImageIndex);
2072 }
2073
2074 void ShrinkUnusedCaches()
2075 {
2076 size_t FreeedMemory = 0;
2077 FreeedMemory += m_StagingBufferCache.Shrink(Device&: m_VKDevice);
2078 FreeedMemory += m_StagingBufferCacheImage.Shrink(Device&: m_VKDevice);
2079 if(FreeedMemory > 0)
2080 {
2081 m_pStagingMemoryUsage->store(i: m_pStagingMemoryUsage->load(m: std::memory_order_relaxed) - FreeedMemory, m: std::memory_order_relaxed);
2082 if(IsVerbose())
2083 {
2084 dbg_msg(sys: "vulkan", fmt: "deallocated chunks of memory with size: %" PRIzu " from all frames (staging buffer)", (size_t)FreeedMemory);
2085 }
2086 }
2087 FreeedMemory = 0;
2088 FreeedMemory += m_VertexBufferCache.Shrink(Device&: m_VKDevice);
2089 if(FreeedMemory > 0)
2090 {
2091 m_pBufferMemoryUsage->store(i: m_pBufferMemoryUsage->load(m: std::memory_order_relaxed) - FreeedMemory, m: std::memory_order_relaxed);
2092 if(IsVerbose())
2093 {
2094 dbg_msg(sys: "vulkan", fmt: "deallocated chunks of memory with size: %" PRIzu " from all frames (buffer)", (size_t)FreeedMemory);
2095 }
2096 }
2097 FreeedMemory = 0;
2098 for(auto &ImageBufferCache : m_ImageBufferCaches)
2099 FreeedMemory += ImageBufferCache.second.Shrink(Device&: m_VKDevice);
2100 if(FreeedMemory > 0)
2101 {
2102 m_pTextureMemoryUsage->store(i: m_pTextureMemoryUsage->load(m: std::memory_order_relaxed) - FreeedMemory, m: std::memory_order_relaxed);
2103 if(IsVerbose())
2104 {
2105 dbg_msg(sys: "vulkan", fmt: "deallocated chunks of memory with size: %" PRIzu " from all frames (texture)", (size_t)FreeedMemory);
2106 }
2107 }
2108 }
2109
2110 [[nodiscard]] bool MemoryBarrier(VkBuffer Buffer, VkDeviceSize Offset, VkDeviceSize Size, VkAccessFlags BufferAccessType, bool BeforeCommand)
2111 {
2112 VkCommandBuffer *pMemCommandBuffer;
2113 if(!GetMemoryCommandBuffer(pMemCommandBuffer))
2114 return false;
2115 auto &MemCommandBuffer = *pMemCommandBuffer;
2116
2117 VkBufferMemoryBarrier Barrier{};
2118 Barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
2119 Barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2120 Barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2121 Barrier.buffer = Buffer;
2122 Barrier.offset = Offset;
2123 Barrier.size = Size;
2124
2125 VkPipelineStageFlags SourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
2126 VkPipelineStageFlags DestinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2127
2128 if(BeforeCommand)
2129 {
2130 Barrier.srcAccessMask = BufferAccessType;
2131 Barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2132
2133 SourceStage = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
2134 DestinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2135 }
2136 else
2137 {
2138 Barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2139 Barrier.dstAccessMask = BufferAccessType;
2140
2141 SourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2142 DestinationStage = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
2143 }
2144
2145 vkCmdPipelineBarrier(
2146 commandBuffer: MemCommandBuffer,
2147 srcStageMask: SourceStage, dstStageMask: DestinationStage,
2148 dependencyFlags: 0,
2149 memoryBarrierCount: 0, pMemoryBarriers: nullptr,
2150 bufferMemoryBarrierCount: 1, pBufferMemoryBarriers: &Barrier,
2151 imageMemoryBarrierCount: 0, pImageMemoryBarriers: nullptr);
2152
2153 return true;
2154 }
2155
2156 /************************
2157 * SWAPPING MECHANISM
2158 ************************/
2159
2160 void StartRenderThread(size_t ThreadIndex)
2161 {
2162 auto &List = m_vvThreadCommandLists[ThreadIndex];
2163 if(!List.empty())
2164 {
2165 m_vThreadHelperHadCommands[ThreadIndex] = true;
2166 auto *pThread = m_vpRenderThreads[ThreadIndex].get();
2167 std::unique_lock<std::mutex> Lock(pThread->m_Mutex);
2168 pThread->m_IsRendering = true;
2169 pThread->m_Cond.notify_one();
2170 }
2171 }
2172
2173 void FinishRenderThreads()
2174 {
2175 if(m_ThreadCount > 1)
2176 {
2177 // execute threads
2178
2179 for(size_t ThreadIndex = 0; ThreadIndex < m_ThreadCount - 1; ++ThreadIndex)
2180 {
2181 if(!m_vThreadHelperHadCommands[ThreadIndex])
2182 {
2183 StartRenderThread(ThreadIndex);
2184 }
2185 }
2186
2187 for(size_t ThreadIndex = 0; ThreadIndex < m_ThreadCount - 1; ++ThreadIndex)
2188 {
2189 if(m_vThreadHelperHadCommands[ThreadIndex])
2190 {
2191 auto &pRenderThread = m_vpRenderThreads[ThreadIndex];
2192 m_vThreadHelperHadCommands[ThreadIndex] = false;
2193 std::unique_lock<std::mutex> Lock(pRenderThread->m_Mutex);
2194 pRenderThread->m_Cond.wait(lock&: Lock, p: [&pRenderThread] { return !pRenderThread->m_IsRendering; });
2195 m_vLastPipeline[ThreadIndex + 1] = VK_NULL_HANDLE;
2196 }
2197 }
2198 }
2199 }
2200
2201 void ExecuteMemoryCommandBuffer()
2202 {
2203 if(m_vUsedMemoryCommandBuffer[m_CurImageIndex])
2204 {
2205 auto &MemoryCommandBuffer = m_vMemoryCommandBuffers[m_CurImageIndex];
2206 vkEndCommandBuffer(commandBuffer: MemoryCommandBuffer);
2207
2208 VkSubmitInfo SubmitInfo{};
2209 SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
2210
2211 SubmitInfo.commandBufferCount = 1;
2212 SubmitInfo.pCommandBuffers = &MemoryCommandBuffer;
2213 vkQueueSubmit(queue: m_VKGraphicsQueue, submitCount: 1, pSubmits: &SubmitInfo, VK_NULL_HANDLE);
2214 vkQueueWaitIdle(queue: m_VKGraphicsQueue);
2215
2216 m_vUsedMemoryCommandBuffer[m_CurImageIndex] = false;
2217 }
2218 }
2219
2220 void ClearFrameMemoryUsage()
2221 {
2222 ClearFrameData(FrameImageIndex: m_CurImageIndex);
2223 ShrinkUnusedCaches();
2224 }
2225
2226 [[nodiscard]] bool WaitFrame()
2227 {
2228 FinishRenderThreads();
2229 m_LastCommandsInPipeThreadIndex = 0;
2230
2231 UploadNonFlushedBuffers<true>();
2232
2233 auto &CommandBuffer = GetMainGraphicCommandBuffer();
2234
2235 // render threads
2236 if(m_ThreadCount > 1)
2237 {
2238 size_t ThreadedCommandsUsedCount = 0;
2239 size_t RenderThreadCount = m_ThreadCount - 1;
2240 for(size_t i = 0; i < RenderThreadCount; ++i)
2241 {
2242 if(m_vvUsedThreadDrawCommandBuffer[i + 1][m_CurImageIndex])
2243 {
2244 const auto &GraphicThreadCommandBuffer = m_vvThreadDrawCommandBuffers[i + 1][m_CurImageIndex];
2245 m_vHelperThreadDrawCommandBuffers[ThreadedCommandsUsedCount++] = GraphicThreadCommandBuffer;
2246
2247 m_vvUsedThreadDrawCommandBuffer[i + 1][m_CurImageIndex] = false;
2248 }
2249 }
2250 if(ThreadedCommandsUsedCount > 0)
2251 {
2252 vkCmdExecuteCommands(commandBuffer: CommandBuffer, commandBufferCount: ThreadedCommandsUsedCount, pCommandBuffers: m_vHelperThreadDrawCommandBuffers.data());
2253 }
2254
2255 // special case if swap chain was not completed in one runbuffer call
2256
2257 if(m_vvUsedThreadDrawCommandBuffer[0][m_CurImageIndex])
2258 {
2259 auto &GraphicThreadCommandBuffer = m_vvThreadDrawCommandBuffers[0][m_CurImageIndex];
2260 vkEndCommandBuffer(commandBuffer: GraphicThreadCommandBuffer);
2261
2262 vkCmdExecuteCommands(commandBuffer: CommandBuffer, commandBufferCount: 1, pCommandBuffers: &GraphicThreadCommandBuffer);
2263
2264 m_vvUsedThreadDrawCommandBuffer[0][m_CurImageIndex] = false;
2265 }
2266 }
2267
2268 vkCmdEndRenderPass(commandBuffer: CommandBuffer);
2269
2270 if(vkEndCommandBuffer(commandBuffer: CommandBuffer) != VK_SUCCESS)
2271 {
2272 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_RENDER_RECORDING, pErr: "Command buffer cannot be ended anymore.");
2273 return false;
2274 }
2275
2276 VkSemaphore WaitSemaphore = m_vWaitSemaphores[m_CurFrameSyncObject];
2277
2278 VkSubmitInfo SubmitInfo{};
2279 SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
2280
2281 SubmitInfo.commandBufferCount = 1;
2282 SubmitInfo.pCommandBuffers = &CommandBuffer;
2283
2284 std::array<VkCommandBuffer, 2> aCommandBuffers = {};
2285
2286 if(m_vUsedMemoryCommandBuffer[m_CurImageIndex])
2287 {
2288 auto &MemoryCommandBuffer = m_vMemoryCommandBuffers[m_CurImageIndex];
2289 vkEndCommandBuffer(commandBuffer: MemoryCommandBuffer);
2290
2291 aCommandBuffers[0] = MemoryCommandBuffer;
2292 aCommandBuffers[1] = CommandBuffer;
2293 SubmitInfo.commandBufferCount = 2;
2294 SubmitInfo.pCommandBuffers = aCommandBuffers.data();
2295
2296 m_vUsedMemoryCommandBuffer[m_CurImageIndex] = false;
2297 }
2298
2299 std::array<VkSemaphore, 1> aWaitSemaphores = {WaitSemaphore};
2300 std::array<VkPipelineStageFlags, 1> aWaitStages = {(VkPipelineStageFlags)VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
2301 SubmitInfo.waitSemaphoreCount = aWaitSemaphores.size();
2302 SubmitInfo.pWaitSemaphores = aWaitSemaphores.data();
2303 SubmitInfo.pWaitDstStageMask = aWaitStages.data();
2304
2305 std::array<VkSemaphore, 1> aSignalSemaphores = {m_vSigSemaphores[m_CurFrameSyncObject]};
2306 SubmitInfo.signalSemaphoreCount = aSignalSemaphores.size();
2307 SubmitInfo.pSignalSemaphores = aSignalSemaphores.data();
2308
2309 vkResetFences(device: m_VKDevice, fenceCount: 1, pFences: &m_vFrameFences[m_CurFrameSyncObject]);
2310
2311 VkResult QueueSubmitRes = vkQueueSubmit(queue: m_VKGraphicsQueue, submitCount: 1, pSubmits: &SubmitInfo, fence: m_vFrameFences[m_CurFrameSyncObject]);
2312 if(QueueSubmitRes != VK_SUCCESS)
2313 {
2314 const char *pCritErrorMsg = CheckVulkanCriticalError(CallResult: QueueSubmitRes);
2315 if(pCritErrorMsg != nullptr)
2316 {
2317 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_RENDER_SUBMIT_FAILED, pErr: "Submitting to graphics queue failed.", pErrStrExtra: pCritErrorMsg);
2318 return false;
2319 }
2320 }
2321
2322 std::swap(a&: m_vWaitSemaphores[m_CurFrameSyncObject], b&: m_vSigSemaphores[m_CurFrameSyncObject]);
2323
2324 VkPresentInfoKHR PresentInfo{};
2325 PresentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
2326
2327 PresentInfo.waitSemaphoreCount = aSignalSemaphores.size();
2328 PresentInfo.pWaitSemaphores = aSignalSemaphores.data();
2329
2330 std::array<VkSwapchainKHR, 1> aSwapChains = {m_VKSwapChain};
2331 PresentInfo.swapchainCount = aSwapChains.size();
2332 PresentInfo.pSwapchains = aSwapChains.data();
2333
2334 PresentInfo.pImageIndices = &m_CurImageIndex;
2335
2336 m_LastPresentedSwapChainImageIndex = m_CurImageIndex;
2337
2338 VkResult QueuePresentRes = vkQueuePresentKHR(queue: m_VKPresentQueue, pPresentInfo: &PresentInfo);
2339 if(QueuePresentRes != VK_SUCCESS && QueuePresentRes != VK_SUBOPTIMAL_KHR)
2340 {
2341 const char *pCritErrorMsg = CheckVulkanCriticalError(CallResult: QueuePresentRes);
2342 if(pCritErrorMsg != nullptr)
2343 {
2344 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_SWAP_FAILED, pErr: "Presenting graphics queue failed.", pErrStrExtra: pCritErrorMsg);
2345 return false;
2346 }
2347 }
2348
2349 m_CurFrameSyncObject = (m_CurFrameSyncObject + 1) % m_vWaitSemaphores.size();
2350 return true;
2351 }
2352
2353 [[nodiscard]] bool PrepareFrame()
2354 {
2355 if(m_RecreateSwapChain)
2356 {
2357 m_RecreateSwapChain = false;
2358 if(IsVerbose())
2359 {
2360 dbg_msg(sys: "vulkan", fmt: "recreating swap chain requested by user (prepare frame).");
2361 }
2362 RecreateSwapChain();
2363 }
2364
2365 auto AcqResult = vkAcquireNextImageKHR(device: m_VKDevice, swapchain: m_VKSwapChain, timeout: std::numeric_limits<uint64_t>::max(), semaphore: m_vSigSemaphores[m_CurFrameSyncObject], VK_NULL_HANDLE, pImageIndex: &m_CurImageIndex);
2366 if(AcqResult != VK_SUCCESS)
2367 {
2368 if(AcqResult == VK_ERROR_OUT_OF_DATE_KHR || m_RecreateSwapChain)
2369 {
2370 m_RecreateSwapChain = false;
2371 if(IsVerbose())
2372 {
2373 dbg_msg(sys: "vulkan", fmt: "recreating swap chain requested by acquire next image (prepare frame).");
2374 }
2375 RecreateSwapChain();
2376 return PrepareFrame();
2377 }
2378 else
2379 {
2380 if(AcqResult != VK_SUBOPTIMAL_KHR)
2381 dbg_msg(sys: "vulkan", fmt: "acquire next image failed %d", (int)AcqResult);
2382
2383 const char *pCritErrorMsg = CheckVulkanCriticalError(CallResult: AcqResult);
2384 if(pCritErrorMsg != nullptr)
2385 {
2386 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_SWAP_FAILED, pErr: "Acquiring next image failed.", pErrStrExtra: pCritErrorMsg);
2387 return false;
2388 }
2389 else if(AcqResult == VK_ERROR_SURFACE_LOST_KHR)
2390 {
2391 m_RenderingPaused = true;
2392 return true;
2393 }
2394 }
2395 }
2396 std::swap(a&: m_vWaitSemaphores[m_CurFrameSyncObject], b&: m_vSigSemaphores[m_CurFrameSyncObject]);
2397
2398 if(m_vImagesFences[m_CurImageIndex] != VK_NULL_HANDLE)
2399 {
2400 vkWaitForFences(device: m_VKDevice, fenceCount: 1, pFences: &m_vImagesFences[m_CurImageIndex], VK_TRUE, timeout: std::numeric_limits<uint64_t>::max());
2401 }
2402 m_vImagesFences[m_CurImageIndex] = m_vFrameFences[m_CurFrameSyncObject];
2403
2404 // next frame
2405 m_CurFrame++;
2406 m_vImageLastFrameCheck[m_CurImageIndex] = m_CurFrame;
2407
2408 // check if older frames weren't used in a long time
2409 for(size_t FrameImageIndex = 0; FrameImageIndex < m_vImageLastFrameCheck.size(); ++FrameImageIndex)
2410 {
2411 auto LastFrame = m_vImageLastFrameCheck[FrameImageIndex];
2412 if(m_CurFrame - LastFrame > (uint64_t)m_SwapChainImageCount)
2413 {
2414 if(m_vImagesFences[FrameImageIndex] != VK_NULL_HANDLE)
2415 {
2416 vkWaitForFences(device: m_VKDevice, fenceCount: 1, pFences: &m_vImagesFences[FrameImageIndex], VK_TRUE, timeout: std::numeric_limits<uint64_t>::max());
2417 ClearFrameData(FrameImageIndex);
2418 m_vImagesFences[FrameImageIndex] = VK_NULL_HANDLE;
2419 }
2420 m_vImageLastFrameCheck[FrameImageIndex] = m_CurFrame;
2421 }
2422 }
2423
2424 // clear frame's memory data
2425 ClearFrameMemoryUsage();
2426
2427 // clear frame
2428 vkResetCommandBuffer(commandBuffer: GetMainGraphicCommandBuffer(), flags: VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
2429
2430 auto &CommandBuffer = GetMainGraphicCommandBuffer();
2431 VkCommandBufferBeginInfo BeginInfo{};
2432 BeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
2433 BeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
2434
2435 if(vkBeginCommandBuffer(commandBuffer: CommandBuffer, pBeginInfo: &BeginInfo) != VK_SUCCESS)
2436 {
2437 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_RENDER_RECORDING, pErr: "Command buffer cannot be filled anymore.");
2438 return false;
2439 }
2440
2441 VkRenderPassBeginInfo RenderPassInfo{};
2442 RenderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
2443 RenderPassInfo.renderPass = m_VKRenderPass;
2444 RenderPassInfo.framebuffer = m_vFramebufferList[m_CurImageIndex];
2445 RenderPassInfo.renderArea.offset = {.x: 0, .y: 0};
2446 RenderPassInfo.renderArea.extent = m_VKSwapImgAndViewportExtent.m_SwapImageViewport;
2447
2448 VkClearValue ClearColorVal = {.color: {.float32: {m_aClearColor[0], m_aClearColor[1], m_aClearColor[2], m_aClearColor[3]}}};
2449 RenderPassInfo.clearValueCount = 1;
2450 RenderPassInfo.pClearValues = &ClearColorVal;
2451
2452 vkCmdBeginRenderPass(commandBuffer: CommandBuffer, pRenderPassBegin: &RenderPassInfo, contents: m_ThreadCount > 1 ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS : VK_SUBPASS_CONTENTS_INLINE);
2453
2454 for(auto &LastPipe : m_vLastPipeline)
2455 LastPipe = VK_NULL_HANDLE;
2456
2457 return true;
2458 }
2459
2460 void UploadStagingBuffers()
2461 {
2462 if(!m_vNonFlushedStagingBufferRange.empty())
2463 {
2464 vkFlushMappedMemoryRanges(device: m_VKDevice, memoryRangeCount: m_vNonFlushedStagingBufferRange.size(), pMemoryRanges: m_vNonFlushedStagingBufferRange.data());
2465
2466 m_vNonFlushedStagingBufferRange.clear();
2467 }
2468 }
2469
2470 template<bool FlushForRendering>
2471 void UploadNonFlushedBuffers()
2472 {
2473 // streamed vertices
2474 for(auto &StreamVertexBuffer : m_vStreamedVertexBuffers)
2475 UploadStreamedBuffer<FlushForRendering>(StreamVertexBuffer);
2476 // now the buffer objects
2477 for(auto &StreamUniformBuffer : m_vStreamedUniformBuffers)
2478 UploadStreamedBuffer<FlushForRendering>(StreamUniformBuffer);
2479
2480 UploadStagingBuffers();
2481 }
2482
2483 [[nodiscard]] bool PureMemoryFrame()
2484 {
2485 ExecuteMemoryCommandBuffer();
2486
2487 // reset streamed data
2488 UploadNonFlushedBuffers<false>();
2489
2490 ClearFrameMemoryUsage();
2491
2492 return true;
2493 }
2494
2495 [[nodiscard]] bool NextFrame()
2496 {
2497 if(!m_RenderingPaused)
2498 {
2499 if(!WaitFrame())
2500 return false;
2501 if(!PrepareFrame())
2502 return false;
2503 }
2504 // else only execute the memory command buffer
2505 else
2506 {
2507 if(!PureMemoryFrame())
2508 return false;
2509 }
2510
2511 return true;
2512 }
2513
2514 /************************
2515 * TEXTURES
2516 ************************/
2517
2518 size_t VulkanFormatToPixelSize(VkFormat Format)
2519 {
2520 if(Format == VK_FORMAT_R8G8B8_UNORM)
2521 return 3;
2522 else if(Format == VK_FORMAT_R8G8B8A8_UNORM)
2523 return 4;
2524 else if(Format == VK_FORMAT_R8_UNORM)
2525 return 1;
2526 return 4;
2527 }
2528
2529 [[nodiscard]] bool UpdateTexture(size_t TextureSlot, VkFormat Format, uint8_t *&pData, int64_t XOff, int64_t YOff, size_t Width, size_t Height)
2530 {
2531 const size_t ImageSize = Width * Height * VulkanFormatToPixelSize(Format);
2532 SMemoryBlock<s_StagingBufferImageCacheId> StagingBuffer;
2533 if(!GetStagingBufferImage(ResBlock&: StagingBuffer, pBufferData: pData, RequiredSize: ImageSize))
2534 return false;
2535
2536 auto &Tex = m_vTextures[TextureSlot];
2537
2538 if(Tex.m_RescaleCount > 0)
2539 {
2540 for(uint32_t i = 0; i < Tex.m_RescaleCount; ++i)
2541 {
2542 Width >>= 1;
2543 Height >>= 1;
2544
2545 XOff /= 2;
2546 YOff /= 2;
2547 }
2548
2549 uint8_t *pTmpData = ResizeImage(pImageData: pData, Width, Height, NewWidth: Width, NewHeight: Height, BPP: VulkanFormatToPixelSize(Format));
2550 free(ptr: pData);
2551 pData = pTmpData;
2552 }
2553
2554 if(!ImageBarrier(Image: Tex.m_Img, MipMapBase: 0, MipMapCount: Tex.m_MipMapCount, LayerBase: 0, LayerCount: 1, Format, OldLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, NewLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL))
2555 return false;
2556 if(!CopyBufferToImage(Buffer: StagingBuffer.m_Buffer, BufferOffset: StagingBuffer.m_HeapData.m_OffsetToAlign, Image: Tex.m_Img, X: XOff, Y: YOff, Width, Height, Depth: 1))
2557 return false;
2558
2559 if(Tex.m_MipMapCount > 1)
2560 {
2561 if(!BuildMipmaps(Image: Tex.m_Img, ImageFormat: Format, Width, Height, Depth: 1, MipMapLevelCount: Tex.m_MipMapCount))
2562 return false;
2563 }
2564 else
2565 {
2566 if(!ImageBarrier(Image: Tex.m_Img, MipMapBase: 0, MipMapCount: 1, LayerBase: 0, LayerCount: 1, Format, OldLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, NewLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL))
2567 return false;
2568 }
2569
2570 UploadAndFreeStagingImageMemBlock(Block&: StagingBuffer);
2571
2572 return true;
2573 }
2574
2575 [[nodiscard]] bool CreateTextureCMD(
2576 int Slot,
2577 int Width,
2578 int Height,
2579 VkFormat Format,
2580 VkFormat StoreFormat,
2581 int Flags,
2582 uint8_t *&pData)
2583 {
2584 size_t ImageIndex = (size_t)Slot;
2585 const size_t PixelSize = VulkanFormatToPixelSize(Format);
2586
2587 while(ImageIndex >= m_vTextures.size())
2588 {
2589 m_vTextures.resize(new_size: (m_vTextures.size() * 2) + 1);
2590 }
2591
2592 // resample if needed
2593 uint32_t RescaleCount = 0;
2594 if((size_t)Width > m_MaxTextureSize || (size_t)Height > m_MaxTextureSize)
2595 {
2596 do
2597 {
2598 Width >>= 1;
2599 Height >>= 1;
2600 ++RescaleCount;
2601 } while((size_t)Width > m_MaxTextureSize || (size_t)Height > m_MaxTextureSize);
2602
2603 uint8_t *pTmpData = ResizeImage(pImageData: pData, Width, Height, NewWidth: Width, NewHeight: Height, BPP: PixelSize);
2604 free(ptr: pData);
2605 pData = pTmpData;
2606 }
2607
2608 bool Requires2DTexture = (Flags & CCommandBuffer::TEXFLAG_NO_2D_TEXTURE) == 0;
2609 bool Requires2DTextureArray = (Flags & (CCommandBuffer::TEXFLAG_TO_2D_ARRAY_TEXTURE)) != 0;
2610 bool RequiresMipMaps = (Flags & CCommandBuffer::TEXFLAG_NOMIPMAPS) == 0;
2611 size_t MipMapLevelCount = 1;
2612 if(RequiresMipMaps)
2613 {
2614 VkExtent3D ImgSize{.width: (uint32_t)Width, .height: (uint32_t)Height, .depth: 1};
2615 MipMapLevelCount = ImageMipLevelCount(ImgExtent: ImgSize);
2616 if(!m_OptimalRGBAImageBlitting)
2617 MipMapLevelCount = 1;
2618 }
2619
2620 CTexture &Texture = m_vTextures[ImageIndex];
2621
2622 Texture.m_Width = Width;
2623 Texture.m_Height = Height;
2624 Texture.m_RescaleCount = RescaleCount;
2625 Texture.m_MipMapCount = MipMapLevelCount;
2626
2627 if(Requires2DTexture)
2628 {
2629 if(!CreateTextureImage(ImageIndex, NewImage&: Texture.m_Img, NewImgMem&: Texture.m_ImgMem, pData, Format, Width, Height, Depth: 1, PixelSize, MipMapLevelCount))
2630 return false;
2631 VkFormat ImgFormat = Format;
2632 VkImageView ImgView = CreateTextureImageView(TexImage: Texture.m_Img, ImgFormat, ViewType: VK_IMAGE_VIEW_TYPE_2D, Depth: 1, MipMapLevelCount);
2633 Texture.m_ImgView = ImgView;
2634 VkSampler ImgSampler = GetTextureSampler(SamplerType: SUPPORTED_SAMPLER_TYPE_REPEAT);
2635 Texture.m_aSamplers[0] = ImgSampler;
2636 ImgSampler = GetTextureSampler(SamplerType: SUPPORTED_SAMPLER_TYPE_CLAMP_TO_EDGE);
2637 Texture.m_aSamplers[1] = ImgSampler;
2638
2639 if(!CreateNewTexturedStandardDescriptorSets(TextureSlot: ImageIndex, DescrIndex: 0))
2640 return false;
2641 if(!CreateNewTexturedStandardDescriptorSets(TextureSlot: ImageIndex, DescrIndex: 1))
2642 return false;
2643 }
2644
2645 if(Requires2DTextureArray)
2646 {
2647 int Image3DWidth = Width;
2648 int Image3DHeight = Height;
2649
2650 int ConvertWidth = Width;
2651 int ConvertHeight = Height;
2652
2653 if(ConvertWidth == 0 || (ConvertWidth % 16) != 0 || ConvertHeight == 0 || (ConvertHeight % 16) != 0)
2654 {
2655 dbg_msg(sys: "vulkan", fmt: "3D/2D array texture was resized");
2656 int NewWidth = maximum<int>(a: HighestBit(OfVar: ConvertWidth), b: 16);
2657 int NewHeight = maximum<int>(a: HighestBit(OfVar: ConvertHeight), b: 16);
2658 uint8_t *pNewTexData = ResizeImage(pImageData: pData, Width: ConvertWidth, Height: ConvertHeight, NewWidth, NewHeight, BPP: PixelSize);
2659
2660 ConvertWidth = NewWidth;
2661 ConvertHeight = NewHeight;
2662
2663 free(ptr: pData);
2664 pData = pNewTexData;
2665 }
2666
2667 bool Needs3DTexDel = false;
2668 uint8_t *p3DTexData = static_cast<uint8_t *>(malloc(size: (size_t)PixelSize * ConvertWidth * ConvertHeight));
2669 if(!Texture2DTo3D(pImageBuffer: pData, ImageWidth: ConvertWidth, ImageHeight: ConvertHeight, PixelSize, SplitCountWidth: 16, SplitCountHeight: 16, pTarget3DImageData: p3DTexData, Target3DImageWidth&: Image3DWidth, Target3DImageHeight&: Image3DHeight))
2670 {
2671 free(ptr: p3DTexData);
2672 p3DTexData = nullptr;
2673 }
2674 Needs3DTexDel = true;
2675
2676 if(p3DTexData != nullptr)
2677 {
2678 const size_t ImageDepth2DArray = (size_t)16 * 16;
2679 VkExtent3D ImgSize{.width: (uint32_t)Image3DWidth, .height: (uint32_t)Image3DHeight, .depth: 1};
2680 if(RequiresMipMaps)
2681 {
2682 MipMapLevelCount = ImageMipLevelCount(ImgExtent: ImgSize);
2683 if(!m_OptimalRGBAImageBlitting)
2684 MipMapLevelCount = 1;
2685 }
2686
2687 if(!CreateTextureImage(ImageIndex, NewImage&: Texture.m_Img3D, NewImgMem&: Texture.m_Img3DMem, pData: p3DTexData, Format, Width: Image3DWidth, Height: Image3DHeight, Depth: ImageDepth2DArray, PixelSize, MipMapLevelCount))
2688 return false;
2689 VkFormat ImgFormat = Format;
2690 VkImageView ImgView = CreateTextureImageView(TexImage: Texture.m_Img3D, ImgFormat, ViewType: VK_IMAGE_VIEW_TYPE_2D_ARRAY, Depth: ImageDepth2DArray, MipMapLevelCount);
2691 Texture.m_Img3DView = ImgView;
2692 VkSampler ImgSampler = GetTextureSampler(SamplerType: SUPPORTED_SAMPLER_TYPE_2D_TEXTURE_ARRAY);
2693 Texture.m_Sampler3D = ImgSampler;
2694
2695 if(!CreateNew3DTexturedStandardDescriptorSets(TextureSlot: ImageIndex))
2696 return false;
2697
2698 if(Needs3DTexDel)
2699 free(ptr: p3DTexData);
2700 }
2701 }
2702 return true;
2703 }
2704
2705 [[nodiscard]] bool BuildMipmaps(VkImage Image, VkFormat ImageFormat, size_t Width, size_t Height, size_t Depth, size_t MipMapLevelCount)
2706 {
2707 VkCommandBuffer *pMemCommandBuffer;
2708 if(!GetMemoryCommandBuffer(pMemCommandBuffer))
2709 return false;
2710 auto &MemCommandBuffer = *pMemCommandBuffer;
2711
2712 VkImageMemoryBarrier Barrier{};
2713 Barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2714 Barrier.image = Image;
2715 Barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2716 Barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2717 Barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2718 Barrier.subresourceRange.levelCount = 1;
2719 Barrier.subresourceRange.baseArrayLayer = 0;
2720 Barrier.subresourceRange.layerCount = Depth;
2721
2722 int32_t TmpMipWidth = (int32_t)Width;
2723 int32_t TmpMipHeight = (int32_t)Height;
2724
2725 for(size_t i = 1; i < MipMapLevelCount; ++i)
2726 {
2727 Barrier.subresourceRange.baseMipLevel = i - 1;
2728 Barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2729 Barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2730 Barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2731 Barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2732
2733 vkCmdPipelineBarrier(commandBuffer: MemCommandBuffer, srcStageMask: VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask: VK_PIPELINE_STAGE_TRANSFER_BIT, dependencyFlags: 0, memoryBarrierCount: 0, pMemoryBarriers: nullptr, bufferMemoryBarrierCount: 0, pBufferMemoryBarriers: nullptr, imageMemoryBarrierCount: 1, pImageMemoryBarriers: &Barrier);
2734
2735 VkImageBlit Blit{};
2736 Blit.srcOffsets[0] = {.x: 0, .y: 0, .z: 0};
2737 Blit.srcOffsets[1] = {.x: TmpMipWidth, .y: TmpMipHeight, .z: 1};
2738 Blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2739 Blit.srcSubresource.mipLevel = i - 1;
2740 Blit.srcSubresource.baseArrayLayer = 0;
2741 Blit.srcSubresource.layerCount = Depth;
2742 Blit.dstOffsets[0] = {.x: 0, .y: 0, .z: 0};
2743 Blit.dstOffsets[1] = {.x: TmpMipWidth > 1 ? TmpMipWidth / 2 : 1, .y: TmpMipHeight > 1 ? TmpMipHeight / 2 : 1, .z: 1};
2744 Blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2745 Blit.dstSubresource.mipLevel = i;
2746 Blit.dstSubresource.baseArrayLayer = 0;
2747 Blit.dstSubresource.layerCount = Depth;
2748
2749 vkCmdBlitImage(commandBuffer: MemCommandBuffer,
2750 srcImage: Image, srcImageLayout: VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
2751 dstImage: Image, dstImageLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
2752 regionCount: 1, pRegions: &Blit,
2753 filter: m_AllowsLinearBlitting ? VK_FILTER_LINEAR : VK_FILTER_NEAREST);
2754
2755 Barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
2756 Barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
2757 Barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2758 Barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
2759
2760 vkCmdPipelineBarrier(commandBuffer: MemCommandBuffer,
2761 srcStageMask: VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask: VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, dependencyFlags: 0,
2762 memoryBarrierCount: 0, pMemoryBarriers: nullptr,
2763 bufferMemoryBarrierCount: 0, pBufferMemoryBarriers: nullptr,
2764 imageMemoryBarrierCount: 1, pImageMemoryBarriers: &Barrier);
2765
2766 if(TmpMipWidth > 1)
2767 TmpMipWidth /= 2;
2768 if(TmpMipHeight > 1)
2769 TmpMipHeight /= 2;
2770 }
2771
2772 Barrier.subresourceRange.baseMipLevel = MipMapLevelCount - 1;
2773 Barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
2774 Barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
2775 Barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2776 Barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
2777
2778 vkCmdPipelineBarrier(commandBuffer: MemCommandBuffer,
2779 srcStageMask: VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask: VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, dependencyFlags: 0,
2780 memoryBarrierCount: 0, pMemoryBarriers: nullptr,
2781 bufferMemoryBarrierCount: 0, pBufferMemoryBarriers: nullptr,
2782 imageMemoryBarrierCount: 1, pImageMemoryBarriers: &Barrier);
2783
2784 return true;
2785 }
2786
2787 [[nodiscard]] bool CreateTextureImage(size_t ImageIndex, VkImage &NewImage, SMemoryImageBlock<s_ImageBufferCacheId> &NewImgMem, const uint8_t *pData, VkFormat Format, size_t Width, size_t Height, size_t Depth, size_t PixelSize, size_t MipMapLevelCount)
2788 {
2789 VkDeviceSize ImageSize = Width * Height * Depth * PixelSize;
2790
2791 SMemoryBlock<s_StagingBufferImageCacheId> StagingBuffer;
2792 if(!GetStagingBufferImage(ResBlock&: StagingBuffer, pBufferData: pData, RequiredSize: ImageSize))
2793 return false;
2794
2795 VkFormat ImgFormat = Format;
2796
2797 if(!CreateImage(Width, Height, Depth, MipMapLevelCount, Format: ImgFormat, Tiling: VK_IMAGE_TILING_OPTIMAL, Image&: NewImage, ImageMemory&: NewImgMem))
2798 return false;
2799
2800 if(!ImageBarrier(Image: NewImage, MipMapBase: 0, MipMapCount: MipMapLevelCount, LayerBase: 0, LayerCount: Depth, Format: ImgFormat, OldLayout: VK_IMAGE_LAYOUT_UNDEFINED, NewLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL))
2801 return false;
2802 if(!CopyBufferToImage(Buffer: StagingBuffer.m_Buffer, BufferOffset: StagingBuffer.m_HeapData.m_OffsetToAlign, Image: NewImage, X: 0, Y: 0, Width: static_cast<uint32_t>(Width), Height: static_cast<uint32_t>(Height), Depth))
2803 return false;
2804
2805 UploadAndFreeStagingImageMemBlock(Block&: StagingBuffer);
2806
2807 if(MipMapLevelCount > 1)
2808 {
2809 if(!BuildMipmaps(Image: NewImage, ImageFormat: ImgFormat, Width, Height, Depth, MipMapLevelCount))
2810 return false;
2811 }
2812 else
2813 {
2814 if(!ImageBarrier(Image: NewImage, MipMapBase: 0, MipMapCount: 1, LayerBase: 0, LayerCount: Depth, Format: ImgFormat, OldLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, NewLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL))
2815 return false;
2816 }
2817
2818 return true;
2819 }
2820
2821 VkImageView CreateTextureImageView(VkImage TexImage, VkFormat ImgFormat, VkImageViewType ViewType, size_t Depth, size_t MipMapLevelCount)
2822 {
2823 return CreateImageView(Image: TexImage, Format: ImgFormat, ViewType, Depth, MipMapLevelCount);
2824 }
2825
2826 [[nodiscard]] bool CreateTextureSamplersImpl(VkSampler &CreatedSampler, VkSamplerAddressMode AddrModeU, VkSamplerAddressMode AddrModeV, VkSamplerAddressMode AddrModeW)
2827 {
2828 VkSamplerCreateInfo SamplerInfo{};
2829 SamplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
2830 SamplerInfo.magFilter = VK_FILTER_LINEAR;
2831 SamplerInfo.minFilter = VK_FILTER_LINEAR;
2832 SamplerInfo.addressModeU = AddrModeU;
2833 SamplerInfo.addressModeV = AddrModeV;
2834 SamplerInfo.addressModeW = AddrModeW;
2835 SamplerInfo.anisotropyEnable = VK_FALSE;
2836 SamplerInfo.maxAnisotropy = m_MaxSamplerAnisotropy;
2837 SamplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
2838 SamplerInfo.unnormalizedCoordinates = VK_FALSE;
2839 SamplerInfo.compareEnable = VK_FALSE;
2840 SamplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
2841 SamplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
2842 SamplerInfo.mipLodBias = (m_GlobalTextureLodBIAS / 1000.0f);
2843 SamplerInfo.minLod = -1000;
2844 SamplerInfo.maxLod = 1000;
2845
2846 if(vkCreateSampler(device: m_VKDevice, pCreateInfo: &SamplerInfo, pAllocator: nullptr, pSampler: &CreatedSampler) != VK_SUCCESS)
2847 {
2848 dbg_msg(sys: "vulkan", fmt: "failed to create texture sampler!");
2849 return false;
2850 }
2851 return true;
2852 }
2853
2854 [[nodiscard]] bool CreateTextureSamplers()
2855 {
2856 bool Ret = true;
2857 Ret &= CreateTextureSamplersImpl(CreatedSampler&: m_aSamplers[SUPPORTED_SAMPLER_TYPE_REPEAT], AddrModeU: VkSamplerAddressMode::VK_SAMPLER_ADDRESS_MODE_REPEAT, AddrModeV: VkSamplerAddressMode::VK_SAMPLER_ADDRESS_MODE_REPEAT, AddrModeW: VkSamplerAddressMode::VK_SAMPLER_ADDRESS_MODE_REPEAT);
2858 Ret &= CreateTextureSamplersImpl(CreatedSampler&: m_aSamplers[SUPPORTED_SAMPLER_TYPE_CLAMP_TO_EDGE], AddrModeU: VkSamplerAddressMode::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, AddrModeV: VkSamplerAddressMode::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, AddrModeW: VkSamplerAddressMode::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE);
2859 Ret &= CreateTextureSamplersImpl(CreatedSampler&: m_aSamplers[SUPPORTED_SAMPLER_TYPE_2D_TEXTURE_ARRAY], AddrModeU: VkSamplerAddressMode::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, AddrModeV: VkSamplerAddressMode::VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, AddrModeW: VkSamplerAddressMode::VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT);
2860 return Ret;
2861 }
2862
2863 void DestroyTextureSamplers()
2864 {
2865 vkDestroySampler(device: m_VKDevice, sampler: m_aSamplers[SUPPORTED_SAMPLER_TYPE_REPEAT], pAllocator: nullptr);
2866 vkDestroySampler(device: m_VKDevice, sampler: m_aSamplers[SUPPORTED_SAMPLER_TYPE_CLAMP_TO_EDGE], pAllocator: nullptr);
2867 vkDestroySampler(device: m_VKDevice, sampler: m_aSamplers[SUPPORTED_SAMPLER_TYPE_2D_TEXTURE_ARRAY], pAllocator: nullptr);
2868 }
2869
2870 VkSampler GetTextureSampler(ESupportedSamplerTypes SamplerType)
2871 {
2872 return m_aSamplers[SamplerType];
2873 }
2874
2875 VkImageView CreateImageView(VkImage Image, VkFormat Format, VkImageViewType ViewType, size_t Depth, size_t MipMapLevelCount)
2876 {
2877 VkImageViewCreateInfo ViewCreateInfo{};
2878 ViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
2879 ViewCreateInfo.image = Image;
2880 ViewCreateInfo.viewType = ViewType;
2881 ViewCreateInfo.format = Format;
2882 ViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2883 ViewCreateInfo.subresourceRange.baseMipLevel = 0;
2884 ViewCreateInfo.subresourceRange.levelCount = MipMapLevelCount;
2885 ViewCreateInfo.subresourceRange.baseArrayLayer = 0;
2886 ViewCreateInfo.subresourceRange.layerCount = Depth;
2887
2888 VkImageView ImageView;
2889 if(vkCreateImageView(device: m_VKDevice, pCreateInfo: &ViewCreateInfo, pAllocator: nullptr, pView: &ImageView) != VK_SUCCESS)
2890 {
2891 return VK_NULL_HANDLE;
2892 }
2893
2894 return ImageView;
2895 }
2896
2897 [[nodiscard]] bool CreateImage(uint32_t Width, uint32_t Height, uint32_t Depth, size_t MipMapLevelCount, VkFormat Format, VkImageTiling Tiling, VkImage &Image, SMemoryImageBlock<s_ImageBufferCacheId> &ImageMemory, VkImageUsageFlags ImageUsage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)
2898 {
2899 VkImageCreateInfo ImageInfo{};
2900 ImageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
2901 ImageInfo.imageType = VK_IMAGE_TYPE_2D;
2902 ImageInfo.extent.width = Width;
2903 ImageInfo.extent.height = Height;
2904 ImageInfo.extent.depth = 1;
2905 ImageInfo.mipLevels = MipMapLevelCount;
2906 ImageInfo.arrayLayers = Depth;
2907 ImageInfo.format = Format;
2908 ImageInfo.tiling = Tiling;
2909 ImageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
2910 ImageInfo.usage = ImageUsage;
2911 ImageInfo.samples = (ImageUsage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) == 0 ? VK_SAMPLE_COUNT_1_BIT : GetSampleCount();
2912 ImageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
2913
2914 if(vkCreateImage(device: m_VKDevice, pCreateInfo: &ImageInfo, pAllocator: nullptr, pImage: &Image) != VK_SUCCESS)
2915 {
2916 dbg_msg(sys: "vulkan", fmt: "failed to create image!");
2917 }
2918
2919 VkMemoryRequirements MemRequirements;
2920 vkGetImageMemoryRequirements(device: m_VKDevice, image: Image, pMemoryRequirements: &MemRequirements);
2921
2922 if(!GetImageMemory(RetBlock&: ImageMemory, RequiredSize: MemRequirements.size, RequiredAlignment: MemRequirements.alignment, RequiredMemoryTypeBits: MemRequirements.memoryTypeBits))
2923 return false;
2924
2925 vkBindImageMemory(device: m_VKDevice, image: Image, memory: ImageMemory.m_BufferMem.m_Mem, memoryOffset: ImageMemory.m_HeapData.m_OffsetToAlign);
2926
2927 return true;
2928 }
2929
2930 [[nodiscard]] bool ImageBarrier(const VkImage &Image, size_t MipMapBase, size_t MipMapCount, size_t LayerBase, size_t LayerCount, VkFormat Format, VkImageLayout OldLayout, VkImageLayout NewLayout)
2931 {
2932 VkCommandBuffer *pMemCommandBuffer;
2933 if(!GetMemoryCommandBuffer(pMemCommandBuffer))
2934 return false;
2935 auto &MemCommandBuffer = *pMemCommandBuffer;
2936
2937 VkImageMemoryBarrier Barrier{};
2938 Barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
2939 Barrier.oldLayout = OldLayout;
2940 Barrier.newLayout = NewLayout;
2941 Barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2942 Barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
2943 Barrier.image = Image;
2944 Barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
2945 Barrier.subresourceRange.baseMipLevel = MipMapBase;
2946 Barrier.subresourceRange.levelCount = MipMapCount;
2947 Barrier.subresourceRange.baseArrayLayer = LayerBase;
2948 Barrier.subresourceRange.layerCount = LayerCount;
2949
2950 VkPipelineStageFlags SourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
2951 VkPipelineStageFlags DestinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2952
2953 if(OldLayout == VK_IMAGE_LAYOUT_UNDEFINED && NewLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
2954 {
2955 Barrier.srcAccessMask = 0;
2956 Barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2957
2958 SourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
2959 DestinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2960 }
2961 else if(OldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && NewLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
2962 {
2963 Barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2964 Barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
2965
2966 SourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2967 DestinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
2968 }
2969 else if(OldLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL && NewLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
2970 {
2971 Barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
2972 Barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
2973
2974 SourceStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
2975 DestinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2976 }
2977 else if(OldLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL && NewLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR)
2978 {
2979 Barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2980 Barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
2981
2982 SourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2983 DestinationStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
2984 }
2985 else if(OldLayout == VK_IMAGE_LAYOUT_PRESENT_SRC_KHR && NewLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL)
2986 {
2987 Barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
2988 Barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
2989
2990 SourceStage = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
2991 DestinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
2992 }
2993 else if(OldLayout == VK_IMAGE_LAYOUT_UNDEFINED && NewLayout == VK_IMAGE_LAYOUT_GENERAL)
2994 {
2995 Barrier.srcAccessMask = 0;
2996 Barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
2997
2998 SourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
2999 DestinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
3000 }
3001 else if(OldLayout == VK_IMAGE_LAYOUT_GENERAL && NewLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
3002 {
3003 Barrier.srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
3004 Barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
3005
3006 SourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
3007 DestinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
3008 }
3009 else if(OldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL && NewLayout == VK_IMAGE_LAYOUT_GENERAL)
3010 {
3011 Barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
3012 Barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
3013
3014 SourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
3015 DestinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
3016 }
3017 else
3018 {
3019 dbg_msg(sys: "vulkan", fmt: "unsupported layout transition!");
3020 }
3021
3022 vkCmdPipelineBarrier(
3023 commandBuffer: MemCommandBuffer,
3024 srcStageMask: SourceStage, dstStageMask: DestinationStage,
3025 dependencyFlags: 0,
3026 memoryBarrierCount: 0, pMemoryBarriers: nullptr,
3027 bufferMemoryBarrierCount: 0, pBufferMemoryBarriers: nullptr,
3028 imageMemoryBarrierCount: 1, pImageMemoryBarriers: &Barrier);
3029
3030 return true;
3031 }
3032
3033 [[nodiscard]] bool CopyBufferToImage(VkBuffer Buffer, VkDeviceSize BufferOffset, VkImage Image, int32_t X, int32_t Y, uint32_t Width, uint32_t Height, size_t Depth)
3034 {
3035 VkCommandBuffer *pCommandBuffer;
3036 if(!GetMemoryCommandBuffer(pMemCommandBuffer&: pCommandBuffer))
3037 return false;
3038 auto &CommandBuffer = *pCommandBuffer;
3039
3040 VkBufferImageCopy Region{};
3041 Region.bufferOffset = BufferOffset;
3042 Region.bufferRowLength = 0;
3043 Region.bufferImageHeight = 0;
3044 Region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
3045 Region.imageSubresource.mipLevel = 0;
3046 Region.imageSubresource.baseArrayLayer = 0;
3047 Region.imageSubresource.layerCount = Depth;
3048 Region.imageOffset = {.x: X, .y: Y, .z: 0};
3049 Region.imageExtent = {
3050 .width: Width,
3051 .height: Height,
3052 .depth: 1};
3053
3054 vkCmdCopyBufferToImage(commandBuffer: CommandBuffer, srcBuffer: Buffer, dstImage: Image, dstImageLayout: VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, regionCount: 1, pRegions: &Region);
3055
3056 return true;
3057 }
3058
3059 /************************
3060 * BUFFERS
3061 ************************/
3062
3063 [[nodiscard]] bool CreateBufferObject(size_t BufferIndex, const void *pUploadData, VkDeviceSize BufferDataSize, bool IsOneFrameBuffer)
3064 {
3065 std::vector<uint8_t> UploadDataTmp;
3066 if(pUploadData == nullptr)
3067 {
3068 UploadDataTmp.resize(new_size: BufferDataSize);
3069 pUploadData = UploadDataTmp.data();
3070 }
3071
3072 while(BufferIndex >= m_vBufferObjects.size())
3073 {
3074 m_vBufferObjects.resize(new_size: (m_vBufferObjects.size() * 2) + 1);
3075 }
3076 auto &BufferObject = m_vBufferObjects[BufferIndex];
3077
3078 VkBuffer VertexBuffer;
3079 size_t BufferOffset = 0;
3080 if(!IsOneFrameBuffer)
3081 {
3082 SMemoryBlock<s_StagingBufferCacheId> StagingBuffer;
3083 if(!GetStagingBuffer(ResBlock&: StagingBuffer, pBufferData: pUploadData, RequiredSize: BufferDataSize))
3084 return false;
3085
3086 SMemoryBlock<s_VertexBufferCacheId> Mem;
3087 if(!GetVertexBuffer(ResBlock&: Mem, RequiredSize: BufferDataSize))
3088 return false;
3089
3090 BufferObject.m_BufferObject.m_Mem = Mem;
3091 VertexBuffer = Mem.m_Buffer;
3092 BufferOffset = Mem.m_HeapData.m_OffsetToAlign;
3093
3094 if(!MemoryBarrier(Buffer: VertexBuffer, Offset: Mem.m_HeapData.m_OffsetToAlign, Size: BufferDataSize, BufferAccessType: VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, BeforeCommand: true))
3095 return false;
3096 if(!CopyBuffer(SrcBuffer: StagingBuffer.m_Buffer, DstBuffer: VertexBuffer, SrcOffset: StagingBuffer.m_HeapData.m_OffsetToAlign, DstOffset: Mem.m_HeapData.m_OffsetToAlign, CopySize: BufferDataSize))
3097 return false;
3098 if(!MemoryBarrier(Buffer: VertexBuffer, Offset: Mem.m_HeapData.m_OffsetToAlign, Size: BufferDataSize, BufferAccessType: VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, BeforeCommand: false))
3099 return false;
3100 UploadAndFreeStagingMemBlock(Block&: StagingBuffer);
3101 }
3102 else
3103 {
3104 SDeviceMemoryBlock VertexBufferMemory;
3105 if(!CreateStreamVertexBuffer(RenderThreadIndex: ms_MainThreadIndex, NewBuffer&: VertexBuffer, NewBufferMem&: VertexBufferMemory, BufferOffset, pData: pUploadData, DataSize: BufferDataSize))
3106 return false;
3107 }
3108 BufferObject.m_IsStreamedBuffer = IsOneFrameBuffer;
3109 BufferObject.m_CurBuffer = VertexBuffer;
3110 BufferObject.m_CurBufferOffset = BufferOffset;
3111
3112 return true;
3113 }
3114
3115 void DeleteBufferObject(size_t BufferIndex)
3116 {
3117 auto &BufferObject = m_vBufferObjects[BufferIndex];
3118 if(!BufferObject.m_IsStreamedBuffer)
3119 {
3120 FreeVertexMemBlock(Block&: BufferObject.m_BufferObject.m_Mem);
3121 }
3122 BufferObject = {};
3123 }
3124
3125 [[nodiscard]] bool CopyBuffer(VkBuffer SrcBuffer, VkBuffer DstBuffer, VkDeviceSize SrcOffset, VkDeviceSize DstOffset, VkDeviceSize CopySize)
3126 {
3127 VkCommandBuffer *pCommandBuffer;
3128 if(!GetMemoryCommandBuffer(pMemCommandBuffer&: pCommandBuffer))
3129 return false;
3130 auto &CommandBuffer = *pCommandBuffer;
3131 VkBufferCopy CopyRegion{};
3132 CopyRegion.srcOffset = SrcOffset;
3133 CopyRegion.dstOffset = DstOffset;
3134 CopyRegion.size = CopySize;
3135 vkCmdCopyBuffer(commandBuffer: CommandBuffer, srcBuffer: SrcBuffer, dstBuffer: DstBuffer, regionCount: 1, pRegions: &CopyRegion);
3136
3137 return true;
3138 }
3139
3140 /************************
3141 * RENDER STATES
3142 ************************/
3143
3144 void GetStateMatrix(const CCommandBuffer::SState &State, std::array<float, (size_t)4 * 2> &Matrix)
3145 {
3146 Matrix = {
3147 // column 1
3148 2.f / (State.m_ScreenBR.x - State.m_ScreenTL.x),
3149 0,
3150 // column 2
3151 0,
3152 2.f / (State.m_ScreenBR.y - State.m_ScreenTL.y),
3153 // column 3
3154 0,
3155 0,
3156 // column 4
3157 -((State.m_ScreenTL.x + State.m_ScreenBR.x) / (State.m_ScreenBR.x - State.m_ScreenTL.x)),
3158 -((State.m_ScreenTL.y + State.m_ScreenBR.y) / (State.m_ScreenBR.y - State.m_ScreenTL.y)),
3159 };
3160 }
3161
3162 [[nodiscard]] bool GetIsTextured(const CCommandBuffer::SState &State)
3163 {
3164 return State.m_Texture != -1;
3165 }
3166
3167 size_t GetAddressModeIndex(const CCommandBuffer::SState &State)
3168 {
3169 return State.m_WrapMode == CCommandBuffer::WRAP_REPEAT ? VULKAN_BACKEND_ADDRESS_MODE_REPEAT : VULKAN_BACKEND_ADDRESS_MODE_CLAMP_EDGES;
3170 }
3171
3172 size_t GetBlendModeIndex(const CCommandBuffer::SState &State)
3173 {
3174 return State.m_BlendMode == CCommandBuffer::BLEND_ADDITIVE ? VULKAN_BACKEND_BLEND_MODE_ADDITATIVE : (State.m_BlendMode == CCommandBuffer::BLEND_NONE ? VULKAN_BACKEND_BLEND_MODE_NONE : VULKAN_BACKEND_BLEND_MODE_ALPHA);
3175 }
3176
3177 size_t GetDynamicModeIndexFromState(const CCommandBuffer::SState &State) const
3178 {
3179 return (State.m_ClipEnable || m_HasDynamicViewport || m_VKSwapImgAndViewportExtent.m_HasForcedViewport) ? VULKAN_BACKEND_CLIP_MODE_DYNAMIC_SCISSOR_AND_VIEWPORT : VULKAN_BACKEND_CLIP_MODE_NONE;
3180 }
3181
3182 size_t GetDynamicModeIndexFromExecBuffer(const SRenderCommandExecuteBuffer &ExecBuffer)
3183 {
3184 return (ExecBuffer.m_HasDynamicState) ? VULKAN_BACKEND_CLIP_MODE_DYNAMIC_SCISSOR_AND_VIEWPORT : VULKAN_BACKEND_CLIP_MODE_NONE;
3185 }
3186
3187 VkPipeline &GetPipeline(SPipelineContainer &Container, bool IsTextured, size_t BlendModeIndex, size_t DynamicIndex)
3188 {
3189 return Container.m_aaaPipelines[BlendModeIndex][DynamicIndex][(size_t)IsTextured];
3190 }
3191
3192 VkPipelineLayout &GetPipeLayout(SPipelineContainer &Container, bool IsTextured, size_t BlendModeIndex, size_t DynamicIndex)
3193 {
3194 return Container.m_aaaPipelineLayouts[BlendModeIndex][DynamicIndex][(size_t)IsTextured];
3195 }
3196
3197 VkPipelineLayout &GetStandardPipeLayout(bool IsLineGeometry, bool IsTextured, size_t BlendModeIndex, size_t DynamicIndex)
3198 {
3199 if(IsLineGeometry)
3200 return GetPipeLayout(Container&: m_StandardLinePipeline, IsTextured, BlendModeIndex, DynamicIndex);
3201 else
3202 return GetPipeLayout(Container&: m_StandardPipeline, IsTextured, BlendModeIndex, DynamicIndex);
3203 }
3204
3205 VkPipeline &GetStandardPipe(bool IsLineGeometry, bool IsTextured, size_t BlendModeIndex, size_t DynamicIndex)
3206 {
3207 if(IsLineGeometry)
3208 return GetPipeline(Container&: m_StandardLinePipeline, IsTextured, BlendModeIndex, DynamicIndex);
3209 else
3210 return GetPipeline(Container&: m_StandardPipeline, IsTextured, BlendModeIndex, DynamicIndex);
3211 }
3212
3213 VkPipelineLayout &GetTileLayerPipeLayout(bool IsBorder, bool IsTextured, size_t BlendModeIndex, size_t DynamicIndex)
3214 {
3215 if(!IsBorder)
3216 return GetPipeLayout(Container&: m_TilePipeline, IsTextured, BlendModeIndex, DynamicIndex);
3217 else
3218 return GetPipeLayout(Container&: m_TileBorderPipeline, IsTextured, BlendModeIndex, DynamicIndex);
3219 }
3220
3221 VkPipeline &GetTileLayerPipe(bool IsBorder, bool IsTextured, size_t BlendModeIndex, size_t DynamicIndex)
3222 {
3223 if(!IsBorder)
3224 return GetPipeline(Container&: m_TilePipeline, IsTextured, BlendModeIndex, DynamicIndex);
3225 else
3226 return GetPipeline(Container&: m_TileBorderPipeline, IsTextured, BlendModeIndex, DynamicIndex);
3227 }
3228
3229 void GetStateIndices(const SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SState &State, bool &IsTextured, size_t &BlendModeIndex, size_t &DynamicIndex, size_t &AddressModeIndex)
3230 {
3231 IsTextured = GetIsTextured(State);
3232 AddressModeIndex = GetAddressModeIndex(State);
3233 BlendModeIndex = GetBlendModeIndex(State);
3234 DynamicIndex = GetDynamicModeIndexFromExecBuffer(ExecBuffer);
3235 }
3236
3237 void ExecBufferFillDynamicStates(const CCommandBuffer::SState &State, SRenderCommandExecuteBuffer &ExecBuffer)
3238 {
3239 size_t DynamicStateIndex = GetDynamicModeIndexFromState(State);
3240 if(DynamicStateIndex == VULKAN_BACKEND_CLIP_MODE_DYNAMIC_SCISSOR_AND_VIEWPORT)
3241 {
3242 VkViewport Viewport;
3243 if(m_HasDynamicViewport)
3244 {
3245 Viewport.x = (float)m_DynamicViewportOffset.x;
3246 Viewport.y = (float)m_DynamicViewportOffset.y;
3247 Viewport.width = (float)m_DynamicViewportSize.width;
3248 Viewport.height = (float)m_DynamicViewportSize.height;
3249 Viewport.minDepth = 0.0f;
3250 Viewport.maxDepth = 1.0f;
3251 }
3252 // else check if there is a forced viewport
3253 else if(m_VKSwapImgAndViewportExtent.m_HasForcedViewport)
3254 {
3255 Viewport.x = 0.0f;
3256 Viewport.y = 0.0f;
3257 Viewport.width = (float)m_VKSwapImgAndViewportExtent.m_ForcedViewport.width;
3258 Viewport.height = (float)m_VKSwapImgAndViewportExtent.m_ForcedViewport.height;
3259 Viewport.minDepth = 0.0f;
3260 Viewport.maxDepth = 1.0f;
3261 }
3262 else
3263 {
3264 Viewport.x = 0.0f;
3265 Viewport.y = 0.0f;
3266 Viewport.width = (float)m_VKSwapImgAndViewportExtent.m_SwapImageViewport.width;
3267 Viewport.height = (float)m_VKSwapImgAndViewportExtent.m_SwapImageViewport.height;
3268 Viewport.minDepth = 0.0f;
3269 Viewport.maxDepth = 1.0f;
3270 }
3271
3272 VkRect2D Scissor;
3273 // convert from OGL to vulkan clip
3274
3275 // the scissor always assumes the presented viewport, because the front-end keeps the calculation
3276 // for the forced viewport in sync
3277 auto ScissorViewport = m_VKSwapImgAndViewportExtent.GetPresentedImageViewport();
3278 if(State.m_ClipEnable)
3279 {
3280 int32_t ScissorY = (int32_t)ScissorViewport.height - ((int32_t)State.m_ClipY + (int32_t)State.m_ClipH);
3281 uint32_t ScissorH = (int32_t)State.m_ClipH;
3282 Scissor.offset = {.x: (int32_t)State.m_ClipX, .y: ScissorY};
3283 Scissor.extent = {.width: (uint32_t)State.m_ClipW, .height: ScissorH};
3284 }
3285 else
3286 {
3287 Scissor.offset = {.x: 0, .y: 0};
3288 Scissor.extent = {.width: (uint32_t)ScissorViewport.width, .height: (uint32_t)ScissorViewport.height};
3289 }
3290
3291 // if there is a dynamic viewport make sure the scissor data is scaled down to that
3292 if(m_HasDynamicViewport)
3293 {
3294 Scissor.offset.x = (int32_t)(((float)Scissor.offset.x / (float)ScissorViewport.width) * (float)m_DynamicViewportSize.width) + m_DynamicViewportOffset.x;
3295 Scissor.offset.y = (int32_t)(((float)Scissor.offset.y / (float)ScissorViewport.height) * (float)m_DynamicViewportSize.height) + m_DynamicViewportOffset.y;
3296 Scissor.extent.width = (uint32_t)(((float)Scissor.extent.width / (float)ScissorViewport.width) * (float)m_DynamicViewportSize.width);
3297 Scissor.extent.height = (uint32_t)(((float)Scissor.extent.height / (float)ScissorViewport.height) * (float)m_DynamicViewportSize.height);
3298 }
3299
3300 Viewport.x = clamp(val: Viewport.x, lo: 0.0f, hi: std::numeric_limits<decltype(Viewport.x)>::max());
3301 Viewport.y = clamp(val: Viewport.y, lo: 0.0f, hi: std::numeric_limits<decltype(Viewport.y)>::max());
3302
3303 Scissor.offset.x = clamp(val: Scissor.offset.x, lo: 0, hi: std::numeric_limits<decltype(Scissor.offset.x)>::max());
3304 Scissor.offset.y = clamp(val: Scissor.offset.y, lo: 0, hi: std::numeric_limits<decltype(Scissor.offset.y)>::max());
3305
3306 ExecBuffer.m_HasDynamicState = true;
3307 ExecBuffer.m_Viewport = Viewport;
3308 ExecBuffer.m_Scissor = Scissor;
3309 }
3310 else
3311 {
3312 ExecBuffer.m_HasDynamicState = false;
3313 }
3314 }
3315
3316 void BindPipeline(size_t RenderThreadIndex, VkCommandBuffer &CommandBuffer, SRenderCommandExecuteBuffer &ExecBuffer, VkPipeline &BindingPipe, const CCommandBuffer::SState &State)
3317 {
3318 if(m_vLastPipeline[RenderThreadIndex] != BindingPipe)
3319 {
3320 vkCmdBindPipeline(commandBuffer: CommandBuffer, pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline: BindingPipe);
3321 m_vLastPipeline[RenderThreadIndex] = BindingPipe;
3322 }
3323
3324 size_t DynamicStateIndex = GetDynamicModeIndexFromExecBuffer(ExecBuffer);
3325 if(DynamicStateIndex == VULKAN_BACKEND_CLIP_MODE_DYNAMIC_SCISSOR_AND_VIEWPORT)
3326 {
3327 vkCmdSetViewport(commandBuffer: CommandBuffer, firstViewport: 0, viewportCount: 1, pViewports: &ExecBuffer.m_Viewport);
3328 vkCmdSetScissor(commandBuffer: CommandBuffer, firstScissor: 0, scissorCount: 1, pScissors: &ExecBuffer.m_Scissor);
3329 }
3330 }
3331
3332 /**************************
3333 * RENDERING IMPLEMENTATION
3334 ***************************/
3335
3336 void RenderTileLayer_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, size_t DrawCalls, const CCommandBuffer::SState &State, size_t BufferContainerIndex)
3337 {
3338 size_t BufferObjectIndex = (size_t)m_vBufferContainers[BufferContainerIndex].m_BufferObjectIndex;
3339 const auto &BufferObject = m_vBufferObjects[BufferObjectIndex];
3340
3341 ExecBuffer.m_Buffer = BufferObject.m_CurBuffer;
3342 ExecBuffer.m_BufferOff = BufferObject.m_CurBufferOffset;
3343
3344 bool IsTextured = GetIsTextured(State);
3345 if(IsTextured)
3346 {
3347 ExecBuffer.m_aDescriptors[0] = m_vTextures[State.m_Texture].m_VKStandard3DTexturedDescrSet;
3348 }
3349
3350 ExecBuffer.m_IndexBuffer = m_RenderIndexBuffer;
3351
3352 ExecBuffer.m_EstimatedRenderCallCount = DrawCalls;
3353
3354 ExecBufferFillDynamicStates(State, ExecBuffer);
3355 }
3356
3357 [[nodiscard]] bool RenderTileLayer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SState &State, bool IsBorder, const GL_SColorf &Color, const vec2 &Scale, const vec2 &Off, size_t IndicesDrawNum, char *const *pIndicesOffsets, const unsigned int *pDrawCount)
3358 {
3359 std::array<float, (size_t)4 * 2> m;
3360 GetStateMatrix(State, Matrix&: m);
3361
3362 bool IsTextured;
3363 size_t BlendModeIndex;
3364 size_t DynamicIndex;
3365 size_t AddressModeIndex;
3366 GetStateIndices(ExecBuffer, State, IsTextured, BlendModeIndex, DynamicIndex, AddressModeIndex);
3367 auto &PipeLayout = GetTileLayerPipeLayout(IsBorder, IsTextured, BlendModeIndex, DynamicIndex);
3368 auto &PipeLine = GetTileLayerPipe(IsBorder, IsTextured, BlendModeIndex, DynamicIndex);
3369
3370 VkCommandBuffer *pCommandBuffer;
3371 if(!GetGraphicCommandBuffer(pDrawCommandBuffer&: pCommandBuffer, RenderThreadIndex: ExecBuffer.m_ThreadIndex))
3372 return false;
3373 auto &CommandBuffer = *pCommandBuffer;
3374
3375 BindPipeline(RenderThreadIndex: ExecBuffer.m_ThreadIndex, CommandBuffer, ExecBuffer, BindingPipe&: PipeLine, State);
3376
3377 std::array<VkBuffer, 1> aVertexBuffers = {ExecBuffer.m_Buffer};
3378 std::array<VkDeviceSize, 1> aOffsets = {(VkDeviceSize)ExecBuffer.m_BufferOff};
3379 vkCmdBindVertexBuffers(commandBuffer: CommandBuffer, firstBinding: 0, bindingCount: 1, pBuffers: aVertexBuffers.data(), pOffsets: aOffsets.data());
3380
3381 if(IsTextured)
3382 {
3383 vkCmdBindDescriptorSets(commandBuffer: CommandBuffer, pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, layout: PipeLayout, firstSet: 0, descriptorSetCount: 1, pDescriptorSets: &ExecBuffer.m_aDescriptors[0].m_Descriptor, dynamicOffsetCount: 0, pDynamicOffsets: nullptr);
3384 }
3385
3386 SUniformTileGPosBorder VertexPushConstants;
3387 size_t VertexPushConstantSize = sizeof(SUniformTileGPos);
3388 SUniformTileGVertColor FragPushConstants;
3389 size_t FragPushConstantSize = sizeof(SUniformTileGVertColor);
3390
3391 mem_copy(dest: VertexPushConstants.m_aPos, source: m.data(), size: m.size() * sizeof(float));
3392 FragPushConstants = Color;
3393
3394 if(IsBorder)
3395 {
3396 VertexPushConstants.m_Scale = Scale;
3397 VertexPushConstants.m_Offset = Off;
3398 VertexPushConstantSize = sizeof(SUniformTileGPosBorder);
3399 }
3400
3401 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_VERTEX_BIT, offset: 0, size: VertexPushConstantSize, pValues: &VertexPushConstants);
3402 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, offset: sizeof(SUniformTileGPosBorder) + sizeof(SUniformTileGVertColorAlign), size: FragPushConstantSize, pValues: &FragPushConstants);
3403
3404 size_t DrawCount = (size_t)IndicesDrawNum;
3405 vkCmdBindIndexBuffer(commandBuffer: CommandBuffer, buffer: ExecBuffer.m_IndexBuffer, offset: 0, indexType: VK_INDEX_TYPE_UINT32);
3406 for(size_t i = 0; i < DrawCount; ++i)
3407 {
3408 VkDeviceSize IndexOffset = (VkDeviceSize)((ptrdiff_t)pIndicesOffsets[i] / sizeof(uint32_t));
3409
3410 vkCmdDrawIndexed(commandBuffer: CommandBuffer, indexCount: static_cast<uint32_t>(pDrawCount[i]), instanceCount: 1, firstIndex: IndexOffset, vertexOffset: 0, firstInstance: 0);
3411 }
3412
3413 return true;
3414 }
3415
3416 template<typename TName, bool Is3DTextured>
3417 [[nodiscard]] bool RenderStandard(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SState &State, int PrimType, const TName *pVertices, int PrimitiveCount)
3418 {
3419 std::array<float, (size_t)4 * 2> m;
3420 GetStateMatrix(State, Matrix&: m);
3421
3422 bool IsLineGeometry = PrimType == CCommandBuffer::PRIMTYPE_LINES;
3423
3424 bool IsTextured;
3425 size_t BlendModeIndex;
3426 size_t DynamicIndex;
3427 size_t AddressModeIndex;
3428 GetStateIndices(ExecBuffer, State, IsTextured, BlendModeIndex, DynamicIndex, AddressModeIndex);
3429 auto &PipeLayout = Is3DTextured ? GetPipeLayout(Container&: m_Standard3DPipeline, IsTextured, BlendModeIndex, DynamicIndex) : GetStandardPipeLayout(IsLineGeometry, IsTextured, BlendModeIndex, DynamicIndex);
3430 auto &PipeLine = Is3DTextured ? GetPipeline(Container&: m_Standard3DPipeline, IsTextured, BlendModeIndex, DynamicIndex) : GetStandardPipe(IsLineGeometry, IsTextured, BlendModeIndex, DynamicIndex);
3431
3432 VkCommandBuffer *pCommandBuffer;
3433 if(!GetGraphicCommandBuffer(pDrawCommandBuffer&: pCommandBuffer, RenderThreadIndex: ExecBuffer.m_ThreadIndex))
3434 return false;
3435 auto &CommandBuffer = *pCommandBuffer;
3436
3437 BindPipeline(RenderThreadIndex: ExecBuffer.m_ThreadIndex, CommandBuffer, ExecBuffer, BindingPipe&: PipeLine, State);
3438
3439 size_t VertPerPrim = 2;
3440 bool IsIndexed = false;
3441 if(PrimType == CCommandBuffer::PRIMTYPE_QUADS)
3442 {
3443 VertPerPrim = 4;
3444 IsIndexed = true;
3445 }
3446 else if(PrimType == CCommandBuffer::PRIMTYPE_TRIANGLES)
3447 {
3448 VertPerPrim = 3;
3449 }
3450
3451 VkBuffer VKBuffer;
3452 SDeviceMemoryBlock VKBufferMem;
3453 size_t BufferOff = 0;
3454 if(!CreateStreamVertexBuffer(RenderThreadIndex: ExecBuffer.m_ThreadIndex, NewBuffer&: VKBuffer, NewBufferMem&: VKBufferMem, BufferOffset&: BufferOff, pData: pVertices, DataSize: VertPerPrim * sizeof(TName) * PrimitiveCount))
3455 return false;
3456
3457 std::array<VkBuffer, 1> aVertexBuffers = {VKBuffer};
3458 std::array<VkDeviceSize, 1> aOffsets = {(VkDeviceSize)BufferOff};
3459 vkCmdBindVertexBuffers(commandBuffer: CommandBuffer, firstBinding: 0, bindingCount: 1, pBuffers: aVertexBuffers.data(), pOffsets: aOffsets.data());
3460
3461 if(IsIndexed)
3462 vkCmdBindIndexBuffer(commandBuffer: CommandBuffer, buffer: ExecBuffer.m_IndexBuffer, offset: 0, indexType: VK_INDEX_TYPE_UINT32);
3463
3464 if(IsTextured)
3465 {
3466 vkCmdBindDescriptorSets(commandBuffer: CommandBuffer, pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, layout: PipeLayout, firstSet: 0, descriptorSetCount: 1, pDescriptorSets: &ExecBuffer.m_aDescriptors[0].m_Descriptor, dynamicOffsetCount: 0, pDynamicOffsets: nullptr);
3467 }
3468
3469 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_VERTEX_BIT, offset: 0, size: sizeof(SUniformGPos), pValues: m.data());
3470
3471 if(IsIndexed)
3472 vkCmdDrawIndexed(commandBuffer: CommandBuffer, indexCount: static_cast<uint32_t>(PrimitiveCount * 6), instanceCount: 1, firstIndex: 0, vertexOffset: 0, firstInstance: 0);
3473 else
3474 vkCmdDraw(commandBuffer: CommandBuffer, vertexCount: static_cast<uint32_t>(PrimitiveCount * VertPerPrim), instanceCount: 1, firstVertex: 0, firstInstance: 0);
3475
3476 return true;
3477 }
3478
3479public:
3480 CCommandProcessorFragment_Vulkan()
3481 {
3482 m_vTextures.reserve(n: CCommandBuffer::MAX_TEXTURES);
3483 }
3484
3485 /************************
3486 * VULKAN SETUP CODE
3487 ************************/
3488
3489 [[nodiscard]] bool GetVulkanExtensions(SDL_Window *pWindow, std::vector<std::string> &vVKExtensions)
3490 {
3491 unsigned int ExtCount = 0;
3492 if(!SDL_Vulkan_GetInstanceExtensions(window: pWindow, pCount: &ExtCount, pNames: nullptr))
3493 {
3494 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Could not get instance extensions from SDL.");
3495 return false;
3496 }
3497
3498 std::vector<const char *> vExtensionList(ExtCount);
3499 if(!SDL_Vulkan_GetInstanceExtensions(window: pWindow, pCount: &ExtCount, pNames: vExtensionList.data()))
3500 {
3501 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Could not get instance extensions from SDL.");
3502 return false;
3503 }
3504
3505 for(uint32_t i = 0; i < ExtCount; i++)
3506 {
3507 vVKExtensions.emplace_back(args&: vExtensionList[i]);
3508 }
3509
3510 return true;
3511 }
3512
3513 std::set<std::string> OurVKLayers()
3514 {
3515 std::set<std::string> OurLayers;
3516
3517 if(g_Config.m_DbgGfx == DEBUG_GFX_MODE_MINIMUM || g_Config.m_DbgGfx == DEBUG_GFX_MODE_ALL)
3518 {
3519 OurLayers.emplace(args: "VK_LAYER_KHRONOS_validation");
3520 // deprecated, but VK_LAYER_KHRONOS_validation was released after vulkan 1.1
3521 OurLayers.emplace(args: "VK_LAYER_LUNARG_standard_validation");
3522 }
3523
3524 return OurLayers;
3525 }
3526
3527 std::set<std::string> OurDeviceExtensions()
3528 {
3529 std::set<std::string> OurExt;
3530 OurExt.emplace(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
3531 return OurExt;
3532 }
3533
3534 std::vector<VkImageUsageFlags> OurImageUsages()
3535 {
3536 std::vector<VkImageUsageFlags> vImgUsages;
3537
3538 vImgUsages.emplace_back(args: VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT);
3539 vImgUsages.emplace_back(args: VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
3540
3541 return vImgUsages;
3542 }
3543
3544 [[nodiscard]] bool GetVulkanLayers(std::vector<std::string> &vVKLayers)
3545 {
3546 uint32_t LayerCount = 0;
3547 VkResult Res = vkEnumerateInstanceLayerProperties(pPropertyCount: &LayerCount, NULL);
3548 if(Res != VK_SUCCESS)
3549 {
3550 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Could not get vulkan layers.");
3551 return false;
3552 }
3553
3554 std::vector<VkLayerProperties> vVKInstanceLayers(LayerCount);
3555 Res = vkEnumerateInstanceLayerProperties(pPropertyCount: &LayerCount, pProperties: vVKInstanceLayers.data());
3556 if(Res != VK_SUCCESS)
3557 {
3558 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Could not get vulkan layers.");
3559 return false;
3560 }
3561
3562 std::set<std::string> ReqLayerNames = OurVKLayers();
3563 vVKLayers.clear();
3564 for(const auto &LayerName : vVKInstanceLayers)
3565 {
3566 auto it = ReqLayerNames.find(x: std::string(LayerName.layerName));
3567 if(it != ReqLayerNames.end())
3568 {
3569 vVKLayers.emplace_back(args: LayerName.layerName);
3570 }
3571 }
3572
3573 return true;
3574 }
3575
3576 [[nodiscard]] bool CreateVulkanInstance(const std::vector<std::string> &vVKLayers, const std::vector<std::string> &vVKExtensions, bool TryDebugExtensions)
3577 {
3578 std::vector<const char *> vLayersCStr;
3579 vLayersCStr.reserve(n: vVKLayers.size());
3580 for(const auto &Layer : vVKLayers)
3581 vLayersCStr.emplace_back(args: Layer.c_str());
3582
3583 std::vector<const char *> vExtCStr;
3584 vExtCStr.reserve(n: vVKExtensions.size() + 1);
3585 for(const auto &Ext : vVKExtensions)
3586 vExtCStr.emplace_back(args: Ext.c_str());
3587
3588#ifdef VK_EXT_debug_utils
3589 if(TryDebugExtensions && (g_Config.m_DbgGfx == DEBUG_GFX_MODE_MINIMUM || g_Config.m_DbgGfx == DEBUG_GFX_MODE_ALL))
3590 {
3591 // debug message support
3592 vExtCStr.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
3593 }
3594#endif
3595
3596 VkApplicationInfo VKAppInfo = {};
3597 VKAppInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
3598 VKAppInfo.pNext = NULL;
3599 VKAppInfo.pApplicationName = "DDNet";
3600 VKAppInfo.applicationVersion = 1;
3601 VKAppInfo.pEngineName = "DDNet-Vulkan";
3602 VKAppInfo.engineVersion = 1;
3603 VKAppInfo.apiVersion = VK_API_VERSION_1_1;
3604
3605 void *pExt = nullptr;
3606#if defined(VK_EXT_validation_features) && VK_EXT_VALIDATION_FEATURES_SPEC_VERSION >= 5
3607 VkValidationFeaturesEXT Features = {};
3608 std::array<VkValidationFeatureEnableEXT, 2> aEnables = {VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT, VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT};
3609 if(TryDebugExtensions && (g_Config.m_DbgGfx == DEBUG_GFX_MODE_AFFECTS_PERFORMANCE || g_Config.m_DbgGfx == DEBUG_GFX_MODE_ALL))
3610 {
3611 Features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
3612 Features.enabledValidationFeatureCount = aEnables.size();
3613 Features.pEnabledValidationFeatures = aEnables.data();
3614
3615 pExt = &Features;
3616 }
3617#endif
3618
3619 VkInstanceCreateInfo VKInstanceInfo = {};
3620 VKInstanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
3621 VKInstanceInfo.pNext = pExt;
3622 VKInstanceInfo.flags = 0;
3623 VKInstanceInfo.pApplicationInfo = &VKAppInfo;
3624 VKInstanceInfo.enabledExtensionCount = static_cast<uint32_t>(vExtCStr.size());
3625 VKInstanceInfo.ppEnabledExtensionNames = vExtCStr.data();
3626 VKInstanceInfo.enabledLayerCount = static_cast<uint32_t>(vLayersCStr.size());
3627 VKInstanceInfo.ppEnabledLayerNames = vLayersCStr.data();
3628
3629 bool TryAgain = false;
3630
3631 VkResult Res = vkCreateInstance(pCreateInfo: &VKInstanceInfo, NULL, pInstance: &m_VKInstance);
3632 const char *pCritErrorMsg = CheckVulkanCriticalError(CallResult: Res);
3633 if(pCritErrorMsg != nullptr)
3634 {
3635 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating instance failed.", pErrStrExtra: pCritErrorMsg);
3636 return false;
3637 }
3638 else if(Res == VK_ERROR_LAYER_NOT_PRESENT || Res == VK_ERROR_EXTENSION_NOT_PRESENT)
3639 TryAgain = true;
3640
3641 if(TryAgain && TryDebugExtensions)
3642 return CreateVulkanInstance(vVKLayers, vVKExtensions, TryDebugExtensions: false);
3643
3644 return true;
3645 }
3646
3647 STWGraphicGpu::ETWGraphicsGpuType VKGPUTypeToGraphicsGpuType(VkPhysicalDeviceType VKGPUType)
3648 {
3649 if(VKGPUType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU)
3650 return STWGraphicGpu::ETWGraphicsGpuType::GRAPHICS_GPU_TYPE_DISCRETE;
3651 else if(VKGPUType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU)
3652 return STWGraphicGpu::ETWGraphicsGpuType::GRAPHICS_GPU_TYPE_INTEGRATED;
3653 else if(VKGPUType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU)
3654 return STWGraphicGpu::ETWGraphicsGpuType::GRAPHICS_GPU_TYPE_VIRTUAL;
3655 else if(VKGPUType == VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU)
3656 return STWGraphicGpu::ETWGraphicsGpuType::GRAPHICS_GPU_TYPE_CPU;
3657
3658 return STWGraphicGpu::ETWGraphicsGpuType::GRAPHICS_GPU_TYPE_CPU;
3659 }
3660
3661 // from: https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5c3986798afc39d736b825bf8a5fbf92b8d9ed49/includes/functions.php#L364
3662 const char *GetDriverVerson(char (&aBuff)[256], uint32_t DriverVersion, uint32_t VendorId)
3663 {
3664 // NVIDIA
3665 if(VendorId == 4318)
3666 {
3667 str_format(buffer: aBuff, buffer_size: std::size(aBuff), format: "%d.%d.%d.%d",
3668 (DriverVersion >> 22) & 0x3ff,
3669 (DriverVersion >> 14) & 0x0ff,
3670 (DriverVersion >> 6) & 0x0ff,
3671 (DriverVersion)&0x003f);
3672 }
3673#ifdef CONF_FAMILY_WINDOWS
3674 // windows only
3675 else if(VendorId == 0x8086)
3676 {
3677 str_format(aBuff, std::size(aBuff),
3678 "%d.%d",
3679 (DriverVersion >> 14),
3680 (DriverVersion)&0x3fff);
3681 }
3682#endif
3683 else
3684 {
3685 // Use Vulkan version conventions if vendor mapping is not available
3686 str_format(buffer: aBuff, buffer_size: std::size(aBuff),
3687 format: "%d.%d.%d",
3688 (DriverVersion >> 22),
3689 (DriverVersion >> 12) & 0x3ff,
3690 DriverVersion & 0xfff);
3691 }
3692
3693 return aBuff;
3694 }
3695
3696 [[nodiscard]] bool SelectGpu(char *pRendererName, char *pVendorName, char *pVersionName)
3697 {
3698 uint32_t DevicesCount = 0;
3699 auto Res = vkEnumeratePhysicalDevices(instance: m_VKInstance, pPhysicalDeviceCount: &DevicesCount, pPhysicalDevices: nullptr);
3700 if(Res != VK_SUCCESS)
3701 {
3702 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: CheckVulkanCriticalError(CallResult: Res));
3703 return false;
3704 }
3705 if(DevicesCount == 0)
3706 {
3707 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "No vulkan compatible devices found.");
3708 return false;
3709 }
3710
3711 std::vector<VkPhysicalDevice> vDeviceList(DevicesCount);
3712 Res = vkEnumeratePhysicalDevices(instance: m_VKInstance, pPhysicalDeviceCount: &DevicesCount, pPhysicalDevices: vDeviceList.data());
3713 if(Res != VK_SUCCESS && Res != VK_INCOMPLETE)
3714 {
3715 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: CheckVulkanCriticalError(CallResult: Res));
3716 return false;
3717 }
3718 if(DevicesCount == 0)
3719 {
3720 SetWarning(WarningType: EGfxWarningType::GFX_WARNING_TYPE_INIT_FAILED_MISSING_INTEGRATED_GPU_DRIVER, pWarning: "No vulkan compatible devices found.");
3721 return false;
3722 }
3723 // make sure to use the correct amount of devices available
3724 // the amount of physical devices can be smaller than the amount of devices reported
3725 // see vkEnumeratePhysicalDevices for details
3726 vDeviceList.resize(new_size: DevicesCount);
3727
3728 size_t Index = 0;
3729 std::vector<VkPhysicalDeviceProperties> vDevicePropList(vDeviceList.size());
3730 m_pGpuList->m_vGpus.reserve(n: vDeviceList.size());
3731
3732 size_t FoundDeviceIndex = 0;
3733 size_t FoundGpuType = STWGraphicGpu::ETWGraphicsGpuType::GRAPHICS_GPU_TYPE_INVALID;
3734
3735 STWGraphicGpu::ETWGraphicsGpuType AutoGpuType = STWGraphicGpu::ETWGraphicsGpuType::GRAPHICS_GPU_TYPE_INVALID;
3736
3737 bool IsAutoGpu = str_comp(a: g_Config.m_GfxGpuName, b: "auto") == 0;
3738
3739 for(auto &CurDevice : vDeviceList)
3740 {
3741 vkGetPhysicalDeviceProperties(physicalDevice: CurDevice, pProperties: &(vDevicePropList[Index]));
3742
3743 auto &DeviceProp = vDevicePropList[Index];
3744
3745 STWGraphicGpu::ETWGraphicsGpuType GPUType = VKGPUTypeToGraphicsGpuType(VKGPUType: DeviceProp.deviceType);
3746
3747 STWGraphicGpu::STWGraphicGpuItem NewGpu;
3748 str_copy(dst&: NewGpu.m_aName, src: DeviceProp.deviceName);
3749 NewGpu.m_GpuType = GPUType;
3750 m_pGpuList->m_vGpus.push_back(x: NewGpu);
3751
3752 Index++;
3753
3754 int DevAPIMajor = (int)VK_API_VERSION_MAJOR(DeviceProp.apiVersion);
3755 int DevAPIMinor = (int)VK_API_VERSION_MINOR(DeviceProp.apiVersion);
3756
3757 if(GPUType < AutoGpuType && (DevAPIMajor > gs_BackendVulkanMajor || (DevAPIMajor == gs_BackendVulkanMajor && DevAPIMinor >= gs_BackendVulkanMinor)))
3758 {
3759 str_copy(dst&: m_pGpuList->m_AutoGpu.m_aName, src: DeviceProp.deviceName);
3760 m_pGpuList->m_AutoGpu.m_GpuType = GPUType;
3761
3762 AutoGpuType = GPUType;
3763 }
3764
3765 if(((IsAutoGpu && (FoundGpuType > STWGraphicGpu::ETWGraphicsGpuType::GRAPHICS_GPU_TYPE_INTEGRATED && GPUType < FoundGpuType)) || str_comp(a: DeviceProp.deviceName, b: g_Config.m_GfxGpuName) == 0) && (DevAPIMajor > gs_BackendVulkanMajor || (DevAPIMajor == gs_BackendVulkanMajor && DevAPIMinor >= gs_BackendVulkanMinor)))
3766 {
3767 FoundDeviceIndex = Index;
3768 FoundGpuType = GPUType;
3769 }
3770 }
3771
3772 if(FoundDeviceIndex == 0)
3773 FoundDeviceIndex = 1;
3774
3775 {
3776 auto &DeviceProp = vDevicePropList[FoundDeviceIndex - 1];
3777
3778 int DevAPIMajor = (int)VK_API_VERSION_MAJOR(DeviceProp.apiVersion);
3779 int DevAPIMinor = (int)VK_API_VERSION_MINOR(DeviceProp.apiVersion);
3780 int DevAPIPatch = (int)VK_API_VERSION_PATCH(DeviceProp.apiVersion);
3781
3782 str_copy(dst: pRendererName, src: DeviceProp.deviceName, dst_size: gs_GpuInfoStringSize);
3783 const char *pVendorNameStr = NULL;
3784 switch(DeviceProp.vendorID)
3785 {
3786 case 0x1002:
3787 pVendorNameStr = "AMD";
3788 break;
3789 case 0x1010:
3790 pVendorNameStr = "ImgTec";
3791 break;
3792 case 0x106B:
3793 pVendorNameStr = "Apple";
3794 break;
3795 case 0x10DE:
3796 pVendorNameStr = "NVIDIA";
3797 break;
3798 case 0x13B5:
3799 pVendorNameStr = "ARM";
3800 break;
3801 case 0x5143:
3802 pVendorNameStr = "Qualcomm";
3803 break;
3804 case 0x8086:
3805 pVendorNameStr = "INTEL";
3806 break;
3807 case 0x10005:
3808 pVendorNameStr = "Mesa";
3809 break;
3810 default:
3811 dbg_msg(sys: "vulkan", fmt: "unknown gpu vendor %u", DeviceProp.vendorID);
3812 pVendorNameStr = "unknown";
3813 break;
3814 }
3815
3816 char aBuff[256];
3817 str_copy(dst: pVendorName, src: pVendorNameStr, dst_size: gs_GpuInfoStringSize);
3818 str_format(buffer: pVersionName, buffer_size: gs_GpuInfoStringSize, format: "Vulkan %d.%d.%d (driver: %s)", DevAPIMajor, DevAPIMinor, DevAPIPatch, GetDriverVerson(aBuff, DriverVersion: DeviceProp.driverVersion, VendorId: DeviceProp.vendorID));
3819
3820 // get important device limits
3821 m_NonCoherentMemAlignment = DeviceProp.limits.nonCoherentAtomSize;
3822 m_OptimalImageCopyMemAlignment = DeviceProp.limits.optimalBufferCopyOffsetAlignment;
3823 m_MaxTextureSize = DeviceProp.limits.maxImageDimension2D;
3824 m_MaxSamplerAnisotropy = DeviceProp.limits.maxSamplerAnisotropy;
3825
3826 m_MinUniformAlign = DeviceProp.limits.minUniformBufferOffsetAlignment;
3827 m_MaxMultiSample = DeviceProp.limits.framebufferColorSampleCounts;
3828
3829 if(IsVerbose())
3830 {
3831 dbg_msg(sys: "vulkan", fmt: "device prop: non-coherent align: %" PRIzu ", optimal image copy align: %" PRIzu ", max texture size: %u, max sampler anisotropy: %u", (size_t)m_NonCoherentMemAlignment, (size_t)m_OptimalImageCopyMemAlignment, m_MaxTextureSize, m_MaxSamplerAnisotropy);
3832 dbg_msg(sys: "vulkan", fmt: "device prop: min uniform align: %u, multi sample: %u", m_MinUniformAlign, (uint32_t)m_MaxMultiSample);
3833 }
3834 }
3835
3836 VkPhysicalDevice CurDevice = vDeviceList[FoundDeviceIndex - 1];
3837
3838 uint32_t FamQueueCount = 0;
3839 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice: CurDevice, pQueueFamilyPropertyCount: &FamQueueCount, pQueueFamilyProperties: nullptr);
3840 if(FamQueueCount == 0)
3841 {
3842 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "No vulkan queue family properties found.");
3843 return false;
3844 }
3845
3846 std::vector<VkQueueFamilyProperties> vQueuePropList(FamQueueCount);
3847 vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice: CurDevice, pQueueFamilyPropertyCount: &FamQueueCount, pQueueFamilyProperties: vQueuePropList.data());
3848
3849 uint32_t QueueNodeIndex = std::numeric_limits<uint32_t>::max();
3850 for(uint32_t i = 0; i < FamQueueCount; i++)
3851 {
3852 if(vQueuePropList[i].queueCount > 0 && (vQueuePropList[i].queueFlags & VK_QUEUE_GRAPHICS_BIT))
3853 {
3854 QueueNodeIndex = i;
3855 }
3856 /*if(vQueuePropList[i].queueCount > 0 && (vQueuePropList[i].queueFlags & VK_QUEUE_COMPUTE_BIT))
3857 {
3858 QueueNodeIndex = i;
3859 }*/
3860 }
3861
3862 if(QueueNodeIndex == std::numeric_limits<uint32_t>::max())
3863 {
3864 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "No vulkan queue found that matches the requirements: graphics queue.");
3865 return false;
3866 }
3867
3868 m_VKGPU = CurDevice;
3869 m_VKGraphicsQueueIndex = QueueNodeIndex;
3870 return true;
3871 }
3872
3873 [[nodiscard]] bool CreateLogicalDevice(const std::vector<std::string> &vVKLayers)
3874 {
3875 std::vector<const char *> vLayerCNames;
3876 vLayerCNames.reserve(n: vVKLayers.size());
3877 for(const auto &Layer : vVKLayers)
3878 vLayerCNames.emplace_back(args: Layer.c_str());
3879
3880 uint32_t DevPropCount = 0;
3881 if(vkEnumerateDeviceExtensionProperties(physicalDevice: m_VKGPU, NULL, pPropertyCount: &DevPropCount, NULL) != VK_SUCCESS)
3882 {
3883 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Querying logical device extension properties failed.");
3884 return false;
3885 }
3886
3887 std::vector<VkExtensionProperties> vDevPropList(DevPropCount);
3888 if(vkEnumerateDeviceExtensionProperties(physicalDevice: m_VKGPU, NULL, pPropertyCount: &DevPropCount, pProperties: vDevPropList.data()) != VK_SUCCESS)
3889 {
3890 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Querying logical device extension properties failed.");
3891 return false;
3892 }
3893
3894 std::vector<const char *> vDevPropCNames;
3895 std::set<std::string> OurDevExt = OurDeviceExtensions();
3896
3897 for(const auto &CurExtProp : vDevPropList)
3898 {
3899 auto it = OurDevExt.find(x: std::string(CurExtProp.extensionName));
3900 if(it != OurDevExt.end())
3901 {
3902 vDevPropCNames.emplace_back(args: CurExtProp.extensionName);
3903 }
3904 }
3905
3906 VkDeviceQueueCreateInfo VKQueueCreateInfo;
3907 VKQueueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
3908 VKQueueCreateInfo.queueFamilyIndex = m_VKGraphicsQueueIndex;
3909 VKQueueCreateInfo.queueCount = 1;
3910 float QueuePrio = 1.0f;
3911 VKQueueCreateInfo.pQueuePriorities = &QueuePrio;
3912 VKQueueCreateInfo.pNext = NULL;
3913 VKQueueCreateInfo.flags = 0;
3914
3915 VkDeviceCreateInfo VKCreateInfo;
3916 VKCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
3917 VKCreateInfo.queueCreateInfoCount = 1;
3918 VKCreateInfo.pQueueCreateInfos = &VKQueueCreateInfo;
3919 VKCreateInfo.ppEnabledLayerNames = vLayerCNames.data();
3920 VKCreateInfo.enabledLayerCount = static_cast<uint32_t>(vLayerCNames.size());
3921 VKCreateInfo.ppEnabledExtensionNames = vDevPropCNames.data();
3922 VKCreateInfo.enabledExtensionCount = static_cast<uint32_t>(vDevPropCNames.size());
3923 VKCreateInfo.pNext = NULL;
3924 VKCreateInfo.pEnabledFeatures = NULL;
3925 VKCreateInfo.flags = 0;
3926
3927 VkResult res = vkCreateDevice(physicalDevice: m_VKGPU, pCreateInfo: &VKCreateInfo, pAllocator: nullptr, pDevice: &m_VKDevice);
3928 if(res != VK_SUCCESS)
3929 {
3930 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Logical device could not be created.");
3931 return false;
3932 }
3933
3934 return true;
3935 }
3936
3937 [[nodiscard]] bool CreateSurface(SDL_Window *pWindow)
3938 {
3939 if(!SDL_Vulkan_CreateSurface(window: pWindow, instance: m_VKInstance, surface: &m_VKPresentSurface))
3940 {
3941 dbg_msg(sys: "vulkan", fmt: "error from sdl: %s", SDL_GetError());
3942 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating a vulkan surface for the SDL window failed.");
3943 return false;
3944 }
3945
3946 VkBool32 IsSupported = false;
3947 vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice: m_VKGPU, queueFamilyIndex: m_VKGraphicsQueueIndex, surface: m_VKPresentSurface, pSupported: &IsSupported);
3948 if(!IsSupported)
3949 {
3950 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "The device surface does not support presenting the framebuffer to a screen. (maybe the wrong GPU was selected?)");
3951 return false;
3952 }
3953
3954 return true;
3955 }
3956
3957 void DestroySurface()
3958 {
3959 vkDestroySurfaceKHR(instance: m_VKInstance, surface: m_VKPresentSurface, pAllocator: nullptr);
3960 }
3961
3962 [[nodiscard]] bool GetPresentationMode(VkPresentModeKHR &VKIOMode)
3963 {
3964 uint32_t PresentModeCount = 0;
3965 if(vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice: m_VKGPU, surface: m_VKPresentSurface, pPresentModeCount: &PresentModeCount, NULL) != VK_SUCCESS)
3966 {
3967 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "The device surface presentation modes could not be fetched.");
3968 return false;
3969 }
3970
3971 std::vector<VkPresentModeKHR> vPresentModeList(PresentModeCount);
3972 if(vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice: m_VKGPU, surface: m_VKPresentSurface, pPresentModeCount: &PresentModeCount, pPresentModes: vPresentModeList.data()) != VK_SUCCESS)
3973 {
3974 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "The device surface presentation modes could not be fetched.");
3975 return false;
3976 }
3977
3978 VKIOMode = g_Config.m_GfxVsync ? VK_PRESENT_MODE_FIFO_KHR : VK_PRESENT_MODE_IMMEDIATE_KHR;
3979 for(const auto &Mode : vPresentModeList)
3980 {
3981 if(Mode == VKIOMode)
3982 return true;
3983 }
3984
3985 dbg_msg(sys: "vulkan", fmt: "warning: requested presentation mode was not available. falling back to mailbox / fifo relaxed.");
3986 VKIOMode = g_Config.m_GfxVsync ? VK_PRESENT_MODE_FIFO_RELAXED_KHR : VK_PRESENT_MODE_MAILBOX_KHR;
3987 for(const auto &Mode : vPresentModeList)
3988 {
3989 if(Mode == VKIOMode)
3990 return true;
3991 }
3992
3993 dbg_msg(sys: "vulkan", fmt: "warning: requested presentation mode was not available. using first available.");
3994 if(PresentModeCount > 0)
3995 VKIOMode = vPresentModeList[0];
3996
3997 return true;
3998 }
3999
4000 [[nodiscard]] bool GetSurfaceProperties(VkSurfaceCapabilitiesKHR &VKSurfCapabilities)
4001 {
4002 if(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice: m_VKGPU, surface: m_VKPresentSurface, pSurfaceCapabilities: &VKSurfCapabilities) != VK_SUCCESS)
4003 {
4004 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "The device surface capabilities could not be fetched.");
4005 return false;
4006 }
4007 return true;
4008 }
4009
4010 uint32_t GetNumberOfSwapImages(const VkSurfaceCapabilitiesKHR &VKCapabilities)
4011 {
4012 uint32_t ImgNumber = VKCapabilities.minImageCount + 1;
4013 if(IsVerbose())
4014 {
4015 dbg_msg(sys: "vulkan", fmt: "minimal swap image count %u", VKCapabilities.minImageCount);
4016 }
4017 return (VKCapabilities.maxImageCount > 0 && ImgNumber > VKCapabilities.maxImageCount) ? VKCapabilities.maxImageCount : ImgNumber;
4018 }
4019
4020 SSwapImgViewportExtent GetSwapImageSize(const VkSurfaceCapabilitiesKHR &VKCapabilities)
4021 {
4022 VkExtent2D RetSize = {.width: m_CanvasWidth, .height: m_CanvasHeight};
4023
4024 if(VKCapabilities.currentExtent.width == std::numeric_limits<uint32_t>::max())
4025 {
4026 RetSize.width = clamp<uint32_t>(val: RetSize.width, lo: VKCapabilities.minImageExtent.width, hi: VKCapabilities.maxImageExtent.width);
4027 RetSize.height = clamp<uint32_t>(val: RetSize.height, lo: VKCapabilities.minImageExtent.height, hi: VKCapabilities.maxImageExtent.height);
4028 }
4029 else
4030 {
4031 RetSize = VKCapabilities.currentExtent;
4032 }
4033
4034 VkExtent2D AutoViewportExtent = RetSize;
4035 bool UsesForcedViewport = false;
4036 // keep this in sync with graphics_threaded AdjustViewport's check
4037 if(AutoViewportExtent.height > 4 * AutoViewportExtent.width / 5)
4038 {
4039 AutoViewportExtent.height = 4 * AutoViewportExtent.width / 5;
4040 UsesForcedViewport = true;
4041 }
4042
4043 SSwapImgViewportExtent Ext;
4044 Ext.m_SwapImageViewport = RetSize;
4045 Ext.m_ForcedViewport = AutoViewportExtent;
4046 Ext.m_HasForcedViewport = UsesForcedViewport;
4047
4048 return Ext;
4049 }
4050
4051 [[nodiscard]] bool GetImageUsage(const VkSurfaceCapabilitiesKHR &VKCapabilities, VkImageUsageFlags &VKOutUsage)
4052 {
4053 std::vector<VkImageUsageFlags> vOurImgUsages = OurImageUsages();
4054 if(vOurImgUsages.empty())
4055 {
4056 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Framebuffer image attachment types not supported.");
4057 return false;
4058 }
4059
4060 VKOutUsage = vOurImgUsages[0];
4061
4062 for(const auto &ImgUsage : vOurImgUsages)
4063 {
4064 VkImageUsageFlags ImgUsageFlags = ImgUsage & VKCapabilities.supportedUsageFlags;
4065 if(ImgUsageFlags != ImgUsage)
4066 {
4067 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Framebuffer image attachment types not supported.");
4068 return false;
4069 }
4070
4071 VKOutUsage = (VKOutUsage | ImgUsage);
4072 }
4073
4074 return true;
4075 }
4076
4077 VkSurfaceTransformFlagBitsKHR GetTransform(const VkSurfaceCapabilitiesKHR &VKCapabilities)
4078 {
4079 if(VKCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
4080 return VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
4081 return VKCapabilities.currentTransform;
4082 }
4083
4084 [[nodiscard]] bool GetFormat()
4085 {
4086 uint32_t SurfFormats = 0;
4087 VkResult Res = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice: m_VKGPU, surface: m_VKPresentSurface, pSurfaceFormatCount: &SurfFormats, pSurfaceFormats: nullptr);
4088 if(Res != VK_SUCCESS && Res != VK_INCOMPLETE)
4089 {
4090 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "The device surface format fetching failed.");
4091 return false;
4092 }
4093
4094 std::vector<VkSurfaceFormatKHR> vSurfFormatList(SurfFormats);
4095 Res = vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice: m_VKGPU, surface: m_VKPresentSurface, pSurfaceFormatCount: &SurfFormats, pSurfaceFormats: vSurfFormatList.data());
4096 if(Res != VK_SUCCESS && Res != VK_INCOMPLETE)
4097 {
4098 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "The device surface format fetching failed.");
4099 return false;
4100 }
4101
4102 if(Res == VK_INCOMPLETE)
4103 {
4104 dbg_msg(sys: "vulkan", fmt: "warning: not all surface formats are requestable with your current settings.");
4105 }
4106
4107 if(vSurfFormatList.size() == 1 && vSurfFormatList[0].format == VK_FORMAT_UNDEFINED)
4108 {
4109 m_VKSurfFormat.format = VK_FORMAT_B8G8R8A8_UNORM;
4110 m_VKSurfFormat.colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR;
4111 dbg_msg(sys: "vulkan", fmt: "warning: surface format was undefined. This can potentially cause bugs.");
4112 return true;
4113 }
4114
4115 for(const auto &FindFormat : vSurfFormatList)
4116 {
4117 if(FindFormat.format == VK_FORMAT_B8G8R8A8_UNORM && FindFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
4118 {
4119 m_VKSurfFormat = FindFormat;
4120 return true;
4121 }
4122 else if(FindFormat.format == VK_FORMAT_R8G8B8A8_UNORM && FindFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR)
4123 {
4124 m_VKSurfFormat = FindFormat;
4125 return true;
4126 }
4127 }
4128
4129 dbg_msg(sys: "vulkan", fmt: "warning: surface format was not RGBA(or variants of it). This can potentially cause weird looking images(too bright etc.).");
4130 m_VKSurfFormat = vSurfFormatList[0];
4131 return true;
4132 }
4133
4134 [[nodiscard]] bool CreateSwapChain(VkSwapchainKHR &OldSwapChain)
4135 {
4136 VkSurfaceCapabilitiesKHR VKSurfCap;
4137 if(!GetSurfaceProperties(VKSurfCapabilities&: VKSurfCap))
4138 return false;
4139
4140 VkPresentModeKHR PresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
4141 if(!GetPresentationMode(VKIOMode&: PresentMode))
4142 return false;
4143
4144 uint32_t SwapImgCount = GetNumberOfSwapImages(VKCapabilities: VKSurfCap);
4145
4146 m_VKSwapImgAndViewportExtent = GetSwapImageSize(VKCapabilities: VKSurfCap);
4147
4148 VkImageUsageFlags UsageFlags;
4149 if(!GetImageUsage(VKCapabilities: VKSurfCap, VKOutUsage&: UsageFlags))
4150 return false;
4151
4152 VkSurfaceTransformFlagBitsKHR TransformFlagBits = GetTransform(VKCapabilities: VKSurfCap);
4153
4154 if(!GetFormat())
4155 return false;
4156
4157 OldSwapChain = m_VKSwapChain;
4158
4159 VkSwapchainCreateInfoKHR SwapInfo;
4160 SwapInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
4161 SwapInfo.pNext = nullptr;
4162 SwapInfo.flags = 0;
4163 SwapInfo.surface = m_VKPresentSurface;
4164 SwapInfo.minImageCount = SwapImgCount;
4165 SwapInfo.imageFormat = m_VKSurfFormat.format;
4166 SwapInfo.imageColorSpace = m_VKSurfFormat.colorSpace;
4167 SwapInfo.imageExtent = m_VKSwapImgAndViewportExtent.m_SwapImageViewport;
4168 SwapInfo.imageArrayLayers = 1;
4169 SwapInfo.imageUsage = UsageFlags;
4170 SwapInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
4171 SwapInfo.queueFamilyIndexCount = 0;
4172 SwapInfo.pQueueFamilyIndices = nullptr;
4173 SwapInfo.preTransform = TransformFlagBits;
4174 SwapInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
4175 SwapInfo.presentMode = PresentMode;
4176 SwapInfo.clipped = true;
4177 SwapInfo.oldSwapchain = OldSwapChain;
4178
4179 m_VKSwapChain = VK_NULL_HANDLE;
4180 VkResult SwapchainCreateRes = vkCreateSwapchainKHR(device: m_VKDevice, pCreateInfo: &SwapInfo, pAllocator: nullptr, pSwapchain: &m_VKSwapChain);
4181 const char *pCritErrorMsg = CheckVulkanCriticalError(CallResult: SwapchainCreateRes);
4182 if(pCritErrorMsg != nullptr)
4183 {
4184 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating the swap chain failed.", pErrStrExtra: pCritErrorMsg);
4185 return false;
4186 }
4187 else if(SwapchainCreateRes == VK_ERROR_NATIVE_WINDOW_IN_USE_KHR)
4188 return false;
4189
4190 return true;
4191 }
4192
4193 void DestroySwapChain(bool ForceDestroy)
4194 {
4195 if(ForceDestroy)
4196 {
4197 vkDestroySwapchainKHR(device: m_VKDevice, swapchain: m_VKSwapChain, pAllocator: nullptr);
4198 m_VKSwapChain = VK_NULL_HANDLE;
4199 }
4200 }
4201
4202 [[nodiscard]] bool GetSwapChainImageHandles()
4203 {
4204 uint32_t ImgCount = 0;
4205 VkResult res = vkGetSwapchainImagesKHR(device: m_VKDevice, swapchain: m_VKSwapChain, pSwapchainImageCount: &ImgCount, pSwapchainImages: nullptr);
4206 if(res != VK_SUCCESS)
4207 {
4208 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Could not get swap chain images.");
4209 return false;
4210 }
4211
4212 m_SwapChainImageCount = ImgCount;
4213
4214 m_vSwapChainImages.resize(new_size: ImgCount);
4215 if(vkGetSwapchainImagesKHR(device: m_VKDevice, swapchain: m_VKSwapChain, pSwapchainImageCount: &ImgCount, pSwapchainImages: m_vSwapChainImages.data()) != VK_SUCCESS)
4216 {
4217 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Could not get swap chain images.");
4218 return false;
4219 }
4220
4221 return true;
4222 }
4223
4224 void ClearSwapChainImageHandles()
4225 {
4226 m_vSwapChainImages.clear();
4227 }
4228
4229 void GetDeviceQueue()
4230 {
4231 vkGetDeviceQueue(device: m_VKDevice, queueFamilyIndex: m_VKGraphicsQueueIndex, queueIndex: 0, pQueue: &m_VKGraphicsQueue);
4232 vkGetDeviceQueue(device: m_VKDevice, queueFamilyIndex: m_VKGraphicsQueueIndex, queueIndex: 0, pQueue: &m_VKPresentQueue);
4233 }
4234
4235#ifdef VK_EXT_debug_utils
4236 static VKAPI_ATTR VkBool32 VKAPI_CALL VKDebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT MessageSeverity, VkDebugUtilsMessageTypeFlagsEXT MessageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData)
4237 {
4238 if((MessageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) != 0)
4239 {
4240 dbg_msg(sys: "vulkan_debug", fmt: "validation error: %s", pCallbackData->pMessage);
4241 }
4242 else
4243 {
4244 dbg_msg(sys: "vulkan_debug", fmt: "%s", pCallbackData->pMessage);
4245 }
4246
4247 return VK_FALSE;
4248 }
4249
4250 VkResult CreateDebugUtilsMessengerEXT(const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDebugUtilsMessengerEXT *pDebugMessenger)
4251 {
4252 auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance: m_VKInstance, pName: "vkCreateDebugUtilsMessengerEXT");
4253 if(func != nullptr)
4254 {
4255 return func(m_VKInstance, pCreateInfo, pAllocator, pDebugMessenger);
4256 }
4257 else
4258 {
4259 return VK_ERROR_EXTENSION_NOT_PRESENT;
4260 }
4261 }
4262
4263 void DestroyDebugUtilsMessengerEXT(VkDebugUtilsMessengerEXT &DebugMessenger)
4264 {
4265 auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance: m_VKInstance, pName: "vkDestroyDebugUtilsMessengerEXT");
4266 if(func != nullptr)
4267 {
4268 func(m_VKInstance, DebugMessenger, nullptr);
4269 }
4270 }
4271#endif
4272
4273 void SetupDebugCallback()
4274 {
4275#ifdef VK_EXT_debug_utils
4276 VkDebugUtilsMessengerCreateInfoEXT CreateInfo = {};
4277 CreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
4278 CreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
4279 CreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; // | VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT <- too annoying
4280 CreateInfo.pfnUserCallback = VKDebugCallback;
4281
4282 if(CreateDebugUtilsMessengerEXT(pCreateInfo: &CreateInfo, pAllocator: nullptr, pDebugMessenger: &m_DebugMessenger) != VK_SUCCESS)
4283 {
4284 m_DebugMessenger = VK_NULL_HANDLE;
4285 dbg_msg(sys: "vulkan", fmt: "didn't find vulkan debug layer.");
4286 }
4287 else
4288 {
4289 dbg_msg(sys: "vulkan", fmt: "enabled vulkan debug context.");
4290 }
4291#endif
4292 }
4293
4294 void UnregisterDebugCallback()
4295 {
4296#ifdef VK_EXT_debug_utils
4297 if(m_DebugMessenger != VK_NULL_HANDLE)
4298 DestroyDebugUtilsMessengerEXT(DebugMessenger&: m_DebugMessenger);
4299#endif
4300 }
4301
4302 [[nodiscard]] bool CreateImageViews()
4303 {
4304 m_vSwapChainImageViewList.resize(new_size: m_SwapChainImageCount);
4305
4306 for(size_t i = 0; i < m_SwapChainImageCount; i++)
4307 {
4308 VkImageViewCreateInfo CreateInfo{};
4309 CreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
4310 CreateInfo.image = m_vSwapChainImages[i];
4311 CreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
4312 CreateInfo.format = m_VKSurfFormat.format;
4313 CreateInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
4314 CreateInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
4315 CreateInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
4316 CreateInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
4317 CreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
4318 CreateInfo.subresourceRange.baseMipLevel = 0;
4319 CreateInfo.subresourceRange.levelCount = 1;
4320 CreateInfo.subresourceRange.baseArrayLayer = 0;
4321 CreateInfo.subresourceRange.layerCount = 1;
4322
4323 if(vkCreateImageView(device: m_VKDevice, pCreateInfo: &CreateInfo, pAllocator: nullptr, pView: &m_vSwapChainImageViewList[i]) != VK_SUCCESS)
4324 {
4325 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Could not create image views for the swap chain framebuffers.");
4326 return false;
4327 }
4328 }
4329
4330 return true;
4331 }
4332
4333 void DestroyImageViews()
4334 {
4335 for(auto &ImageView : m_vSwapChainImageViewList)
4336 {
4337 vkDestroyImageView(device: m_VKDevice, imageView: ImageView, pAllocator: nullptr);
4338 }
4339
4340 m_vSwapChainImageViewList.clear();
4341 }
4342
4343 [[nodiscard]] bool CreateMultiSamplerImageAttachments()
4344 {
4345 m_vSwapChainMultiSamplingImages.resize(new_size: m_SwapChainImageCount);
4346 if(HasMultiSampling())
4347 {
4348 for(size_t i = 0; i < m_SwapChainImageCount; ++i)
4349 {
4350 if(!CreateImage(Width: m_VKSwapImgAndViewportExtent.m_SwapImageViewport.width, Height: m_VKSwapImgAndViewportExtent.m_SwapImageViewport.height, Depth: 1, MipMapLevelCount: 1, Format: m_VKSurfFormat.format, Tiling: VK_IMAGE_TILING_OPTIMAL, Image&: m_vSwapChainMultiSamplingImages[i].m_Image, ImageMemory&: m_vSwapChainMultiSamplingImages[i].m_ImgMem, ImageUsage: VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT))
4351 return false;
4352 m_vSwapChainMultiSamplingImages[i].m_ImgView = CreateImageView(Image: m_vSwapChainMultiSamplingImages[i].m_Image, Format: m_VKSurfFormat.format, ViewType: VK_IMAGE_VIEW_TYPE_2D, Depth: 1, MipMapLevelCount: 1);
4353 }
4354 }
4355
4356 return true;
4357 }
4358
4359 void DestroyMultiSamplerImageAttachments()
4360 {
4361 if(HasMultiSampling())
4362 {
4363 m_vSwapChainMultiSamplingImages.resize(new_size: m_SwapChainImageCount);
4364 for(size_t i = 0; i < m_SwapChainImageCount; ++i)
4365 {
4366 vkDestroyImage(device: m_VKDevice, image: m_vSwapChainMultiSamplingImages[i].m_Image, pAllocator: nullptr);
4367 vkDestroyImageView(device: m_VKDevice, imageView: m_vSwapChainMultiSamplingImages[i].m_ImgView, pAllocator: nullptr);
4368 FreeImageMemBlock(Block&: m_vSwapChainMultiSamplingImages[i].m_ImgMem);
4369 }
4370 }
4371 m_vSwapChainMultiSamplingImages.clear();
4372 }
4373
4374 [[nodiscard]] bool CreateRenderPass(bool ClearAttachs)
4375 {
4376 bool HasMultiSamplingTargets = HasMultiSampling();
4377 VkAttachmentDescription MultiSamplingColorAttachment{};
4378 MultiSamplingColorAttachment.format = m_VKSurfFormat.format;
4379 MultiSamplingColorAttachment.samples = GetSampleCount();
4380 MultiSamplingColorAttachment.loadOp = ClearAttachs ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
4381 MultiSamplingColorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
4382 MultiSamplingColorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
4383 MultiSamplingColorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
4384 MultiSamplingColorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4385 MultiSamplingColorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
4386
4387 VkAttachmentDescription ColorAttachment{};
4388 ColorAttachment.format = m_VKSurfFormat.format;
4389 ColorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
4390 ColorAttachment.loadOp = ClearAttachs && !HasMultiSamplingTargets ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE;
4391 ColorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
4392 ColorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
4393 ColorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
4394 ColorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
4395 ColorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
4396
4397 VkAttachmentReference MultiSamplingColorAttachmentRef{};
4398 MultiSamplingColorAttachmentRef.attachment = 0;
4399 MultiSamplingColorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
4400
4401 VkAttachmentReference ColorAttachmentRef{};
4402 ColorAttachmentRef.attachment = HasMultiSamplingTargets ? 1 : 0;
4403 ColorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
4404
4405 VkSubpassDescription Subpass{};
4406 Subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
4407 Subpass.colorAttachmentCount = 1;
4408 Subpass.pColorAttachments = HasMultiSamplingTargets ? &MultiSamplingColorAttachmentRef : &ColorAttachmentRef;
4409 Subpass.pResolveAttachments = HasMultiSamplingTargets ? &ColorAttachmentRef : nullptr;
4410
4411 std::array<VkAttachmentDescription, 2> aAttachments;
4412 aAttachments[0] = MultiSamplingColorAttachment;
4413 aAttachments[1] = ColorAttachment;
4414
4415 VkSubpassDependency Dependency{};
4416 Dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
4417 Dependency.dstSubpass = 0;
4418 Dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
4419 Dependency.srcAccessMask = 0;
4420 Dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
4421 Dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
4422
4423 VkRenderPassCreateInfo CreateRenderPassInfo{};
4424 CreateRenderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
4425 CreateRenderPassInfo.attachmentCount = HasMultiSamplingTargets ? 2 : 1;
4426 CreateRenderPassInfo.pAttachments = HasMultiSamplingTargets ? aAttachments.data() : aAttachments.data() + 1;
4427 CreateRenderPassInfo.subpassCount = 1;
4428 CreateRenderPassInfo.pSubpasses = &Subpass;
4429 CreateRenderPassInfo.dependencyCount = 1;
4430 CreateRenderPassInfo.pDependencies = &Dependency;
4431
4432 if(vkCreateRenderPass(device: m_VKDevice, pCreateInfo: &CreateRenderPassInfo, pAllocator: nullptr, pRenderPass: &m_VKRenderPass) != VK_SUCCESS)
4433 {
4434 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating the render pass failed.");
4435 return false;
4436 }
4437
4438 return true;
4439 }
4440
4441 void DestroyRenderPass()
4442 {
4443 vkDestroyRenderPass(device: m_VKDevice, renderPass: m_VKRenderPass, pAllocator: nullptr);
4444 }
4445
4446 [[nodiscard]] bool CreateFramebuffers()
4447 {
4448 m_vFramebufferList.resize(new_size: m_SwapChainImageCount);
4449
4450 for(size_t i = 0; i < m_SwapChainImageCount; i++)
4451 {
4452 std::array<VkImageView, 2> aAttachments = {
4453 m_vSwapChainMultiSamplingImages[i].m_ImgView,
4454 m_vSwapChainImageViewList[i]};
4455
4456 bool HasMultiSamplingTargets = HasMultiSampling();
4457
4458 VkFramebufferCreateInfo FramebufferInfo{};
4459 FramebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
4460 FramebufferInfo.renderPass = m_VKRenderPass;
4461 FramebufferInfo.attachmentCount = HasMultiSamplingTargets ? aAttachments.size() : aAttachments.size() - 1;
4462 FramebufferInfo.pAttachments = HasMultiSamplingTargets ? aAttachments.data() : aAttachments.data() + 1;
4463 FramebufferInfo.width = m_VKSwapImgAndViewportExtent.m_SwapImageViewport.width;
4464 FramebufferInfo.height = m_VKSwapImgAndViewportExtent.m_SwapImageViewport.height;
4465 FramebufferInfo.layers = 1;
4466
4467 if(vkCreateFramebuffer(device: m_VKDevice, pCreateInfo: &FramebufferInfo, pAllocator: nullptr, pFramebuffer: &m_vFramebufferList[i]) != VK_SUCCESS)
4468 {
4469 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating the framebuffers failed.");
4470 return false;
4471 }
4472 }
4473
4474 return true;
4475 }
4476
4477 void DestroyFramebuffers()
4478 {
4479 for(auto &FrameBuffer : m_vFramebufferList)
4480 {
4481 vkDestroyFramebuffer(device: m_VKDevice, framebuffer: FrameBuffer, pAllocator: nullptr);
4482 }
4483
4484 m_vFramebufferList.clear();
4485 }
4486
4487 [[nodiscard]] bool CreateShaderModule(const std::vector<uint8_t> &vCode, VkShaderModule &ShaderModule)
4488 {
4489 VkShaderModuleCreateInfo CreateInfo{};
4490 CreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
4491 CreateInfo.codeSize = vCode.size();
4492 CreateInfo.pCode = (const uint32_t *)(vCode.data());
4493
4494 if(vkCreateShaderModule(device: m_VKDevice, pCreateInfo: &CreateInfo, pAllocator: nullptr, pShaderModule: &ShaderModule) != VK_SUCCESS)
4495 {
4496 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Shader module was not created.");
4497 return false;
4498 }
4499
4500 return true;
4501 }
4502
4503 [[nodiscard]] bool CreateDescriptorSetLayouts()
4504 {
4505 VkDescriptorSetLayoutBinding SamplerLayoutBinding{};
4506 SamplerLayoutBinding.binding = 0;
4507 SamplerLayoutBinding.descriptorCount = 1;
4508 SamplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
4509 SamplerLayoutBinding.pImmutableSamplers = nullptr;
4510 SamplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
4511
4512 std::array<VkDescriptorSetLayoutBinding, 1> aBindings = {SamplerLayoutBinding};
4513 VkDescriptorSetLayoutCreateInfo LayoutInfo{};
4514 LayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
4515 LayoutInfo.bindingCount = aBindings.size();
4516 LayoutInfo.pBindings = aBindings.data();
4517
4518 if(vkCreateDescriptorSetLayout(device: m_VKDevice, pCreateInfo: &LayoutInfo, pAllocator: nullptr, pSetLayout: &m_StandardTexturedDescriptorSetLayout) != VK_SUCCESS)
4519 {
4520 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating descriptor layout failed.");
4521 return false;
4522 }
4523
4524 if(vkCreateDescriptorSetLayout(device: m_VKDevice, pCreateInfo: &LayoutInfo, pAllocator: nullptr, pSetLayout: &m_Standard3DTexturedDescriptorSetLayout) != VK_SUCCESS)
4525 {
4526 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating descriptor layout failed.");
4527 return false;
4528 }
4529 return true;
4530 }
4531
4532 void DestroyDescriptorSetLayouts()
4533 {
4534 vkDestroyDescriptorSetLayout(device: m_VKDevice, descriptorSetLayout: m_StandardTexturedDescriptorSetLayout, pAllocator: nullptr);
4535 vkDestroyDescriptorSetLayout(device: m_VKDevice, descriptorSetLayout: m_Standard3DTexturedDescriptorSetLayout, pAllocator: nullptr);
4536 }
4537
4538 [[nodiscard]] bool LoadShader(const char *pFileName, std::vector<uint8_t> *&pvShaderData)
4539 {
4540 auto it = m_ShaderFiles.find(x: pFileName);
4541 if(it == m_ShaderFiles.end())
4542 {
4543 void *pShaderBuff;
4544 unsigned FileSize;
4545 if(!m_pStorage->ReadFile(pFilename: pFileName, Type: IStorage::TYPE_ALL, ppResult: &pShaderBuff, pResultLen: &FileSize))
4546 return false;
4547
4548 std::vector<uint8_t> vShaderBuff;
4549 vShaderBuff.resize(new_size: FileSize);
4550 mem_copy(dest: vShaderBuff.data(), source: pShaderBuff, size: FileSize);
4551 free(ptr: pShaderBuff);
4552
4553 it = m_ShaderFiles.insert(x: {pFileName, {.m_vBinary: std::move(vShaderBuff)}}).first;
4554 }
4555
4556 pvShaderData = &it->second.m_vBinary;
4557
4558 return true;
4559 }
4560
4561 [[nodiscard]] bool CreateShaders(const char *pVertName, const char *pFragName, VkPipelineShaderStageCreateInfo (&aShaderStages)[2], SShaderModule &ShaderModule)
4562 {
4563 bool ShaderLoaded = true;
4564
4565 std::vector<uint8_t> *pvVertBuff;
4566 std::vector<uint8_t> *pvFragBuff;
4567 ShaderLoaded &= LoadShader(pFileName: pVertName, pvShaderData&: pvVertBuff);
4568 ShaderLoaded &= LoadShader(pFileName: pFragName, pvShaderData&: pvFragBuff);
4569
4570 ShaderModule.m_VKDevice = m_VKDevice;
4571
4572 if(!ShaderLoaded)
4573 {
4574 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "A shader file could not load correctly.");
4575 return false;
4576 }
4577
4578 if(!CreateShaderModule(vCode: *pvVertBuff, ShaderModule&: ShaderModule.m_VertShaderModule))
4579 return false;
4580
4581 if(!CreateShaderModule(vCode: *pvFragBuff, ShaderModule&: ShaderModule.m_FragShaderModule))
4582 return false;
4583
4584 VkPipelineShaderStageCreateInfo &VertShaderStageInfo = aShaderStages[0];
4585 VertShaderStageInfo = {};
4586 VertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
4587 VertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
4588 VertShaderStageInfo.module = ShaderModule.m_VertShaderModule;
4589 VertShaderStageInfo.pName = "main";
4590
4591 VkPipelineShaderStageCreateInfo &FragShaderStageInfo = aShaderStages[1];
4592 FragShaderStageInfo = {};
4593 FragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
4594 FragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
4595 FragShaderStageInfo.module = ShaderModule.m_FragShaderModule;
4596 FragShaderStageInfo.pName = "main";
4597 return true;
4598 }
4599
4600 bool GetStandardPipelineInfo(VkPipelineInputAssemblyStateCreateInfo &InputAssembly,
4601 VkViewport &Viewport,
4602 VkRect2D &Scissor,
4603 VkPipelineViewportStateCreateInfo &ViewportState,
4604 VkPipelineRasterizationStateCreateInfo &Rasterizer,
4605 VkPipelineMultisampleStateCreateInfo &Multisampling,
4606 VkPipelineColorBlendAttachmentState &ColorBlendAttachment,
4607 VkPipelineColorBlendStateCreateInfo &ColorBlending) const
4608 {
4609 InputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
4610 InputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
4611 InputAssembly.primitiveRestartEnable = VK_FALSE;
4612
4613 Viewport.x = 0.0f;
4614 Viewport.y = 0.0f;
4615 Viewport.width = (float)m_VKSwapImgAndViewportExtent.m_SwapImageViewport.width;
4616 Viewport.height = (float)m_VKSwapImgAndViewportExtent.m_SwapImageViewport.height;
4617 Viewport.minDepth = 0.0f;
4618 Viewport.maxDepth = 1.0f;
4619
4620 Scissor.offset = {.x: 0, .y: 0};
4621 Scissor.extent = m_VKSwapImgAndViewportExtent.m_SwapImageViewport;
4622
4623 ViewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
4624 ViewportState.viewportCount = 1;
4625 ViewportState.pViewports = &Viewport;
4626 ViewportState.scissorCount = 1;
4627 ViewportState.pScissors = &Scissor;
4628
4629 Rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
4630 Rasterizer.depthClampEnable = VK_FALSE;
4631 Rasterizer.rasterizerDiscardEnable = VK_FALSE;
4632 Rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
4633 Rasterizer.lineWidth = 1.0f;
4634 Rasterizer.cullMode = VK_CULL_MODE_NONE;
4635 Rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
4636 Rasterizer.depthBiasEnable = VK_FALSE;
4637
4638 Multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
4639 Multisampling.sampleShadingEnable = VK_FALSE;
4640 Multisampling.rasterizationSamples = GetSampleCount();
4641
4642 ColorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
4643 ColorBlendAttachment.blendEnable = VK_TRUE;
4644
4645 ColorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
4646 ColorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
4647 ColorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD;
4648 ColorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
4649 ColorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
4650 ColorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
4651
4652 ColorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
4653 ColorBlending.logicOpEnable = VK_FALSE;
4654 ColorBlending.logicOp = VK_LOGIC_OP_COPY;
4655 ColorBlending.attachmentCount = 1;
4656 ColorBlending.pAttachments = &ColorBlendAttachment;
4657 ColorBlending.blendConstants[0] = 0.0f;
4658 ColorBlending.blendConstants[1] = 0.0f;
4659 ColorBlending.blendConstants[2] = 0.0f;
4660 ColorBlending.blendConstants[3] = 0.0f;
4661
4662 return true;
4663 }
4664
4665 template<bool ForceRequireDescriptors, size_t ArraySize, size_t DescrArraySize, size_t PushArraySize>
4666 [[nodiscard]] bool CreateGraphicsPipeline(const char *pVertName, const char *pFragName, SPipelineContainer &PipeContainer, uint32_t Stride, std::array<VkVertexInputAttributeDescription, ArraySize> &aInputAttr,
4667 std::array<VkDescriptorSetLayout, DescrArraySize> &aSetLayouts, std::array<VkPushConstantRange, PushArraySize> &aPushConstants, EVulkanBackendTextureModes TexMode,
4668 EVulkanBackendBlendModes BlendMode, EVulkanBackendClipModes DynamicMode, bool IsLinePrim = false)
4669 {
4670 VkPipelineShaderStageCreateInfo aShaderStages[2];
4671 SShaderModule Module;
4672 if(!CreateShaders(pVertName, pFragName, aShaderStages, ShaderModule&: Module))
4673 return false;
4674
4675 bool HasSampler = TexMode == VULKAN_BACKEND_TEXTURE_MODE_TEXTURED;
4676
4677 VkPipelineVertexInputStateCreateInfo VertexInputInfo{};
4678 VertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
4679 VkVertexInputBindingDescription BindingDescription{};
4680 BindingDescription.binding = 0;
4681 BindingDescription.stride = Stride;
4682 BindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
4683
4684 VertexInputInfo.vertexBindingDescriptionCount = 1;
4685 VertexInputInfo.vertexAttributeDescriptionCount = aInputAttr.size();
4686 VertexInputInfo.pVertexBindingDescriptions = &BindingDescription;
4687 VertexInputInfo.pVertexAttributeDescriptions = aInputAttr.data();
4688
4689 VkPipelineInputAssemblyStateCreateInfo InputAssembly{};
4690 VkViewport Viewport{};
4691 VkRect2D Scissor{};
4692 VkPipelineViewportStateCreateInfo ViewportState{};
4693 VkPipelineRasterizationStateCreateInfo Rasterizer{};
4694 VkPipelineMultisampleStateCreateInfo Multisampling{};
4695 VkPipelineColorBlendAttachmentState ColorBlendAttachment{};
4696 VkPipelineColorBlendStateCreateInfo ColorBlending{};
4697
4698 GetStandardPipelineInfo(InputAssembly, Viewport, Scissor, ViewportState, Rasterizer, Multisampling, ColorBlendAttachment, ColorBlending);
4699 InputAssembly.topology = IsLinePrim ? VK_PRIMITIVE_TOPOLOGY_LINE_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
4700
4701 VkPipelineLayoutCreateInfo PipelineLayoutInfo{};
4702 PipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
4703 PipelineLayoutInfo.setLayoutCount = (HasSampler || ForceRequireDescriptors) ? aSetLayouts.size() : 0;
4704 PipelineLayoutInfo.pSetLayouts = (HasSampler || ForceRequireDescriptors) && !aSetLayouts.empty() ? aSetLayouts.data() : nullptr;
4705
4706 PipelineLayoutInfo.pushConstantRangeCount = aPushConstants.size();
4707 PipelineLayoutInfo.pPushConstantRanges = !aPushConstants.empty() ? aPushConstants.data() : nullptr;
4708
4709 VkPipelineLayout &PipeLayout = GetPipeLayout(Container&: PipeContainer, IsTextured: HasSampler, BlendModeIndex: size_t(BlendMode), DynamicIndex: size_t(DynamicMode));
4710 VkPipeline &Pipeline = GetPipeline(Container&: PipeContainer, IsTextured: HasSampler, BlendModeIndex: size_t(BlendMode), DynamicIndex: size_t(DynamicMode));
4711
4712 if(vkCreatePipelineLayout(device: m_VKDevice, pCreateInfo: &PipelineLayoutInfo, pAllocator: nullptr, pPipelineLayout: &PipeLayout) != VK_SUCCESS)
4713 {
4714 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating pipeline layout failed.");
4715 return false;
4716 }
4717
4718 VkGraphicsPipelineCreateInfo PipelineInfo{};
4719 PipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
4720 PipelineInfo.stageCount = 2;
4721 PipelineInfo.pStages = aShaderStages;
4722 PipelineInfo.pVertexInputState = &VertexInputInfo;
4723 PipelineInfo.pInputAssemblyState = &InputAssembly;
4724 PipelineInfo.pViewportState = &ViewportState;
4725 PipelineInfo.pRasterizationState = &Rasterizer;
4726 PipelineInfo.pMultisampleState = &Multisampling;
4727 PipelineInfo.pColorBlendState = &ColorBlending;
4728 PipelineInfo.layout = PipeLayout;
4729 PipelineInfo.renderPass = m_VKRenderPass;
4730 PipelineInfo.subpass = 0;
4731 PipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
4732
4733 std::array<VkDynamicState, 2> aDynamicStates = {
4734 VK_DYNAMIC_STATE_VIEWPORT,
4735 VK_DYNAMIC_STATE_SCISSOR,
4736 };
4737
4738 VkPipelineDynamicStateCreateInfo DynamicStateCreate{};
4739 DynamicStateCreate.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
4740 DynamicStateCreate.dynamicStateCount = aDynamicStates.size();
4741 DynamicStateCreate.pDynamicStates = aDynamicStates.data();
4742
4743 if(DynamicMode == VULKAN_BACKEND_CLIP_MODE_DYNAMIC_SCISSOR_AND_VIEWPORT)
4744 {
4745 PipelineInfo.pDynamicState = &DynamicStateCreate;
4746 }
4747
4748 if(vkCreateGraphicsPipelines(device: m_VKDevice, VK_NULL_HANDLE, createInfoCount: 1, pCreateInfos: &PipelineInfo, pAllocator: nullptr, pPipelines: &Pipeline) != VK_SUCCESS)
4749 {
4750 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating the graphic pipeline failed.");
4751 return false;
4752 }
4753
4754 return true;
4755 }
4756
4757 [[nodiscard]] bool CreateStandardGraphicsPipelineImpl(const char *pVertName, const char *pFragName, SPipelineContainer &PipeContainer, EVulkanBackendTextureModes TexMode, EVulkanBackendBlendModes BlendMode, EVulkanBackendClipModes DynamicMode, bool IsLinePrim)
4758 {
4759 std::array<VkVertexInputAttributeDescription, 3> aAttributeDescriptions = {};
4760
4761 aAttributeDescriptions[0] = {.location: 0, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: 0};
4762 aAttributeDescriptions[1] = {.location: 1, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: sizeof(float) * 2};
4763 aAttributeDescriptions[2] = {.location: 2, .binding: 0, .format: VK_FORMAT_R8G8B8A8_UNORM, .offset: sizeof(float) * (2 + 2)};
4764
4765 std::array<VkDescriptorSetLayout, 1> aSetLayouts = {m_StandardTexturedDescriptorSetLayout};
4766
4767 std::array<VkPushConstantRange, 1> aPushConstants{};
4768 aPushConstants[0] = {.stageFlags: VK_SHADER_STAGE_VERTEX_BIT, .offset: 0, .size: sizeof(SUniformGPos)};
4769
4770 return CreateGraphicsPipeline<false>(pVertName, pFragName, PipeContainer, Stride: sizeof(float) * (2 + 2) + sizeof(uint8_t) * 4, aInputAttr&: aAttributeDescriptions, aSetLayouts, aPushConstants, TexMode, BlendMode, DynamicMode, IsLinePrim);
4771 }
4772
4773 [[nodiscard]] bool CreateStandardGraphicsPipeline(const char *pVertName, const char *pFragName, bool HasSampler, bool IsLinePipe)
4774 {
4775 bool Ret = true;
4776
4777 EVulkanBackendTextureModes TexMode = HasSampler ? VULKAN_BACKEND_TEXTURE_MODE_TEXTURED : VULKAN_BACKEND_TEXTURE_MODE_NOT_TEXTURED;
4778
4779 for(size_t i = 0; i < VULKAN_BACKEND_BLEND_MODE_COUNT; ++i)
4780 {
4781 for(size_t j = 0; j < VULKAN_BACKEND_CLIP_MODE_COUNT; ++j)
4782 {
4783 Ret &= CreateStandardGraphicsPipelineImpl(pVertName, pFragName, PipeContainer&: IsLinePipe ? m_StandardLinePipeline : m_StandardPipeline, TexMode, BlendMode: EVulkanBackendBlendModes(i), DynamicMode: EVulkanBackendClipModes(j), IsLinePrim: IsLinePipe);
4784 }
4785 }
4786
4787 return Ret;
4788 }
4789
4790 [[nodiscard]] bool CreateStandard3DGraphicsPipelineImpl(const char *pVertName, const char *pFragName, SPipelineContainer &PipeContainer, EVulkanBackendTextureModes TexMode, EVulkanBackendBlendModes BlendMode, EVulkanBackendClipModes DynamicMode)
4791 {
4792 std::array<VkVertexInputAttributeDescription, 3> aAttributeDescriptions = {};
4793
4794 aAttributeDescriptions[0] = {.location: 0, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: 0};
4795 aAttributeDescriptions[1] = {.location: 1, .binding: 0, .format: VK_FORMAT_R8G8B8A8_UNORM, .offset: sizeof(float) * 2};
4796 aAttributeDescriptions[2] = {.location: 2, .binding: 0, .format: VK_FORMAT_R32G32B32_SFLOAT, .offset: sizeof(float) * 2 + sizeof(uint8_t) * 4};
4797
4798 std::array<VkDescriptorSetLayout, 1> aSetLayouts = {m_Standard3DTexturedDescriptorSetLayout};
4799
4800 std::array<VkPushConstantRange, 1> aPushConstants{};
4801 aPushConstants[0] = {.stageFlags: VK_SHADER_STAGE_VERTEX_BIT, .offset: 0, .size: sizeof(SUniformGPos)};
4802
4803 return CreateGraphicsPipeline<false>(pVertName, pFragName, PipeContainer, Stride: sizeof(float) * 2 + sizeof(uint8_t) * 4 + sizeof(float) * 3, aInputAttr&: aAttributeDescriptions, aSetLayouts, aPushConstants, TexMode, BlendMode, DynamicMode);
4804 }
4805
4806 [[nodiscard]] bool CreateStandard3DGraphicsPipeline(const char *pVertName, const char *pFragName, bool HasSampler)
4807 {
4808 bool Ret = true;
4809
4810 EVulkanBackendTextureModes TexMode = HasSampler ? VULKAN_BACKEND_TEXTURE_MODE_TEXTURED : VULKAN_BACKEND_TEXTURE_MODE_NOT_TEXTURED;
4811
4812 for(size_t i = 0; i < VULKAN_BACKEND_BLEND_MODE_COUNT; ++i)
4813 {
4814 for(size_t j = 0; j < VULKAN_BACKEND_CLIP_MODE_COUNT; ++j)
4815 {
4816 Ret &= CreateStandard3DGraphicsPipelineImpl(pVertName, pFragName, PipeContainer&: m_Standard3DPipeline, TexMode, BlendMode: EVulkanBackendBlendModes(i), DynamicMode: EVulkanBackendClipModes(j));
4817 }
4818 }
4819
4820 return Ret;
4821 }
4822
4823 [[nodiscard]] bool CreateTextDescriptorSetLayout()
4824 {
4825 VkDescriptorSetLayoutBinding SamplerLayoutBinding{};
4826 SamplerLayoutBinding.binding = 0;
4827 SamplerLayoutBinding.descriptorCount = 1;
4828 SamplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
4829 SamplerLayoutBinding.pImmutableSamplers = nullptr;
4830 SamplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
4831
4832 auto SamplerLayoutBinding2 = SamplerLayoutBinding;
4833 SamplerLayoutBinding2.binding = 1;
4834
4835 std::array<VkDescriptorSetLayoutBinding, 2> aBindings = {SamplerLayoutBinding, SamplerLayoutBinding2};
4836 VkDescriptorSetLayoutCreateInfo LayoutInfo{};
4837 LayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
4838 LayoutInfo.bindingCount = aBindings.size();
4839 LayoutInfo.pBindings = aBindings.data();
4840
4841 if(vkCreateDescriptorSetLayout(device: m_VKDevice, pCreateInfo: &LayoutInfo, pAllocator: nullptr, pSetLayout: &m_TextDescriptorSetLayout) != VK_SUCCESS)
4842 {
4843 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating descriptor layout failed.");
4844 return false;
4845 }
4846
4847 return true;
4848 }
4849
4850 void DestroyTextDescriptorSetLayout()
4851 {
4852 vkDestroyDescriptorSetLayout(device: m_VKDevice, descriptorSetLayout: m_TextDescriptorSetLayout, pAllocator: nullptr);
4853 }
4854
4855 [[nodiscard]] bool CreateTextGraphicsPipelineImpl(const char *pVertName, const char *pFragName, SPipelineContainer &PipeContainer, EVulkanBackendTextureModes TexMode, EVulkanBackendBlendModes BlendMode, EVulkanBackendClipModes DynamicMode)
4856 {
4857 std::array<VkVertexInputAttributeDescription, 3> aAttributeDescriptions = {};
4858 aAttributeDescriptions[0] = {.location: 0, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: 0};
4859 aAttributeDescriptions[1] = {.location: 1, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: sizeof(float) * 2};
4860 aAttributeDescriptions[2] = {.location: 2, .binding: 0, .format: VK_FORMAT_R8G8B8A8_UNORM, .offset: sizeof(float) * (2 + 2)};
4861
4862 std::array<VkDescriptorSetLayout, 1> aSetLayouts = {m_TextDescriptorSetLayout};
4863
4864 std::array<VkPushConstantRange, 2> aPushConstants{};
4865 aPushConstants[0] = {.stageFlags: VK_SHADER_STAGE_VERTEX_BIT, .offset: 0, .size: sizeof(SUniformGTextPos)};
4866 aPushConstants[1] = {.stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, .offset: sizeof(SUniformGTextPos) + sizeof(SUniformTextGFragmentOffset), .size: sizeof(SUniformTextGFragmentConstants)};
4867
4868 return CreateGraphicsPipeline<false>(pVertName, pFragName, PipeContainer, Stride: sizeof(float) * (2 + 2) + sizeof(uint8_t) * 4, aInputAttr&: aAttributeDescriptions, aSetLayouts, aPushConstants, TexMode, BlendMode, DynamicMode);
4869 }
4870
4871 [[nodiscard]] bool CreateTextGraphicsPipeline(const char *pVertName, const char *pFragName)
4872 {
4873 bool Ret = true;
4874
4875 EVulkanBackendTextureModes TexMode = VULKAN_BACKEND_TEXTURE_MODE_TEXTURED;
4876
4877 for(size_t i = 0; i < VULKAN_BACKEND_BLEND_MODE_COUNT; ++i)
4878 {
4879 for(size_t j = 0; j < VULKAN_BACKEND_CLIP_MODE_COUNT; ++j)
4880 {
4881 Ret &= CreateTextGraphicsPipelineImpl(pVertName, pFragName, PipeContainer&: m_TextPipeline, TexMode, BlendMode: EVulkanBackendBlendModes(i), DynamicMode: EVulkanBackendClipModes(j));
4882 }
4883 }
4884
4885 return Ret;
4886 }
4887
4888 template<bool HasSampler>
4889 [[nodiscard]] bool CreateTileGraphicsPipelineImpl(const char *pVertName, const char *pFragName, bool IsBorder, SPipelineContainer &PipeContainer, EVulkanBackendTextureModes TexMode, EVulkanBackendBlendModes BlendMode, EVulkanBackendClipModes DynamicMode)
4890 {
4891 std::array<VkVertexInputAttributeDescription, HasSampler ? 2 : 1> aAttributeDescriptions = {};
4892 aAttributeDescriptions[0] = {0, 0, VK_FORMAT_R32G32_SFLOAT, 0};
4893 if(HasSampler)
4894 aAttributeDescriptions[1] = {1, 0, VK_FORMAT_R8G8B8A8_UINT, sizeof(float) * 2};
4895
4896 std::array<VkDescriptorSetLayout, 1> aSetLayouts;
4897 aSetLayouts[0] = m_Standard3DTexturedDescriptorSetLayout;
4898
4899 uint32_t VertPushConstantSize = sizeof(SUniformTileGPos);
4900 if(IsBorder)
4901 VertPushConstantSize = sizeof(SUniformTileGPosBorder);
4902
4903 uint32_t FragPushConstantSize = sizeof(SUniformTileGVertColor);
4904
4905 std::array<VkPushConstantRange, 2> aPushConstants{};
4906 aPushConstants[0] = {.stageFlags: VK_SHADER_STAGE_VERTEX_BIT, .offset: 0, .size: VertPushConstantSize};
4907 aPushConstants[1] = {.stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, .offset: sizeof(SUniformTileGPosBorder) + sizeof(SUniformTileGVertColorAlign), .size: FragPushConstantSize};
4908
4909 return CreateGraphicsPipeline<false>(pVertName, pFragName, PipeContainer, HasSampler ? (sizeof(float) * 2 + sizeof(uint8_t) * 4) : (sizeof(float) * 2), aAttributeDescriptions, aSetLayouts, aPushConstants, TexMode, BlendMode, DynamicMode);
4910 }
4911
4912 template<bool HasSampler>
4913 [[nodiscard]] bool CreateTileGraphicsPipeline(const char *pVertName, const char *pFragName, bool IsBorder)
4914 {
4915 bool Ret = true;
4916
4917 EVulkanBackendTextureModes TexMode = HasSampler ? VULKAN_BACKEND_TEXTURE_MODE_TEXTURED : VULKAN_BACKEND_TEXTURE_MODE_NOT_TEXTURED;
4918
4919 for(size_t i = 0; i < VULKAN_BACKEND_BLEND_MODE_COUNT; ++i)
4920 {
4921 for(size_t j = 0; j < VULKAN_BACKEND_CLIP_MODE_COUNT; ++j)
4922 {
4923 Ret &= CreateTileGraphicsPipelineImpl<HasSampler>(pVertName, pFragName, IsBorder, !IsBorder ? m_TilePipeline : m_TileBorderPipeline, TexMode, EVulkanBackendBlendModes(i), EVulkanBackendClipModes(j));
4924 }
4925 }
4926
4927 return Ret;
4928 }
4929
4930 [[nodiscard]] bool CreatePrimExGraphicsPipelineImpl(const char *pVertName, const char *pFragName, bool Rotationless, SPipelineContainer &PipeContainer, EVulkanBackendTextureModes TexMode, EVulkanBackendBlendModes BlendMode, EVulkanBackendClipModes DynamicMode)
4931 {
4932 std::array<VkVertexInputAttributeDescription, 3> aAttributeDescriptions = {};
4933 aAttributeDescriptions[0] = {.location: 0, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: 0};
4934 aAttributeDescriptions[1] = {.location: 1, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: sizeof(float) * 2};
4935 aAttributeDescriptions[2] = {.location: 2, .binding: 0, .format: VK_FORMAT_R8G8B8A8_UNORM, .offset: sizeof(float) * (2 + 2)};
4936
4937 std::array<VkDescriptorSetLayout, 1> aSetLayouts;
4938 aSetLayouts[0] = m_StandardTexturedDescriptorSetLayout;
4939 uint32_t VertPushConstantSize = sizeof(SUniformPrimExGPos);
4940 if(Rotationless)
4941 VertPushConstantSize = sizeof(SUniformPrimExGPosRotationless);
4942
4943 uint32_t FragPushConstantSize = sizeof(SUniformPrimExGVertColor);
4944
4945 std::array<VkPushConstantRange, 2> aPushConstants{};
4946 aPushConstants[0] = {.stageFlags: VK_SHADER_STAGE_VERTEX_BIT, .offset: 0, .size: VertPushConstantSize};
4947 aPushConstants[1] = {.stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, .offset: sizeof(SUniformPrimExGPos) + sizeof(SUniformPrimExGVertColorAlign), .size: FragPushConstantSize};
4948
4949 return CreateGraphicsPipeline<false>(pVertName, pFragName, PipeContainer, Stride: sizeof(float) * (2 + 2) + sizeof(uint8_t) * 4, aInputAttr&: aAttributeDescriptions, aSetLayouts, aPushConstants, TexMode, BlendMode, DynamicMode);
4950 }
4951
4952 [[nodiscard]] bool CreatePrimExGraphicsPipeline(const char *pVertName, const char *pFragName, bool HasSampler, bool Rotationless)
4953 {
4954 bool Ret = true;
4955
4956 EVulkanBackendTextureModes TexMode = HasSampler ? VULKAN_BACKEND_TEXTURE_MODE_TEXTURED : VULKAN_BACKEND_TEXTURE_MODE_NOT_TEXTURED;
4957
4958 for(size_t i = 0; i < VULKAN_BACKEND_BLEND_MODE_COUNT; ++i)
4959 {
4960 for(size_t j = 0; j < VULKAN_BACKEND_CLIP_MODE_COUNT; ++j)
4961 {
4962 Ret &= CreatePrimExGraphicsPipelineImpl(pVertName, pFragName, Rotationless, PipeContainer&: Rotationless ? m_PrimExRotationlessPipeline : m_PrimExPipeline, TexMode, BlendMode: EVulkanBackendBlendModes(i), DynamicMode: EVulkanBackendClipModes(j));
4963 }
4964 }
4965
4966 return Ret;
4967 }
4968
4969 [[nodiscard]] bool CreateUniformDescriptorSetLayout(VkDescriptorSetLayout &SetLayout, VkShaderStageFlags StageFlags)
4970 {
4971 VkDescriptorSetLayoutBinding SamplerLayoutBinding{};
4972 SamplerLayoutBinding.binding = 1;
4973 SamplerLayoutBinding.descriptorCount = 1;
4974 SamplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
4975 SamplerLayoutBinding.pImmutableSamplers = nullptr;
4976 SamplerLayoutBinding.stageFlags = StageFlags;
4977
4978 std::array<VkDescriptorSetLayoutBinding, 1> aBindings = {SamplerLayoutBinding};
4979 VkDescriptorSetLayoutCreateInfo LayoutInfo{};
4980 LayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
4981 LayoutInfo.bindingCount = aBindings.size();
4982 LayoutInfo.pBindings = aBindings.data();
4983
4984 if(vkCreateDescriptorSetLayout(device: m_VKDevice, pCreateInfo: &LayoutInfo, pAllocator: nullptr, pSetLayout: &SetLayout) != VK_SUCCESS)
4985 {
4986 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating descriptor layout failed.");
4987 return false;
4988 }
4989 return true;
4990 }
4991
4992 [[nodiscard]] bool CreateSpriteMultiUniformDescriptorSetLayout()
4993 {
4994 return CreateUniformDescriptorSetLayout(SetLayout&: m_SpriteMultiUniformDescriptorSetLayout, StageFlags: VK_SHADER_STAGE_VERTEX_BIT);
4995 }
4996
4997 [[nodiscard]] bool CreateQuadUniformDescriptorSetLayout()
4998 {
4999 return CreateUniformDescriptorSetLayout(SetLayout&: m_QuadUniformDescriptorSetLayout, StageFlags: VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
5000 }
5001
5002 void DestroyUniformDescriptorSetLayouts()
5003 {
5004 vkDestroyDescriptorSetLayout(device: m_VKDevice, descriptorSetLayout: m_QuadUniformDescriptorSetLayout, pAllocator: nullptr);
5005 vkDestroyDescriptorSetLayout(device: m_VKDevice, descriptorSetLayout: m_SpriteMultiUniformDescriptorSetLayout, pAllocator: nullptr);
5006 }
5007
5008 [[nodiscard]] bool CreateUniformDescriptorSets(size_t RenderThreadIndex, VkDescriptorSetLayout &SetLayout, SDeviceDescriptorSet *pSets, size_t SetCount, VkBuffer BindBuffer, size_t SingleBufferInstanceSize, VkDeviceSize MemoryOffset)
5009 {
5010 VkDescriptorPool RetDescr;
5011 if(!GetDescriptorPoolForAlloc(RetDescr, DescriptorPools&: m_vUniformBufferDescrPools[RenderThreadIndex], pSets, AllocNum: SetCount))
5012 return false;
5013 VkDescriptorSetAllocateInfo DesAllocInfo{};
5014 DesAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
5015 DesAllocInfo.descriptorSetCount = 1;
5016 DesAllocInfo.pSetLayouts = &SetLayout;
5017 for(size_t i = 0; i < SetCount; ++i)
5018 {
5019 DesAllocInfo.descriptorPool = pSets[i].m_pPools->m_vPools[pSets[i].m_PoolIndex].m_Pool;
5020 if(vkAllocateDescriptorSets(device: m_VKDevice, pAllocateInfo: &DesAllocInfo, pDescriptorSets: &pSets[i].m_Descriptor) != VK_SUCCESS)
5021 {
5022 return false;
5023 }
5024
5025 VkDescriptorBufferInfo BufferInfo{};
5026 BufferInfo.buffer = BindBuffer;
5027 BufferInfo.offset = MemoryOffset + SingleBufferInstanceSize * i;
5028 BufferInfo.range = SingleBufferInstanceSize;
5029
5030 std::array<VkWriteDescriptorSet, 1> aDescriptorWrites{};
5031
5032 aDescriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5033 aDescriptorWrites[0].dstSet = pSets[i].m_Descriptor;
5034 aDescriptorWrites[0].dstBinding = 1;
5035 aDescriptorWrites[0].dstArrayElement = 0;
5036 aDescriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
5037 aDescriptorWrites[0].descriptorCount = 1;
5038 aDescriptorWrites[0].pBufferInfo = &BufferInfo;
5039
5040 vkUpdateDescriptorSets(device: m_VKDevice, descriptorWriteCount: static_cast<uint32_t>(aDescriptorWrites.size()), pDescriptorWrites: aDescriptorWrites.data(), descriptorCopyCount: 0, pDescriptorCopies: nullptr);
5041 }
5042
5043 return true;
5044 }
5045
5046 void DestroyUniformDescriptorSets(SDeviceDescriptorSet *pSets, size_t SetCount)
5047 {
5048 for(size_t i = 0; i < SetCount; ++i)
5049 {
5050 vkFreeDescriptorSets(device: m_VKDevice, descriptorPool: pSets[i].m_pPools->m_vPools[pSets[i].m_PoolIndex].m_Pool, descriptorSetCount: 1, pDescriptorSets: &pSets[i].m_Descriptor);
5051 pSets[i].m_Descriptor = VK_NULL_HANDLE;
5052 }
5053 }
5054
5055 [[nodiscard]] bool CreateSpriteMultiGraphicsPipelineImpl(const char *pVertName, const char *pFragName, SPipelineContainer &PipeContainer, EVulkanBackendTextureModes TexMode, EVulkanBackendBlendModes BlendMode, EVulkanBackendClipModes DynamicMode)
5056 {
5057 std::array<VkVertexInputAttributeDescription, 3> aAttributeDescriptions = {};
5058 aAttributeDescriptions[0] = {.location: 0, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: 0};
5059 aAttributeDescriptions[1] = {.location: 1, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: sizeof(float) * 2};
5060 aAttributeDescriptions[2] = {.location: 2, .binding: 0, .format: VK_FORMAT_R8G8B8A8_UNORM, .offset: sizeof(float) * (2 + 2)};
5061
5062 std::array<VkDescriptorSetLayout, 2> aSetLayouts;
5063 aSetLayouts[0] = m_StandardTexturedDescriptorSetLayout;
5064 aSetLayouts[1] = m_SpriteMultiUniformDescriptorSetLayout;
5065
5066 uint32_t VertPushConstantSize = sizeof(SUniformSpriteMultiGPos);
5067 uint32_t FragPushConstantSize = sizeof(SUniformSpriteMultiGVertColor);
5068
5069 std::array<VkPushConstantRange, 2> aPushConstants{};
5070 aPushConstants[0] = {.stageFlags: VK_SHADER_STAGE_VERTEX_BIT, .offset: 0, .size: VertPushConstantSize};
5071 aPushConstants[1] = {.stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, .offset: sizeof(SUniformSpriteMultiGPos) + sizeof(SUniformSpriteMultiGVertColorAlign), .size: FragPushConstantSize};
5072
5073 return CreateGraphicsPipeline<false>(pVertName, pFragName, PipeContainer, Stride: sizeof(float) * (2 + 2) + sizeof(uint8_t) * 4, aInputAttr&: aAttributeDescriptions, aSetLayouts, aPushConstants, TexMode, BlendMode, DynamicMode);
5074 }
5075
5076 [[nodiscard]] bool CreateSpriteMultiGraphicsPipeline(const char *pVertName, const char *pFragName)
5077 {
5078 bool Ret = true;
5079
5080 EVulkanBackendTextureModes TexMode = VULKAN_BACKEND_TEXTURE_MODE_TEXTURED;
5081
5082 for(size_t i = 0; i < VULKAN_BACKEND_BLEND_MODE_COUNT; ++i)
5083 {
5084 for(size_t j = 0; j < VULKAN_BACKEND_CLIP_MODE_COUNT; ++j)
5085 {
5086 Ret &= CreateSpriteMultiGraphicsPipelineImpl(pVertName, pFragName, PipeContainer&: m_SpriteMultiPipeline, TexMode, BlendMode: EVulkanBackendBlendModes(i), DynamicMode: EVulkanBackendClipModes(j));
5087 }
5088 }
5089
5090 return Ret;
5091 }
5092
5093 [[nodiscard]] bool CreateSpriteMultiPushGraphicsPipelineImpl(const char *pVertName, const char *pFragName, SPipelineContainer &PipeContainer, EVulkanBackendTextureModes TexMode, EVulkanBackendBlendModes BlendMode, EVulkanBackendClipModes DynamicMode)
5094 {
5095 std::array<VkVertexInputAttributeDescription, 3> aAttributeDescriptions = {};
5096 aAttributeDescriptions[0] = {.location: 0, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: 0};
5097 aAttributeDescriptions[1] = {.location: 1, .binding: 0, .format: VK_FORMAT_R32G32_SFLOAT, .offset: sizeof(float) * 2};
5098 aAttributeDescriptions[2] = {.location: 2, .binding: 0, .format: VK_FORMAT_R8G8B8A8_UNORM, .offset: sizeof(float) * (2 + 2)};
5099
5100 std::array<VkDescriptorSetLayout, 1> aSetLayouts;
5101 aSetLayouts[0] = m_StandardTexturedDescriptorSetLayout;
5102
5103 uint32_t VertPushConstantSize = sizeof(SUniformSpriteMultiPushGPos);
5104 uint32_t FragPushConstantSize = sizeof(SUniformSpriteMultiPushGVertColor);
5105
5106 std::array<VkPushConstantRange, 2> aPushConstants{};
5107 aPushConstants[0] = {.stageFlags: VK_SHADER_STAGE_VERTEX_BIT, .offset: 0, .size: VertPushConstantSize};
5108 aPushConstants[1] = {.stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, .offset: sizeof(SUniformSpriteMultiPushGPos), .size: FragPushConstantSize};
5109
5110 return CreateGraphicsPipeline<false>(pVertName, pFragName, PipeContainer, Stride: sizeof(float) * (2 + 2) + sizeof(uint8_t) * 4, aInputAttr&: aAttributeDescriptions, aSetLayouts, aPushConstants, TexMode, BlendMode, DynamicMode);
5111 }
5112
5113 [[nodiscard]] bool CreateSpriteMultiPushGraphicsPipeline(const char *pVertName, const char *pFragName)
5114 {
5115 bool Ret = true;
5116
5117 EVulkanBackendTextureModes TexMode = VULKAN_BACKEND_TEXTURE_MODE_TEXTURED;
5118
5119 for(size_t i = 0; i < VULKAN_BACKEND_BLEND_MODE_COUNT; ++i)
5120 {
5121 for(size_t j = 0; j < VULKAN_BACKEND_CLIP_MODE_COUNT; ++j)
5122 {
5123 Ret &= CreateSpriteMultiPushGraphicsPipelineImpl(pVertName, pFragName, PipeContainer&: m_SpriteMultiPushPipeline, TexMode, BlendMode: EVulkanBackendBlendModes(i), DynamicMode: EVulkanBackendClipModes(j));
5124 }
5125 }
5126
5127 return Ret;
5128 }
5129
5130 template<bool IsTextured>
5131 [[nodiscard]] bool CreateQuadGraphicsPipelineImpl(const char *pVertName, const char *pFragName, SPipelineContainer &PipeContainer, EVulkanBackendTextureModes TexMode, EVulkanBackendBlendModes BlendMode, EVulkanBackendClipModes DynamicMode)
5132 {
5133 std::array<VkVertexInputAttributeDescription, IsTextured ? 3 : 2> aAttributeDescriptions = {};
5134 aAttributeDescriptions[0] = {0, 0, VK_FORMAT_R32G32B32A32_SFLOAT, 0};
5135 aAttributeDescriptions[1] = {1, 0, VK_FORMAT_R8G8B8A8_UNORM, sizeof(float) * 4};
5136 if(IsTextured)
5137 aAttributeDescriptions[2] = {2, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 4 + sizeof(uint8_t) * 4};
5138
5139 std::array<VkDescriptorSetLayout, IsTextured ? 2 : 1> aSetLayouts;
5140 if(IsTextured)
5141 {
5142 aSetLayouts[0] = m_StandardTexturedDescriptorSetLayout;
5143 aSetLayouts[1] = m_QuadUniformDescriptorSetLayout;
5144 }
5145 else
5146 {
5147 aSetLayouts[0] = m_QuadUniformDescriptorSetLayout;
5148 }
5149
5150 uint32_t PushConstantSize = sizeof(SUniformQuadGPos);
5151
5152 std::array<VkPushConstantRange, 1> aPushConstants{};
5153 aPushConstants[0] = {.stageFlags: VK_SHADER_STAGE_VERTEX_BIT, .offset: 0, .size: PushConstantSize};
5154
5155 return CreateGraphicsPipeline<true>(pVertName, pFragName, PipeContainer, sizeof(float) * 4 + sizeof(uint8_t) * 4 + (IsTextured ? (sizeof(float) * 2) : 0), aAttributeDescriptions, aSetLayouts, aPushConstants, TexMode, BlendMode, DynamicMode);
5156 }
5157
5158 template<bool HasSampler>
5159 [[nodiscard]] bool CreateQuadGraphicsPipeline(const char *pVertName, const char *pFragName)
5160 {
5161 bool Ret = true;
5162
5163 EVulkanBackendTextureModes TexMode = HasSampler ? VULKAN_BACKEND_TEXTURE_MODE_TEXTURED : VULKAN_BACKEND_TEXTURE_MODE_NOT_TEXTURED;
5164
5165 for(size_t i = 0; i < VULKAN_BACKEND_BLEND_MODE_COUNT; ++i)
5166 {
5167 for(size_t j = 0; j < VULKAN_BACKEND_CLIP_MODE_COUNT; ++j)
5168 {
5169 Ret &= CreateQuadGraphicsPipelineImpl<HasSampler>(pVertName, pFragName, m_QuadPipeline, TexMode, EVulkanBackendBlendModes(i), EVulkanBackendClipModes(j));
5170 }
5171 }
5172
5173 return Ret;
5174 }
5175
5176 template<bool IsTextured>
5177 [[nodiscard]] bool CreateQuadPushGraphicsPipelineImpl(const char *pVertName, const char *pFragName, SPipelineContainer &PipeContainer, EVulkanBackendTextureModes TexMode, EVulkanBackendBlendModes BlendMode, EVulkanBackendClipModes DynamicMode)
5178 {
5179 std::array<VkVertexInputAttributeDescription, IsTextured ? 3 : 2> aAttributeDescriptions = {};
5180 aAttributeDescriptions[0] = {0, 0, VK_FORMAT_R32G32B32A32_SFLOAT, 0};
5181 aAttributeDescriptions[1] = {1, 0, VK_FORMAT_R8G8B8A8_UNORM, sizeof(float) * 4};
5182 if(IsTextured)
5183 aAttributeDescriptions[2] = {2, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 4 + sizeof(uint8_t) * 4};
5184
5185 std::array<VkDescriptorSetLayout, 1> aSetLayouts;
5186 aSetLayouts[0] = m_StandardTexturedDescriptorSetLayout;
5187
5188 uint32_t PushConstantSize = sizeof(SUniformQuadPushGPos);
5189
5190 std::array<VkPushConstantRange, 1> aPushConstants{};
5191 aPushConstants[0] = {.stageFlags: VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, .offset: 0, .size: PushConstantSize};
5192
5193 return CreateGraphicsPipeline<false>(pVertName, pFragName, PipeContainer, sizeof(float) * 4 + sizeof(uint8_t) * 4 + (IsTextured ? (sizeof(float) * 2) : 0), aAttributeDescriptions, aSetLayouts, aPushConstants, TexMode, BlendMode, DynamicMode);
5194 }
5195
5196 template<bool HasSampler>
5197 [[nodiscard]] bool CreateQuadPushGraphicsPipeline(const char *pVertName, const char *pFragName)
5198 {
5199 bool Ret = true;
5200
5201 EVulkanBackendTextureModes TexMode = HasSampler ? VULKAN_BACKEND_TEXTURE_MODE_TEXTURED : VULKAN_BACKEND_TEXTURE_MODE_NOT_TEXTURED;
5202
5203 for(size_t i = 0; i < VULKAN_BACKEND_BLEND_MODE_COUNT; ++i)
5204 {
5205 for(size_t j = 0; j < VULKAN_BACKEND_CLIP_MODE_COUNT; ++j)
5206 {
5207 Ret &= CreateQuadPushGraphicsPipelineImpl<HasSampler>(pVertName, pFragName, m_QuadPushPipeline, TexMode, EVulkanBackendBlendModes(i), EVulkanBackendClipModes(j));
5208 }
5209 }
5210
5211 return Ret;
5212 }
5213
5214 [[nodiscard]] bool CreateCommandPool()
5215 {
5216 VkCommandPoolCreateInfo CreatePoolInfo{};
5217 CreatePoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
5218 CreatePoolInfo.queueFamilyIndex = m_VKGraphicsQueueIndex;
5219 CreatePoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
5220
5221 m_vCommandPools.resize(new_size: m_ThreadCount);
5222 for(size_t i = 0; i < m_ThreadCount; ++i)
5223 {
5224 if(vkCreateCommandPool(device: m_VKDevice, pCreateInfo: &CreatePoolInfo, pAllocator: nullptr, pCommandPool: &m_vCommandPools[i]) != VK_SUCCESS)
5225 {
5226 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating the command pool failed.");
5227 return false;
5228 }
5229 }
5230 return true;
5231 }
5232
5233 void DestroyCommandPool()
5234 {
5235 for(size_t i = 0; i < m_ThreadCount; ++i)
5236 {
5237 vkDestroyCommandPool(device: m_VKDevice, commandPool: m_vCommandPools[i], pAllocator: nullptr);
5238 }
5239 }
5240
5241 [[nodiscard]] bool CreateCommandBuffers()
5242 {
5243 m_vMainDrawCommandBuffers.resize(new_size: m_SwapChainImageCount);
5244 if(m_ThreadCount > 1)
5245 {
5246 m_vvThreadDrawCommandBuffers.resize(new_size: m_ThreadCount);
5247 m_vvUsedThreadDrawCommandBuffer.resize(new_size: m_ThreadCount);
5248 m_vHelperThreadDrawCommandBuffers.resize(new_size: m_ThreadCount);
5249 for(auto &ThreadDrawCommandBuffers : m_vvThreadDrawCommandBuffers)
5250 {
5251 ThreadDrawCommandBuffers.resize(new_size: m_SwapChainImageCount);
5252 }
5253 for(auto &UsedThreadDrawCommandBuffer : m_vvUsedThreadDrawCommandBuffer)
5254 {
5255 UsedThreadDrawCommandBuffer.resize(new_size: m_SwapChainImageCount, x: false);
5256 }
5257 }
5258 m_vMemoryCommandBuffers.resize(new_size: m_SwapChainImageCount);
5259 m_vUsedMemoryCommandBuffer.resize(new_size: m_SwapChainImageCount, x: false);
5260
5261 VkCommandBufferAllocateInfo AllocInfo{};
5262 AllocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
5263 AllocInfo.commandPool = m_vCommandPools[0];
5264 AllocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
5265 AllocInfo.commandBufferCount = (uint32_t)m_vMainDrawCommandBuffers.size();
5266
5267 if(vkAllocateCommandBuffers(device: m_VKDevice, pAllocateInfo: &AllocInfo, pCommandBuffers: m_vMainDrawCommandBuffers.data()) != VK_SUCCESS)
5268 {
5269 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Allocating command buffers failed.");
5270 return false;
5271 }
5272
5273 AllocInfo.commandBufferCount = (uint32_t)m_vMemoryCommandBuffers.size();
5274
5275 if(vkAllocateCommandBuffers(device: m_VKDevice, pAllocateInfo: &AllocInfo, pCommandBuffers: m_vMemoryCommandBuffers.data()) != VK_SUCCESS)
5276 {
5277 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Allocating memory command buffers failed.");
5278 return false;
5279 }
5280
5281 if(m_ThreadCount > 1)
5282 {
5283 size_t Count = 0;
5284 for(auto &ThreadDrawCommandBuffers : m_vvThreadDrawCommandBuffers)
5285 {
5286 AllocInfo.commandPool = m_vCommandPools[Count];
5287 ++Count;
5288 AllocInfo.commandBufferCount = (uint32_t)ThreadDrawCommandBuffers.size();
5289 AllocInfo.level = VK_COMMAND_BUFFER_LEVEL_SECONDARY;
5290 if(vkAllocateCommandBuffers(device: m_VKDevice, pAllocateInfo: &AllocInfo, pCommandBuffers: ThreadDrawCommandBuffers.data()) != VK_SUCCESS)
5291 {
5292 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Allocating thread command buffers failed.");
5293 return false;
5294 }
5295 }
5296 }
5297
5298 return true;
5299 }
5300
5301 void DestroyCommandBuffer()
5302 {
5303 if(m_ThreadCount > 1)
5304 {
5305 size_t Count = 0;
5306 for(auto &ThreadDrawCommandBuffers : m_vvThreadDrawCommandBuffers)
5307 {
5308 vkFreeCommandBuffers(device: m_VKDevice, commandPool: m_vCommandPools[Count], commandBufferCount: static_cast<uint32_t>(ThreadDrawCommandBuffers.size()), pCommandBuffers: ThreadDrawCommandBuffers.data());
5309 ++Count;
5310 }
5311 }
5312
5313 vkFreeCommandBuffers(device: m_VKDevice, commandPool: m_vCommandPools[0], commandBufferCount: static_cast<uint32_t>(m_vMemoryCommandBuffers.size()), pCommandBuffers: m_vMemoryCommandBuffers.data());
5314 vkFreeCommandBuffers(device: m_VKDevice, commandPool: m_vCommandPools[0], commandBufferCount: static_cast<uint32_t>(m_vMainDrawCommandBuffers.size()), pCommandBuffers: m_vMainDrawCommandBuffers.data());
5315
5316 m_vvThreadDrawCommandBuffers.clear();
5317 m_vvUsedThreadDrawCommandBuffer.clear();
5318 m_vHelperThreadDrawCommandBuffers.clear();
5319
5320 m_vMainDrawCommandBuffers.clear();
5321 m_vMemoryCommandBuffers.clear();
5322 m_vUsedMemoryCommandBuffer.clear();
5323 }
5324
5325 [[nodiscard]] bool CreateSyncObjects()
5326 {
5327 // Create one more sync object than there are frames in flight
5328 auto SyncObjectCount = m_SwapChainImageCount + 1;
5329 m_vWaitSemaphores.resize(new_size: SyncObjectCount);
5330 m_vSigSemaphores.resize(new_size: SyncObjectCount);
5331
5332 m_vFrameFences.resize(new_size: SyncObjectCount);
5333 m_vImagesFences.resize(new_size: m_SwapChainImageCount, VK_NULL_HANDLE);
5334
5335 VkSemaphoreCreateInfo CreateSemaphoreInfo{};
5336 CreateSemaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
5337
5338 VkFenceCreateInfo FenceInfo{};
5339 FenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
5340 FenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
5341
5342 for(size_t i = 0; i < SyncObjectCount; i++)
5343 {
5344 if(vkCreateSemaphore(device: m_VKDevice, pCreateInfo: &CreateSemaphoreInfo, pAllocator: nullptr, pSemaphore: &m_vWaitSemaphores[i]) != VK_SUCCESS ||
5345 vkCreateSemaphore(device: m_VKDevice, pCreateInfo: &CreateSemaphoreInfo, pAllocator: nullptr, pSemaphore: &m_vSigSemaphores[i]) != VK_SUCCESS ||
5346 vkCreateFence(device: m_VKDevice, pCreateInfo: &FenceInfo, pAllocator: nullptr, pFence: &m_vFrameFences[i]) != VK_SUCCESS)
5347 {
5348 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating swap chain sync objects(fences, semaphores) failed.");
5349 return false;
5350 }
5351 }
5352
5353 return true;
5354 }
5355
5356 void DestroySyncObjects()
5357 {
5358 for(size_t i = 0; i < m_vWaitSemaphores.size(); i++)
5359 {
5360 vkDestroySemaphore(device: m_VKDevice, semaphore: m_vWaitSemaphores[i], pAllocator: nullptr);
5361 vkDestroySemaphore(device: m_VKDevice, semaphore: m_vSigSemaphores[i], pAllocator: nullptr);
5362 vkDestroyFence(device: m_VKDevice, fence: m_vFrameFences[i], pAllocator: nullptr);
5363 }
5364
5365 m_vWaitSemaphores.clear();
5366 m_vSigSemaphores.clear();
5367
5368 m_vFrameFences.clear();
5369 m_vImagesFences.clear();
5370
5371 m_CurFrameSyncObject = 0;
5372 }
5373
5374 void DestroyBufferOfFrame(size_t ImageIndex, SFrameBuffers &Buffer)
5375 {
5376 CleanBufferPair(ImageIndex, Buffer&: Buffer.m_Buffer, BufferMem&: Buffer.m_BufferMem);
5377 }
5378
5379 void DestroyUniBufferOfFrame(size_t ImageIndex, SFrameUniformBuffers &Buffer)
5380 {
5381 CleanBufferPair(ImageIndex, Buffer&: Buffer.m_Buffer, BufferMem&: Buffer.m_BufferMem);
5382 for(auto &DescrSet : Buffer.m_aUniformSets)
5383 {
5384 if(DescrSet.m_Descriptor != VK_NULL_HANDLE)
5385 {
5386 DestroyUniformDescriptorSets(pSets: &DescrSet, SetCount: 1);
5387 }
5388 }
5389 }
5390
5391 /*************
5392 * SWAP CHAIN
5393 **************/
5394
5395 void CleanupVulkanSwapChain(bool ForceSwapChainDestruct)
5396 {
5397 m_StandardPipeline.Destroy(Device&: m_VKDevice);
5398 m_StandardLinePipeline.Destroy(Device&: m_VKDevice);
5399 m_Standard3DPipeline.Destroy(Device&: m_VKDevice);
5400 m_TextPipeline.Destroy(Device&: m_VKDevice);
5401 m_TilePipeline.Destroy(Device&: m_VKDevice);
5402 m_TileBorderPipeline.Destroy(Device&: m_VKDevice);
5403 m_PrimExPipeline.Destroy(Device&: m_VKDevice);
5404 m_PrimExRotationlessPipeline.Destroy(Device&: m_VKDevice);
5405 m_SpriteMultiPipeline.Destroy(Device&: m_VKDevice);
5406 m_SpriteMultiPushPipeline.Destroy(Device&: m_VKDevice);
5407 m_QuadPipeline.Destroy(Device&: m_VKDevice);
5408 m_QuadPushPipeline.Destroy(Device&: m_VKDevice);
5409
5410 DestroyFramebuffers();
5411
5412 DestroyRenderPass();
5413
5414 DestroyMultiSamplerImageAttachments();
5415
5416 DestroyImageViews();
5417 ClearSwapChainImageHandles();
5418
5419 DestroySwapChain(ForceDestroy: ForceSwapChainDestruct);
5420
5421 m_SwapchainCreated = false;
5422 }
5423
5424 template<bool IsLastCleanup>
5425 void CleanupVulkan(size_t SwapchainCount)
5426 {
5427 if(IsLastCleanup)
5428 {
5429 if(m_SwapchainCreated)
5430 CleanupVulkanSwapChain(ForceSwapChainDestruct: true);
5431
5432 // clean all images, buffers, buffer containers
5433 for(auto &Texture : m_vTextures)
5434 {
5435 if(Texture.m_VKTextDescrSet.m_Descriptor != VK_NULL_HANDLE && IsVerbose())
5436 {
5437 dbg_msg(sys: "vulkan", fmt: "text textures not cleared over cmd.");
5438 }
5439 DestroyTexture(Texture);
5440 }
5441
5442 for(auto &BufferObject : m_vBufferObjects)
5443 {
5444 if(!BufferObject.m_IsStreamedBuffer)
5445 FreeVertexMemBlock(Block&: BufferObject.m_BufferObject.m_Mem);
5446 }
5447
5448 m_vBufferContainers.clear();
5449 }
5450
5451 m_vImageLastFrameCheck.clear();
5452
5453 m_vLastPipeline.clear();
5454
5455 for(size_t i = 0; i < m_ThreadCount; ++i)
5456 {
5457 m_vStreamedVertexBuffers[i].Destroy(DestroyBuffer: [&](size_t ImageIndex, SFrameBuffers &Buffer) { DestroyBufferOfFrame(ImageIndex, Buffer); });
5458 m_vStreamedUniformBuffers[i].Destroy(DestroyBuffer: [&](size_t ImageIndex, SFrameUniformBuffers &Buffer) { DestroyUniBufferOfFrame(ImageIndex, Buffer); });
5459 }
5460 m_vStreamedVertexBuffers.clear();
5461 m_vStreamedUniformBuffers.clear();
5462
5463 for(size_t i = 0; i < SwapchainCount; ++i)
5464 {
5465 ClearFrameData(FrameImageIndex: i);
5466 }
5467
5468 m_vvFrameDelayedBufferCleanup.clear();
5469 m_vvFrameDelayedTextureCleanup.clear();
5470 m_vvFrameDelayedTextTexturesCleanup.clear();
5471
5472 m_StagingBufferCache.DestroyFrameData(ImageCount: SwapchainCount);
5473 m_StagingBufferCacheImage.DestroyFrameData(ImageCount: SwapchainCount);
5474 m_VertexBufferCache.DestroyFrameData(ImageCount: SwapchainCount);
5475 for(auto &ImageBufferCache : m_ImageBufferCaches)
5476 ImageBufferCache.second.DestroyFrameData(ImageCount: SwapchainCount);
5477
5478 if(IsLastCleanup)
5479 {
5480 m_StagingBufferCache.Destroy(Device&: m_VKDevice);
5481 m_StagingBufferCacheImage.Destroy(Device&: m_VKDevice);
5482 m_VertexBufferCache.Destroy(Device&: m_VKDevice);
5483 for(auto &ImageBufferCache : m_ImageBufferCaches)
5484 ImageBufferCache.second.Destroy(Device&: m_VKDevice);
5485
5486 m_ImageBufferCaches.clear();
5487
5488 DestroyTextureSamplers();
5489 DestroyDescriptorPools();
5490
5491 DeletePresentedImageDataImage();
5492 }
5493
5494 DestroySyncObjects();
5495 DestroyCommandBuffer();
5496
5497 if(IsLastCleanup)
5498 {
5499 DestroyCommandPool();
5500 }
5501
5502 if(IsLastCleanup)
5503 {
5504 DestroyUniformDescriptorSetLayouts();
5505 DestroyTextDescriptorSetLayout();
5506 DestroyDescriptorSetLayouts();
5507 }
5508 }
5509
5510 void CleanupVulkanSDL()
5511 {
5512 if(m_VKInstance != VK_NULL_HANDLE)
5513 {
5514 DestroySurface();
5515 vkDestroyDevice(device: m_VKDevice, pAllocator: nullptr);
5516
5517 if(g_Config.m_DbgGfx == DEBUG_GFX_MODE_MINIMUM || g_Config.m_DbgGfx == DEBUG_GFX_MODE_ALL)
5518 {
5519 UnregisterDebugCallback();
5520 }
5521 vkDestroyInstance(instance: m_VKInstance, pAllocator: nullptr);
5522 m_VKInstance = VK_NULL_HANDLE;
5523 }
5524 }
5525
5526 int RecreateSwapChain()
5527 {
5528 int Ret = 0;
5529 vkDeviceWaitIdle(device: m_VKDevice);
5530
5531 if(IsVerbose())
5532 {
5533 dbg_msg(sys: "vulkan", fmt: "recreating swap chain.");
5534 }
5535
5536 VkSwapchainKHR OldSwapChain = VK_NULL_HANDLE;
5537 uint32_t OldSwapChainImageCount = m_SwapChainImageCount;
5538
5539 if(m_SwapchainCreated)
5540 CleanupVulkanSwapChain(ForceSwapChainDestruct: false);
5541
5542 // set new multi sampling if it was requested
5543 if(m_NextMultiSamplingCount != std::numeric_limits<uint32_t>::max())
5544 {
5545 m_MultiSamplingCount = m_NextMultiSamplingCount;
5546 m_NextMultiSamplingCount = std::numeric_limits<uint32_t>::max();
5547 }
5548
5549 if(!m_SwapchainCreated)
5550 Ret = InitVulkanSwapChain(OldSwapChain);
5551
5552 if(OldSwapChainImageCount != m_SwapChainImageCount)
5553 {
5554 CleanupVulkan<false>(SwapchainCount: OldSwapChainImageCount);
5555 InitVulkan<false>();
5556 }
5557
5558 if(OldSwapChain != VK_NULL_HANDLE)
5559 {
5560 vkDestroySwapchainKHR(device: m_VKDevice, swapchain: OldSwapChain, pAllocator: nullptr);
5561 }
5562
5563 if(Ret != 0 && IsVerbose())
5564 {
5565 dbg_msg(sys: "vulkan", fmt: "recreating swap chain failed.");
5566 }
5567
5568 return Ret;
5569 }
5570
5571 int InitVulkanSDL(SDL_Window *pWindow, uint32_t CanvasWidth, uint32_t CanvasHeight, char *pRendererString, char *pVendorString, char *pVersionString)
5572 {
5573 std::vector<std::string> vVKExtensions;
5574 std::vector<std::string> vVKLayers;
5575
5576 m_CanvasWidth = CanvasWidth;
5577 m_CanvasHeight = CanvasHeight;
5578
5579 if(!GetVulkanExtensions(pWindow, vVKExtensions))
5580 return -1;
5581
5582 if(!GetVulkanLayers(vVKLayers))
5583 return -1;
5584
5585 if(!CreateVulkanInstance(vVKLayers, vVKExtensions, TryDebugExtensions: true))
5586 return -1;
5587
5588 if(g_Config.m_DbgGfx == DEBUG_GFX_MODE_MINIMUM || g_Config.m_DbgGfx == DEBUG_GFX_MODE_ALL)
5589 {
5590 SetupDebugCallback();
5591
5592 for(auto &VKLayer : vVKLayers)
5593 {
5594 dbg_msg(sys: "vulkan", fmt: "Validation layer: %s", VKLayer.c_str());
5595 }
5596 }
5597
5598 if(!SelectGpu(pRendererName: pRendererString, pVendorName: pVendorString, pVersionName: pVersionString))
5599 return -1;
5600
5601 if(!CreateLogicalDevice(vVKLayers))
5602 return -1;
5603
5604 GetDeviceQueue();
5605
5606 if(!CreateSurface(pWindow))
5607 return -1;
5608
5609 return 0;
5610 }
5611
5612 /************************
5613 * MEMORY MANAGEMENT
5614 ************************/
5615
5616 uint32_t FindMemoryType(VkPhysicalDevice PhyDevice, uint32_t TypeFilter, VkMemoryPropertyFlags Properties)
5617 {
5618 VkPhysicalDeviceMemoryProperties MemProperties;
5619 vkGetPhysicalDeviceMemoryProperties(physicalDevice: PhyDevice, pMemoryProperties: &MemProperties);
5620
5621 for(uint32_t i = 0; i < MemProperties.memoryTypeCount; i++)
5622 {
5623 if((TypeFilter & (1 << i)) && (MemProperties.memoryTypes[i].propertyFlags & Properties) == Properties)
5624 {
5625 return i;
5626 }
5627 }
5628
5629 return 0;
5630 }
5631
5632 [[nodiscard]] bool CreateBuffer(VkDeviceSize BufferSize, EMemoryBlockUsage MemUsage, VkBufferUsageFlags BufferUsage, VkMemoryPropertyFlags MemoryProperties, VkBuffer &VKBuffer, SDeviceMemoryBlock &VKBufferMemory)
5633 {
5634 VkBufferCreateInfo BufferInfo{};
5635 BufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
5636 BufferInfo.size = BufferSize;
5637 BufferInfo.usage = BufferUsage;
5638 BufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
5639
5640 if(vkCreateBuffer(device: m_VKDevice, pCreateInfo: &BufferInfo, pAllocator: nullptr, pBuffer: &VKBuffer) != VK_SUCCESS)
5641 {
5642 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_OUT_OF_MEMORY_BUFFER, pErr: "Buffer creation failed.",
5643 pErrStrExtra: GetMemoryUsageShort());
5644 return false;
5645 }
5646
5647 VkMemoryRequirements MemRequirements;
5648 vkGetBufferMemoryRequirements(device: m_VKDevice, buffer: VKBuffer, pMemoryRequirements: &MemRequirements);
5649
5650 VkMemoryAllocateInfo MemAllocInfo{};
5651 MemAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
5652 MemAllocInfo.allocationSize = MemRequirements.size;
5653 MemAllocInfo.memoryTypeIndex = FindMemoryType(PhyDevice: m_VKGPU, TypeFilter: MemRequirements.memoryTypeBits, Properties: MemoryProperties);
5654
5655 VKBufferMemory.m_Size = MemRequirements.size;
5656
5657 if(MemUsage == MEMORY_BLOCK_USAGE_BUFFER)
5658 m_pBufferMemoryUsage->store(i: m_pBufferMemoryUsage->load(m: std::memory_order_relaxed) + MemRequirements.size, m: std::memory_order_relaxed);
5659 else if(MemUsage == MEMORY_BLOCK_USAGE_STAGING)
5660 m_pStagingMemoryUsage->store(i: m_pStagingMemoryUsage->load(m: std::memory_order_relaxed) + MemRequirements.size, m: std::memory_order_relaxed);
5661 else if(MemUsage == MEMORY_BLOCK_USAGE_STREAM)
5662 m_pStreamMemoryUsage->store(i: m_pStreamMemoryUsage->load(m: std::memory_order_relaxed) + MemRequirements.size, m: std::memory_order_relaxed);
5663
5664 if(IsVerbose())
5665 {
5666 VerboseAllocatedMemory(Size: MemRequirements.size, FrameImageIndex: m_CurImageIndex, MemUsage);
5667 }
5668
5669 if(!AllocateVulkanMemory(pAllocateInfo: &MemAllocInfo, pMemory: &VKBufferMemory.m_Mem))
5670 {
5671 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_OUT_OF_MEMORY_BUFFER, pErr: "Allocation for buffer object failed.",
5672 pErrStrExtra: GetMemoryUsageShort());
5673 return false;
5674 }
5675
5676 VKBufferMemory.m_UsageType = MemUsage;
5677
5678 if(vkBindBufferMemory(device: m_VKDevice, buffer: VKBuffer, memory: VKBufferMemory.m_Mem, memoryOffset: 0) != VK_SUCCESS)
5679 {
5680 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_OUT_OF_MEMORY_BUFFER, pErr: "Binding memory to buffer failed.",
5681 pErrStrExtra: GetMemoryUsageShort());
5682 return false;
5683 }
5684
5685 return true;
5686 }
5687
5688 [[nodiscard]] bool AllocateDescriptorPool(SDeviceDescriptorPools &DescriptorPools, size_t AllocPoolSize)
5689 {
5690 SDeviceDescriptorPool NewPool;
5691 NewPool.m_Size = AllocPoolSize;
5692
5693 VkDescriptorPoolSize PoolSize{};
5694 if(DescriptorPools.m_IsUniformPool)
5695 PoolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
5696 else
5697 PoolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
5698 PoolSize.descriptorCount = AllocPoolSize;
5699
5700 VkDescriptorPoolCreateInfo PoolInfo{};
5701 PoolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
5702 PoolInfo.poolSizeCount = 1;
5703 PoolInfo.pPoolSizes = &PoolSize;
5704 PoolInfo.maxSets = AllocPoolSize;
5705 PoolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
5706
5707 if(vkCreateDescriptorPool(device: m_VKDevice, pCreateInfo: &PoolInfo, pAllocator: nullptr, pDescriptorPool: &NewPool.m_Pool) != VK_SUCCESS)
5708 {
5709 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_INIT, pErr: "Creating the descriptor pool failed.");
5710 return false;
5711 }
5712
5713 DescriptorPools.m_vPools.push_back(x: NewPool);
5714
5715 return true;
5716 }
5717
5718 [[nodiscard]] bool CreateDescriptorPools()
5719 {
5720 m_StandardTextureDescrPool.m_IsUniformPool = false;
5721 m_StandardTextureDescrPool.m_DefaultAllocSize = 1024;
5722 m_TextTextureDescrPool.m_IsUniformPool = false;
5723 m_TextTextureDescrPool.m_DefaultAllocSize = 8;
5724
5725 m_vUniformBufferDescrPools.resize(new_size: m_ThreadCount);
5726 for(auto &UniformBufferDescrPool : m_vUniformBufferDescrPools)
5727 {
5728 UniformBufferDescrPool.m_IsUniformPool = true;
5729 UniformBufferDescrPool.m_DefaultAllocSize = 512;
5730 }
5731
5732 bool Ret = AllocateDescriptorPool(DescriptorPools&: m_StandardTextureDescrPool, AllocPoolSize: CCommandBuffer::MAX_TEXTURES);
5733 Ret |= AllocateDescriptorPool(DescriptorPools&: m_TextTextureDescrPool, AllocPoolSize: 8);
5734
5735 for(auto &UniformBufferDescrPool : m_vUniformBufferDescrPools)
5736 {
5737 Ret |= AllocateDescriptorPool(DescriptorPools&: UniformBufferDescrPool, AllocPoolSize: 64);
5738 }
5739
5740 return Ret;
5741 }
5742
5743 void DestroyDescriptorPools()
5744 {
5745 for(auto &DescrPool : m_StandardTextureDescrPool.m_vPools)
5746 vkDestroyDescriptorPool(device: m_VKDevice, descriptorPool: DescrPool.m_Pool, pAllocator: nullptr);
5747 for(auto &DescrPool : m_TextTextureDescrPool.m_vPools)
5748 vkDestroyDescriptorPool(device: m_VKDevice, descriptorPool: DescrPool.m_Pool, pAllocator: nullptr);
5749
5750 for(auto &UniformBufferDescrPool : m_vUniformBufferDescrPools)
5751 {
5752 for(auto &DescrPool : UniformBufferDescrPool.m_vPools)
5753 vkDestroyDescriptorPool(device: m_VKDevice, descriptorPool: DescrPool.m_Pool, pAllocator: nullptr);
5754 }
5755 m_vUniformBufferDescrPools.clear();
5756 }
5757
5758 [[nodiscard]] bool GetDescriptorPoolForAlloc(VkDescriptorPool &RetDescr, SDeviceDescriptorPools &DescriptorPools, SDeviceDescriptorSet *pSets, size_t AllocNum)
5759 {
5760 size_t CurAllocNum = AllocNum;
5761 size_t CurAllocOffset = 0;
5762 RetDescr = VK_NULL_HANDLE;
5763
5764 while(CurAllocNum > 0)
5765 {
5766 size_t AllocatedInThisRun = 0;
5767
5768 bool Found = false;
5769 size_t DescriptorPoolIndex = std::numeric_limits<size_t>::max();
5770 for(size_t i = 0; i < DescriptorPools.m_vPools.size(); ++i)
5771 {
5772 auto &Pool = DescriptorPools.m_vPools[i];
5773 if(Pool.m_CurSize + CurAllocNum < Pool.m_Size)
5774 {
5775 AllocatedInThisRun = CurAllocNum;
5776 Pool.m_CurSize += CurAllocNum;
5777 Found = true;
5778 if(RetDescr == VK_NULL_HANDLE)
5779 RetDescr = Pool.m_Pool;
5780 DescriptorPoolIndex = i;
5781 break;
5782 }
5783 else
5784 {
5785 size_t RemainingPoolCount = Pool.m_Size - Pool.m_CurSize;
5786 if(RemainingPoolCount > 0)
5787 {
5788 AllocatedInThisRun = RemainingPoolCount;
5789 Pool.m_CurSize += RemainingPoolCount;
5790 Found = true;
5791 if(RetDescr == VK_NULL_HANDLE)
5792 RetDescr = Pool.m_Pool;
5793 DescriptorPoolIndex = i;
5794 break;
5795 }
5796 }
5797 }
5798
5799 if(!Found)
5800 {
5801 DescriptorPoolIndex = DescriptorPools.m_vPools.size();
5802
5803 if(!AllocateDescriptorPool(DescriptorPools, AllocPoolSize: DescriptorPools.m_DefaultAllocSize))
5804 return false;
5805
5806 AllocatedInThisRun = minimum(a: (size_t)DescriptorPools.m_DefaultAllocSize, b: CurAllocNum);
5807
5808 auto &Pool = DescriptorPools.m_vPools.back();
5809 Pool.m_CurSize += AllocatedInThisRun;
5810 if(RetDescr == VK_NULL_HANDLE)
5811 RetDescr = Pool.m_Pool;
5812 }
5813
5814 for(size_t i = CurAllocOffset; i < CurAllocOffset + AllocatedInThisRun; ++i)
5815 {
5816 pSets[i].m_pPools = &DescriptorPools;
5817 pSets[i].m_PoolIndex = DescriptorPoolIndex;
5818 }
5819 CurAllocOffset += AllocatedInThisRun;
5820 CurAllocNum -= AllocatedInThisRun;
5821 }
5822
5823 return true;
5824 }
5825
5826 void FreeDescriptorSetFromPool(SDeviceDescriptorSet &DescrSet)
5827 {
5828 if(DescrSet.m_PoolIndex != std::numeric_limits<size_t>::max())
5829 {
5830 vkFreeDescriptorSets(device: m_VKDevice, descriptorPool: DescrSet.m_pPools->m_vPools[DescrSet.m_PoolIndex].m_Pool, descriptorSetCount: 1, pDescriptorSets: &DescrSet.m_Descriptor);
5831 DescrSet.m_pPools->m_vPools[DescrSet.m_PoolIndex].m_CurSize -= 1;
5832 }
5833 }
5834
5835 [[nodiscard]] bool CreateNewTexturedStandardDescriptorSets(size_t TextureSlot, size_t DescrIndex)
5836 {
5837 auto &Texture = m_vTextures[TextureSlot];
5838
5839 auto &DescrSet = Texture.m_aVKStandardTexturedDescrSets[DescrIndex];
5840
5841 VkDescriptorSetAllocateInfo DesAllocInfo{};
5842 DesAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
5843 if(!GetDescriptorPoolForAlloc(RetDescr&: DesAllocInfo.descriptorPool, DescriptorPools&: m_StandardTextureDescrPool, pSets: &DescrSet, AllocNum: 1))
5844 return false;
5845 DesAllocInfo.descriptorSetCount = 1;
5846 DesAllocInfo.pSetLayouts = &m_StandardTexturedDescriptorSetLayout;
5847
5848 if(vkAllocateDescriptorSets(device: m_VKDevice, pAllocateInfo: &DesAllocInfo, pDescriptorSets: &DescrSet.m_Descriptor) != VK_SUCCESS)
5849 {
5850 return false;
5851 }
5852
5853 VkDescriptorImageInfo ImageInfo{};
5854 ImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5855 ImageInfo.imageView = Texture.m_ImgView;
5856 ImageInfo.sampler = Texture.m_aSamplers[DescrIndex];
5857
5858 std::array<VkWriteDescriptorSet, 1> aDescriptorWrites{};
5859
5860 aDescriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5861 aDescriptorWrites[0].dstSet = DescrSet.m_Descriptor;
5862 aDescriptorWrites[0].dstBinding = 0;
5863 aDescriptorWrites[0].dstArrayElement = 0;
5864 aDescriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
5865 aDescriptorWrites[0].descriptorCount = 1;
5866 aDescriptorWrites[0].pImageInfo = &ImageInfo;
5867
5868 vkUpdateDescriptorSets(device: m_VKDevice, descriptorWriteCount: static_cast<uint32_t>(aDescriptorWrites.size()), pDescriptorWrites: aDescriptorWrites.data(), descriptorCopyCount: 0, pDescriptorCopies: nullptr);
5869
5870 return true;
5871 }
5872
5873 void DestroyTexturedStandardDescriptorSets(CTexture &Texture, size_t DescrIndex)
5874 {
5875 auto &DescrSet = Texture.m_aVKStandardTexturedDescrSets[DescrIndex];
5876 FreeDescriptorSetFromPool(DescrSet);
5877 DescrSet = {};
5878 }
5879
5880 [[nodiscard]] bool CreateNew3DTexturedStandardDescriptorSets(size_t TextureSlot)
5881 {
5882 auto &Texture = m_vTextures[TextureSlot];
5883
5884 auto &DescrSet = Texture.m_VKStandard3DTexturedDescrSet;
5885
5886 VkDescriptorSetAllocateInfo DesAllocInfo{};
5887 DesAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
5888 if(!GetDescriptorPoolForAlloc(RetDescr&: DesAllocInfo.descriptorPool, DescriptorPools&: m_StandardTextureDescrPool, pSets: &DescrSet, AllocNum: 1))
5889 return false;
5890 DesAllocInfo.descriptorSetCount = 1;
5891 DesAllocInfo.pSetLayouts = &m_Standard3DTexturedDescriptorSetLayout;
5892
5893 if(vkAllocateDescriptorSets(device: m_VKDevice, pAllocateInfo: &DesAllocInfo, pDescriptorSets: &DescrSet.m_Descriptor) != VK_SUCCESS)
5894 {
5895 return false;
5896 }
5897
5898 VkDescriptorImageInfo ImageInfo{};
5899 ImageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5900 ImageInfo.imageView = Texture.m_Img3DView;
5901 ImageInfo.sampler = Texture.m_Sampler3D;
5902
5903 std::array<VkWriteDescriptorSet, 1> aDescriptorWrites{};
5904
5905 aDescriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5906 aDescriptorWrites[0].dstSet = DescrSet.m_Descriptor;
5907 aDescriptorWrites[0].dstBinding = 0;
5908 aDescriptorWrites[0].dstArrayElement = 0;
5909 aDescriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
5910 aDescriptorWrites[0].descriptorCount = 1;
5911 aDescriptorWrites[0].pImageInfo = &ImageInfo;
5912
5913 vkUpdateDescriptorSets(device: m_VKDevice, descriptorWriteCount: static_cast<uint32_t>(aDescriptorWrites.size()), pDescriptorWrites: aDescriptorWrites.data(), descriptorCopyCount: 0, pDescriptorCopies: nullptr);
5914
5915 return true;
5916 }
5917
5918 void DestroyTextured3DStandardDescriptorSets(CTexture &Texture)
5919 {
5920 auto &DescrSet = Texture.m_VKStandard3DTexturedDescrSet;
5921 FreeDescriptorSetFromPool(DescrSet);
5922 }
5923
5924 [[nodiscard]] bool CreateNewTextDescriptorSets(size_t Texture, size_t TextureOutline)
5925 {
5926 auto &TextureText = m_vTextures[Texture];
5927 auto &TextureTextOutline = m_vTextures[TextureOutline];
5928 auto &DescrSetText = TextureText.m_VKTextDescrSet;
5929
5930 VkDescriptorSetAllocateInfo DesAllocInfo{};
5931 DesAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
5932 if(!GetDescriptorPoolForAlloc(RetDescr&: DesAllocInfo.descriptorPool, DescriptorPools&: m_TextTextureDescrPool, pSets: &DescrSetText, AllocNum: 1))
5933 return false;
5934 DesAllocInfo.descriptorSetCount = 1;
5935 DesAllocInfo.pSetLayouts = &m_TextDescriptorSetLayout;
5936
5937 if(vkAllocateDescriptorSets(device: m_VKDevice, pAllocateInfo: &DesAllocInfo, pDescriptorSets: &DescrSetText.m_Descriptor) != VK_SUCCESS)
5938 {
5939 return false;
5940 }
5941
5942 std::array<VkDescriptorImageInfo, 2> aImageInfo{};
5943 aImageInfo[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5944 aImageInfo[0].imageView = TextureText.m_ImgView;
5945 aImageInfo[0].sampler = TextureText.m_aSamplers[0];
5946 aImageInfo[1].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
5947 aImageInfo[1].imageView = TextureTextOutline.m_ImgView;
5948 aImageInfo[1].sampler = TextureTextOutline.m_aSamplers[0];
5949
5950 std::array<VkWriteDescriptorSet, 2> aDescriptorWrites{};
5951
5952 aDescriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
5953 aDescriptorWrites[0].dstSet = DescrSetText.m_Descriptor;
5954 aDescriptorWrites[0].dstBinding = 0;
5955 aDescriptorWrites[0].dstArrayElement = 0;
5956 aDescriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
5957 aDescriptorWrites[0].descriptorCount = 1;
5958 aDescriptorWrites[0].pImageInfo = aImageInfo.data();
5959 aDescriptorWrites[1] = aDescriptorWrites[0];
5960 aDescriptorWrites[1].dstBinding = 1;
5961 aDescriptorWrites[1].pImageInfo = &aImageInfo[1];
5962
5963 vkUpdateDescriptorSets(device: m_VKDevice, descriptorWriteCount: static_cast<uint32_t>(aDescriptorWrites.size()), pDescriptorWrites: aDescriptorWrites.data(), descriptorCopyCount: 0, pDescriptorCopies: nullptr);
5964
5965 return true;
5966 }
5967
5968 void DestroyTextDescriptorSets(CTexture &Texture, CTexture &TextureOutline)
5969 {
5970 auto &DescrSet = Texture.m_VKTextDescrSet;
5971 FreeDescriptorSetFromPool(DescrSet);
5972 }
5973
5974 [[nodiscard]] bool HasMultiSampling() const
5975 {
5976 return GetSampleCount() != VK_SAMPLE_COUNT_1_BIT;
5977 }
5978
5979 VkSampleCountFlagBits GetMaxSampleCount() const
5980 {
5981 if(m_MaxMultiSample & VK_SAMPLE_COUNT_64_BIT)
5982 return VK_SAMPLE_COUNT_64_BIT;
5983 else if(m_MaxMultiSample & VK_SAMPLE_COUNT_32_BIT)
5984 return VK_SAMPLE_COUNT_32_BIT;
5985 else if(m_MaxMultiSample & VK_SAMPLE_COUNT_16_BIT)
5986 return VK_SAMPLE_COUNT_16_BIT;
5987 else if(m_MaxMultiSample & VK_SAMPLE_COUNT_8_BIT)
5988 return VK_SAMPLE_COUNT_8_BIT;
5989 else if(m_MaxMultiSample & VK_SAMPLE_COUNT_4_BIT)
5990 return VK_SAMPLE_COUNT_4_BIT;
5991 else if(m_MaxMultiSample & VK_SAMPLE_COUNT_2_BIT)
5992 return VK_SAMPLE_COUNT_2_BIT;
5993
5994 return VK_SAMPLE_COUNT_1_BIT;
5995 }
5996
5997 VkSampleCountFlagBits GetSampleCount() const
5998 {
5999 auto MaxSampleCount = GetMaxSampleCount();
6000 if(m_MultiSamplingCount >= 64 && MaxSampleCount >= VK_SAMPLE_COUNT_64_BIT)
6001 return VK_SAMPLE_COUNT_64_BIT;
6002 else if(m_MultiSamplingCount >= 32 && MaxSampleCount >= VK_SAMPLE_COUNT_32_BIT)
6003 return VK_SAMPLE_COUNT_32_BIT;
6004 else if(m_MultiSamplingCount >= 16 && MaxSampleCount >= VK_SAMPLE_COUNT_16_BIT)
6005 return VK_SAMPLE_COUNT_16_BIT;
6006 else if(m_MultiSamplingCount >= 8 && MaxSampleCount >= VK_SAMPLE_COUNT_8_BIT)
6007 return VK_SAMPLE_COUNT_8_BIT;
6008 else if(m_MultiSamplingCount >= 4 && MaxSampleCount >= VK_SAMPLE_COUNT_4_BIT)
6009 return VK_SAMPLE_COUNT_4_BIT;
6010 else if(m_MultiSamplingCount >= 2 && MaxSampleCount >= VK_SAMPLE_COUNT_2_BIT)
6011 return VK_SAMPLE_COUNT_2_BIT;
6012
6013 return VK_SAMPLE_COUNT_1_BIT;
6014 }
6015
6016 int InitVulkanSwapChain(VkSwapchainKHR &OldSwapChain)
6017 {
6018 OldSwapChain = VK_NULL_HANDLE;
6019 if(!CreateSwapChain(OldSwapChain))
6020 return -1;
6021
6022 if(!GetSwapChainImageHandles())
6023 return -1;
6024
6025 if(!CreateImageViews())
6026 return -1;
6027
6028 if(!CreateMultiSamplerImageAttachments())
6029 {
6030 return -1;
6031 }
6032
6033 m_LastPresentedSwapChainImageIndex = std::numeric_limits<decltype(m_LastPresentedSwapChainImageIndex)>::max();
6034
6035 if(!CreateRenderPass(ClearAttachs: true))
6036 return -1;
6037
6038 if(!CreateFramebuffers())
6039 return -1;
6040
6041 if(!CreateStandardGraphicsPipeline(pVertName: "shader/vulkan/prim.vert.spv", pFragName: "shader/vulkan/prim.frag.spv", HasSampler: false, IsLinePipe: false))
6042 return -1;
6043
6044 if(!CreateStandardGraphicsPipeline(pVertName: "shader/vulkan/prim_textured.vert.spv", pFragName: "shader/vulkan/prim_textured.frag.spv", HasSampler: true, IsLinePipe: false))
6045 return -1;
6046
6047 if(!CreateStandardGraphicsPipeline(pVertName: "shader/vulkan/prim.vert.spv", pFragName: "shader/vulkan/prim.frag.spv", HasSampler: false, IsLinePipe: true))
6048 return -1;
6049
6050 if(!CreateStandard3DGraphicsPipeline(pVertName: "shader/vulkan/prim3d.vert.spv", pFragName: "shader/vulkan/prim3d.frag.spv", HasSampler: false))
6051 return -1;
6052
6053 if(!CreateStandard3DGraphicsPipeline(pVertName: "shader/vulkan/prim3d_textured.vert.spv", pFragName: "shader/vulkan/prim3d_textured.frag.spv", HasSampler: true))
6054 return -1;
6055
6056 if(!CreateTextGraphicsPipeline(pVertName: "shader/vulkan/text.vert.spv", pFragName: "shader/vulkan/text.frag.spv"))
6057 return -1;
6058
6059 if(!CreateTileGraphicsPipeline<false>(pVertName: "shader/vulkan/tile.vert.spv", pFragName: "shader/vulkan/tile.frag.spv", IsBorder: false))
6060 return -1;
6061
6062 if(!CreateTileGraphicsPipeline<true>(pVertName: "shader/vulkan/tile_textured.vert.spv", pFragName: "shader/vulkan/tile_textured.frag.spv", IsBorder: false))
6063 return -1;
6064
6065 if(!CreateTileGraphicsPipeline<false>(pVertName: "shader/vulkan/tile_border.vert.spv", pFragName: "shader/vulkan/tile_border.frag.spv", IsBorder: true))
6066 return -1;
6067
6068 if(!CreateTileGraphicsPipeline<true>(pVertName: "shader/vulkan/tile_border_textured.vert.spv", pFragName: "shader/vulkan/tile_border_textured.frag.spv", IsBorder: true))
6069 return -1;
6070
6071 if(!CreatePrimExGraphicsPipeline(pVertName: "shader/vulkan/primex_rotationless.vert.spv", pFragName: "shader/vulkan/primex_rotationless.frag.spv", HasSampler: false, Rotationless: true))
6072 return -1;
6073
6074 if(!CreatePrimExGraphicsPipeline(pVertName: "shader/vulkan/primex_tex_rotationless.vert.spv", pFragName: "shader/vulkan/primex_tex_rotationless.frag.spv", HasSampler: true, Rotationless: true))
6075 return -1;
6076
6077 if(!CreatePrimExGraphicsPipeline(pVertName: "shader/vulkan/primex.vert.spv", pFragName: "shader/vulkan/primex.frag.spv", HasSampler: false, Rotationless: false))
6078 return -1;
6079
6080 if(!CreatePrimExGraphicsPipeline(pVertName: "shader/vulkan/primex_tex.vert.spv", pFragName: "shader/vulkan/primex_tex.frag.spv", HasSampler: true, Rotationless: false))
6081 return -1;
6082
6083 if(!CreateSpriteMultiGraphicsPipeline(pVertName: "shader/vulkan/spritemulti.vert.spv", pFragName: "shader/vulkan/spritemulti.frag.spv"))
6084 return -1;
6085
6086 if(!CreateSpriteMultiPushGraphicsPipeline(pVertName: "shader/vulkan/spritemulti_push.vert.spv", pFragName: "shader/vulkan/spritemulti_push.frag.spv"))
6087 return -1;
6088
6089 if(!CreateQuadGraphicsPipeline<false>(pVertName: "shader/vulkan/quad.vert.spv", pFragName: "shader/vulkan/quad.frag.spv"))
6090 return -1;
6091
6092 if(!CreateQuadGraphicsPipeline<true>(pVertName: "shader/vulkan/quad_textured.vert.spv", pFragName: "shader/vulkan/quad_textured.frag.spv"))
6093 return -1;
6094
6095 if(!CreateQuadPushGraphicsPipeline<false>(pVertName: "shader/vulkan/quad_push.vert.spv", pFragName: "shader/vulkan/quad_push.frag.spv"))
6096 return -1;
6097
6098 if(!CreateQuadPushGraphicsPipeline<true>(pVertName: "shader/vulkan/quad_push_textured.vert.spv", pFragName: "shader/vulkan/quad_push_textured.frag.spv"))
6099 return -1;
6100
6101 m_SwapchainCreated = true;
6102 return 0;
6103 }
6104
6105 template<bool IsFirstInitialization>
6106 int InitVulkan()
6107 {
6108 if(IsFirstInitialization)
6109 {
6110 if(!CreateDescriptorSetLayouts())
6111 return -1;
6112
6113 if(!CreateTextDescriptorSetLayout())
6114 return -1;
6115
6116 if(!CreateSpriteMultiUniformDescriptorSetLayout())
6117 return -1;
6118
6119 if(!CreateQuadUniformDescriptorSetLayout())
6120 return -1;
6121
6122 VkSwapchainKHR OldSwapChain = VK_NULL_HANDLE;
6123 if(InitVulkanSwapChain(OldSwapChain) != 0)
6124 return -1;
6125 }
6126
6127 if(IsFirstInitialization)
6128 {
6129 if(!CreateCommandPool())
6130 return -1;
6131 }
6132
6133 if(!CreateCommandBuffers())
6134 return -1;
6135
6136 if(!CreateSyncObjects())
6137 return -1;
6138
6139 if(IsFirstInitialization)
6140 {
6141 if(!CreateDescriptorPools())
6142 return -1;
6143
6144 if(!CreateTextureSamplers())
6145 return -1;
6146 }
6147
6148 m_vStreamedVertexBuffers.resize(new_size: m_ThreadCount);
6149 m_vStreamedUniformBuffers.resize(new_size: m_ThreadCount);
6150 for(size_t i = 0; i < m_ThreadCount; ++i)
6151 {
6152 m_vStreamedVertexBuffers[i].Init(FrameImageCount: m_SwapChainImageCount);
6153 m_vStreamedUniformBuffers[i].Init(FrameImageCount: m_SwapChainImageCount);
6154 }
6155
6156 m_vLastPipeline.resize(new_size: m_ThreadCount, VK_NULL_HANDLE);
6157
6158 m_vvFrameDelayedBufferCleanup.resize(new_size: m_SwapChainImageCount);
6159 m_vvFrameDelayedTextureCleanup.resize(new_size: m_SwapChainImageCount);
6160 m_vvFrameDelayedTextTexturesCleanup.resize(new_size: m_SwapChainImageCount);
6161 m_StagingBufferCache.Init(SwapChainImageCount: m_SwapChainImageCount);
6162 m_StagingBufferCacheImage.Init(SwapChainImageCount: m_SwapChainImageCount);
6163 m_VertexBufferCache.Init(SwapChainImageCount: m_SwapChainImageCount);
6164 for(auto &ImageBufferCache : m_ImageBufferCaches)
6165 ImageBufferCache.second.Init(SwapChainImageCount: m_SwapChainImageCount);
6166
6167 m_vImageLastFrameCheck.resize(new_size: m_SwapChainImageCount, x: 0);
6168
6169 if(IsFirstInitialization)
6170 {
6171 // check if image format supports linear blitting
6172 VkFormatProperties FormatProperties;
6173 vkGetPhysicalDeviceFormatProperties(physicalDevice: m_VKGPU, format: VK_FORMAT_R8G8B8A8_UNORM, pFormatProperties: &FormatProperties);
6174 if((FormatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT) != 0)
6175 {
6176 m_AllowsLinearBlitting = true;
6177 }
6178 if((FormatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) != 0 && (FormatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) != 0)
6179 {
6180 m_OptimalRGBAImageBlitting = true;
6181 }
6182 // check if image format supports blitting to linear tiled images
6183 if((FormatProperties.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT) != 0)
6184 {
6185 m_LinearRGBAImageBlitting = true;
6186 }
6187
6188 vkGetPhysicalDeviceFormatProperties(physicalDevice: m_VKGPU, format: m_VKSurfFormat.format, pFormatProperties: &FormatProperties);
6189 if((FormatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT) != 0)
6190 {
6191 m_OptimalSwapChainImageBlitting = true;
6192 }
6193 }
6194
6195 return 0;
6196 }
6197
6198 [[nodiscard]] bool GetMemoryCommandBuffer(VkCommandBuffer *&pMemCommandBuffer)
6199 {
6200 auto &MemCommandBuffer = m_vMemoryCommandBuffers[m_CurImageIndex];
6201 if(!m_vUsedMemoryCommandBuffer[m_CurImageIndex])
6202 {
6203 m_vUsedMemoryCommandBuffer[m_CurImageIndex] = true;
6204
6205 vkResetCommandBuffer(commandBuffer: MemCommandBuffer, flags: VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
6206
6207 VkCommandBufferBeginInfo BeginInfo{};
6208 BeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
6209 BeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
6210 if(vkBeginCommandBuffer(commandBuffer: MemCommandBuffer, pBeginInfo: &BeginInfo) != VK_SUCCESS)
6211 {
6212 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_RENDER_RECORDING, pErr: "Command buffer cannot be filled anymore.");
6213 return false;
6214 }
6215 }
6216 pMemCommandBuffer = &MemCommandBuffer;
6217 return true;
6218 }
6219
6220 [[nodiscard]] bool GetGraphicCommandBuffer(VkCommandBuffer *&pDrawCommandBuffer, size_t RenderThreadIndex)
6221 {
6222 if(m_ThreadCount < 2)
6223 {
6224 pDrawCommandBuffer = &m_vMainDrawCommandBuffers[m_CurImageIndex];
6225 return true;
6226 }
6227 else
6228 {
6229 auto &DrawCommandBuffer = m_vvThreadDrawCommandBuffers[RenderThreadIndex][m_CurImageIndex];
6230 if(!m_vvUsedThreadDrawCommandBuffer[RenderThreadIndex][m_CurImageIndex])
6231 {
6232 m_vvUsedThreadDrawCommandBuffer[RenderThreadIndex][m_CurImageIndex] = true;
6233
6234 vkResetCommandBuffer(commandBuffer: DrawCommandBuffer, flags: VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
6235
6236 VkCommandBufferBeginInfo BeginInfo{};
6237 BeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
6238 BeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT;
6239
6240 VkCommandBufferInheritanceInfo InheretInfo{};
6241 InheretInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
6242 InheretInfo.framebuffer = m_vFramebufferList[m_CurImageIndex];
6243 InheretInfo.occlusionQueryEnable = VK_FALSE;
6244 InheretInfo.renderPass = m_VKRenderPass;
6245 InheretInfo.subpass = 0;
6246
6247 BeginInfo.pInheritanceInfo = &InheretInfo;
6248
6249 if(vkBeginCommandBuffer(commandBuffer: DrawCommandBuffer, pBeginInfo: &BeginInfo) != VK_SUCCESS)
6250 {
6251 SetError(ErrType: EGfxErrorType::GFX_ERROR_TYPE_RENDER_RECORDING, pErr: "Thread draw command buffer cannot be filled anymore.");
6252 return false;
6253 }
6254 }
6255 pDrawCommandBuffer = &DrawCommandBuffer;
6256 return true;
6257 }
6258 }
6259
6260 VkCommandBuffer &GetMainGraphicCommandBuffer()
6261 {
6262 return m_vMainDrawCommandBuffers[m_CurImageIndex];
6263 }
6264
6265 /************************
6266 * STREAM BUFFERS SETUP
6267 ************************/
6268
6269 typedef std::function<bool(SFrameBuffers &, VkBuffer, VkDeviceSize)> TNewMemFunc;
6270
6271 // returns true, if the stream memory was just allocated
6272 template<typename TStreamMemName, typename TInstanceTypeName, size_t InstanceTypeCount, size_t BufferCreateCount, bool UsesCurrentCountOffset>
6273 [[nodiscard]] bool CreateStreamBuffer(TStreamMemName *&pBufferMem, TNewMemFunc &&NewMemFunc, SStreamMemory<TStreamMemName> &StreamUniformBuffer, VkBufferUsageFlagBits Usage, VkBuffer &NewBuffer, SDeviceMemoryBlock &NewBufferMem, size_t &BufferOffset, const void *pData, size_t DataSize)
6274 {
6275 VkBuffer Buffer = VK_NULL_HANDLE;
6276 SDeviceMemoryBlock BufferMem;
6277 size_t Offset = 0;
6278
6279 uint8_t *pMem = nullptr;
6280
6281 size_t it = 0;
6282 if(UsesCurrentCountOffset)
6283 it = StreamUniformBuffer.GetUsedCount(m_CurImageIndex);
6284 for(; it < StreamUniformBuffer.GetBuffers(m_CurImageIndex).size(); ++it)
6285 {
6286 auto &BufferOfFrame = StreamUniformBuffer.GetBuffers(m_CurImageIndex)[it];
6287 if(BufferOfFrame.m_Size >= DataSize + BufferOfFrame.m_UsedSize)
6288 {
6289 if(BufferOfFrame.m_UsedSize == 0)
6290 StreamUniformBuffer.IncreaseUsedCount(m_CurImageIndex);
6291 Buffer = BufferOfFrame.m_Buffer;
6292 BufferMem = BufferOfFrame.m_BufferMem;
6293 Offset = BufferOfFrame.m_UsedSize;
6294 BufferOfFrame.m_UsedSize += DataSize;
6295 pMem = BufferOfFrame.m_pMappedBufferData;
6296 pBufferMem = &BufferOfFrame;
6297 break;
6298 }
6299 }
6300
6301 if(BufferMem.m_Mem == VK_NULL_HANDLE)
6302 {
6303 // create memory
6304 VkBuffer StreamBuffer;
6305 SDeviceMemoryBlock StreamBufferMemory;
6306 const VkDeviceSize NewBufferSingleSize = sizeof(TInstanceTypeName) * InstanceTypeCount;
6307 const VkDeviceSize NewBufferSize = NewBufferSingleSize * BufferCreateCount;
6308 if(!CreateBuffer(BufferSize: NewBufferSize, MemUsage: MEMORY_BLOCK_USAGE_STREAM, BufferUsage: Usage, MemoryProperties: VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, VKBuffer&: StreamBuffer, VKBufferMemory&: StreamBufferMemory))
6309 return false;
6310
6311 void *pMappedData = nullptr;
6312 vkMapMemory(device: m_VKDevice, memory: StreamBufferMemory.m_Mem, offset: 0, VK_WHOLE_SIZE, flags: 0, ppData: &pMappedData);
6313
6314 size_t NewBufferIndex = StreamUniformBuffer.GetBuffers(m_CurImageIndex).size();
6315 for(size_t i = 0; i < BufferCreateCount; ++i)
6316 {
6317 StreamUniformBuffer.GetBuffers(m_CurImageIndex).push_back(TStreamMemName(StreamBuffer, StreamBufferMemory, NewBufferSingleSize * i, NewBufferSingleSize, 0, ((uint8_t *)pMappedData) + (NewBufferSingleSize * i)));
6318 StreamUniformBuffer.GetRanges(m_CurImageIndex).push_back({});
6319 if(!NewMemFunc(StreamUniformBuffer.GetBuffers(m_CurImageIndex).back(), StreamBuffer, NewBufferSingleSize * i))
6320 return false;
6321 }
6322 auto &NewStreamBuffer = StreamUniformBuffer.GetBuffers(m_CurImageIndex)[NewBufferIndex];
6323
6324 Buffer = StreamBuffer;
6325 BufferMem = StreamBufferMemory;
6326
6327 pBufferMem = &NewStreamBuffer;
6328 pMem = NewStreamBuffer.m_pMappedBufferData;
6329 Offset = NewStreamBuffer.m_OffsetInBuffer;
6330 NewStreamBuffer.m_UsedSize += DataSize;
6331
6332 StreamUniformBuffer.IncreaseUsedCount(m_CurImageIndex);
6333 }
6334
6335 {
6336 mem_copy(dest: pMem + Offset, source: pData, size: (size_t)DataSize);
6337 }
6338
6339 NewBuffer = Buffer;
6340 NewBufferMem = BufferMem;
6341 BufferOffset = Offset;
6342
6343 return true;
6344 }
6345
6346 [[nodiscard]] bool CreateStreamVertexBuffer(size_t RenderThreadIndex, VkBuffer &NewBuffer, SDeviceMemoryBlock &NewBufferMem, size_t &BufferOffset, const void *pData, size_t DataSize)
6347 {
6348 SFrameBuffers *pStreamBuffer;
6349 return CreateStreamBuffer<SFrameBuffers, GL_SVertexTex3DStream, CCommandBuffer::MAX_VERTICES * 2, 1, false>(
6350 pBufferMem&: pStreamBuffer, NewMemFunc: [](SFrameBuffers &, VkBuffer, VkDeviceSize) { return true; }, StreamUniformBuffer&: m_vStreamedVertexBuffers[RenderThreadIndex], Usage: VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, NewBuffer, NewBufferMem, BufferOffset, pData, DataSize);
6351 }
6352
6353 template<typename TName, size_t InstanceMaxParticleCount, size_t MaxInstances>
6354 [[nodiscard]] bool GetUniformBufferObjectImpl(size_t RenderThreadIndex, bool RequiresSharedStagesDescriptor, SStreamMemory<SFrameUniformBuffers> &StreamUniformBuffer, SDeviceDescriptorSet &DescrSet, const void *pData, size_t DataSize)
6355 {
6356 VkBuffer NewBuffer;
6357 SDeviceMemoryBlock NewBufferMem;
6358 size_t BufferOffset;
6359 SFrameUniformBuffers *pMem;
6360 if(!CreateStreamBuffer<SFrameUniformBuffers, TName, InstanceMaxParticleCount, MaxInstances, true>(
6361 pMem,
6362 [this, RenderThreadIndex](SFrameBuffers &Mem, VkBuffer Buffer, VkDeviceSize MemOffset) {
6363 if(!CreateUniformDescriptorSets(RenderThreadIndex, SetLayout&: m_SpriteMultiUniformDescriptorSetLayout, pSets: ((SFrameUniformBuffers *)(&Mem))->m_aUniformSets.data(), SetCount: 1, BindBuffer: Buffer, SingleBufferInstanceSize: InstanceMaxParticleCount * sizeof(TName), MemoryOffset: MemOffset))
6364 return false;
6365 if(!CreateUniformDescriptorSets(RenderThreadIndex, SetLayout&: m_QuadUniformDescriptorSetLayout, pSets: &((SFrameUniformBuffers *)(&Mem))->m_aUniformSets[1], SetCount: 1, BindBuffer: Buffer, SingleBufferInstanceSize: InstanceMaxParticleCount * sizeof(TName), MemoryOffset: MemOffset))
6366 return false;
6367 return true;
6368 },
6369 StreamUniformBuffer, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, NewBuffer, NewBufferMem, BufferOffset, pData, DataSize))
6370 return false;
6371
6372 DescrSet = pMem->m_aUniformSets[RequiresSharedStagesDescriptor ? 1 : 0];
6373 return true;
6374 }
6375
6376 [[nodiscard]] bool GetUniformBufferObject(size_t RenderThreadIndex, bool RequiresSharedStagesDescriptor, SDeviceDescriptorSet &DescrSet, size_t ParticleCount, const void *pData, size_t DataSize)
6377 {
6378 return GetUniformBufferObjectImpl<IGraphics::SRenderSpriteInfo, 512, 128>(RenderThreadIndex, RequiresSharedStagesDescriptor, StreamUniformBuffer&: m_vStreamedUniformBuffers[RenderThreadIndex], DescrSet, pData, DataSize);
6379 }
6380
6381 [[nodiscard]] bool CreateIndexBuffer(void *pData, size_t DataSize, VkBuffer &Buffer, SDeviceMemoryBlock &Memory)
6382 {
6383 VkDeviceSize BufferDataSize = DataSize;
6384
6385 SMemoryBlock<s_StagingBufferCacheId> StagingBuffer;
6386 if(!GetStagingBuffer(ResBlock&: StagingBuffer, pBufferData: pData, RequiredSize: DataSize))
6387 return false;
6388
6389 SDeviceMemoryBlock VertexBufferMemory;
6390 VkBuffer VertexBuffer;
6391 if(!CreateBuffer(BufferSize: BufferDataSize, MemUsage: MEMORY_BLOCK_USAGE_BUFFER, BufferUsage: VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, MemoryProperties: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, VKBuffer&: VertexBuffer, VKBufferMemory&: VertexBufferMemory))
6392 return false;
6393
6394 if(!MemoryBarrier(Buffer: VertexBuffer, Offset: 0, Size: BufferDataSize, BufferAccessType: VK_ACCESS_INDEX_READ_BIT, BeforeCommand: true))
6395 return false;
6396 if(!CopyBuffer(SrcBuffer: StagingBuffer.m_Buffer, DstBuffer: VertexBuffer, SrcOffset: StagingBuffer.m_HeapData.m_OffsetToAlign, DstOffset: 0, CopySize: BufferDataSize))
6397 return false;
6398 if(!MemoryBarrier(Buffer: VertexBuffer, Offset: 0, Size: BufferDataSize, BufferAccessType: VK_ACCESS_INDEX_READ_BIT, BeforeCommand: false))
6399 return false;
6400
6401 UploadAndFreeStagingMemBlock(Block&: StagingBuffer);
6402
6403 Buffer = VertexBuffer;
6404 Memory = VertexBufferMemory;
6405 return true;
6406 }
6407
6408 void DestroyIndexBuffer(VkBuffer &Buffer, SDeviceMemoryBlock &Memory)
6409 {
6410 CleanBufferPair(ImageIndex: 0, Buffer, BufferMem&: Memory);
6411 }
6412
6413 /************************
6414 * COMMAND IMPLEMENTATION
6415 ************************/
6416 template<typename TName>
6417 [[nodiscard]] static bool IsInCommandRange(TName CMD, TName Min, TName Max)
6418 {
6419 return CMD >= Min && CMD < Max;
6420 }
6421
6422 [[nodiscard]] ERunCommandReturnTypes RunCommand(const CCommandBuffer::SCommand *pBaseCommand) override
6423 {
6424 if(m_HasError)
6425 {
6426 // ignore all further commands
6427 return ERunCommandReturnTypes::RUN_COMMAND_COMMAND_ERROR;
6428 }
6429
6430 if(IsInCommandRange<decltype(pBaseCommand->m_Cmd)>(CMD: pBaseCommand->m_Cmd, Min: CCommandBuffer::CMD_FIRST, Max: CCommandBuffer::CMD_COUNT))
6431 {
6432 auto &CallbackObj = m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: CCommandBuffer::ECommandBufferCMD(pBaseCommand->m_Cmd))];
6433 SRenderCommandExecuteBuffer Buffer;
6434 Buffer.m_Command = (CCommandBuffer::ECommandBufferCMD)pBaseCommand->m_Cmd;
6435 Buffer.m_pRawCommand = pBaseCommand;
6436 Buffer.m_ThreadIndex = 0;
6437
6438 if(m_CurCommandInPipe + 1 == m_CommandsInPipe)
6439 {
6440 m_LastCommandsInPipeThreadIndex = std::numeric_limits<decltype(m_LastCommandsInPipeThreadIndex)>::max();
6441 }
6442
6443 bool CanStartThread = false;
6444 if(CallbackObj.m_IsRenderCommand)
6445 {
6446 bool ForceSingleThread = m_LastCommandsInPipeThreadIndex == std::numeric_limits<decltype(m_LastCommandsInPipeThreadIndex)>::max();
6447
6448 size_t PotentiallyNextThread = (((m_CurCommandInPipe * (m_ThreadCount - 1)) / m_CommandsInPipe) + 1);
6449 if(PotentiallyNextThread - 1 > m_LastCommandsInPipeThreadIndex)
6450 {
6451 CanStartThread = true;
6452 m_LastCommandsInPipeThreadIndex = PotentiallyNextThread - 1;
6453 }
6454 Buffer.m_ThreadIndex = m_ThreadCount > 1 && !ForceSingleThread ? (m_LastCommandsInPipeThreadIndex + 1) : 0;
6455 CallbackObj.m_FillExecuteBuffer(Buffer, pBaseCommand);
6456 m_CurRenderCallCountInPipe += Buffer.m_EstimatedRenderCallCount;
6457 }
6458 bool Ret = true;
6459 if(!CallbackObj.m_IsRenderCommand || (Buffer.m_ThreadIndex == 0 && !m_RenderingPaused))
6460 {
6461 Ret = CallbackObj.m_CMDIsHandled;
6462 if(!CallbackObj.m_CommandCB(pBaseCommand, Buffer))
6463 {
6464 // an error occurred, stop this command and ignore all further commands
6465 return ERunCommandReturnTypes::RUN_COMMAND_COMMAND_ERROR;
6466 }
6467 }
6468 else if(!m_RenderingPaused)
6469 {
6470 if(CanStartThread)
6471 {
6472 StartRenderThread(ThreadIndex: m_LastCommandsInPipeThreadIndex - 1);
6473 }
6474 m_vvThreadCommandLists[Buffer.m_ThreadIndex - 1].push_back(x: Buffer);
6475 }
6476
6477 ++m_CurCommandInPipe;
6478 return Ret ? ERunCommandReturnTypes::RUN_COMMAND_COMMAND_HANDLED : ERunCommandReturnTypes::RUN_COMMAND_COMMAND_UNHANDLED;
6479 }
6480
6481 if(m_CurCommandInPipe + 1 == m_CommandsInPipe)
6482 {
6483 m_LastCommandsInPipeThreadIndex = std::numeric_limits<decltype(m_LastCommandsInPipeThreadIndex)>::max();
6484 }
6485 ++m_CurCommandInPipe;
6486
6487 switch(pBaseCommand->m_Cmd)
6488 {
6489 case CCommandProcessorFragment_GLBase::CMD_INIT:
6490 if(!Cmd_Init(pCommand: static_cast<const SCommand_Init *>(pBaseCommand)))
6491 {
6492 SetWarningPreMsg("Could not initialize Vulkan: ");
6493 return RUN_COMMAND_COMMAND_WARNING;
6494 }
6495 break;
6496 case CCommandProcessorFragment_GLBase::CMD_SHUTDOWN:
6497 if(!Cmd_Shutdown(pCommand: static_cast<const SCommand_Shutdown *>(pBaseCommand)))
6498 {
6499 SetWarningPreMsg("Could not shutdown Vulkan: ");
6500 return RUN_COMMAND_COMMAND_WARNING;
6501 }
6502 break;
6503
6504 case CCommandProcessorFragment_GLBase::CMD_PRE_INIT:
6505 if(!Cmd_PreInit(pCommand: static_cast<const CCommandProcessorFragment_GLBase::SCommand_PreInit *>(pBaseCommand)))
6506 {
6507 SetWarningPreMsg("Could not initialize Vulkan: ");
6508 return RUN_COMMAND_COMMAND_WARNING;
6509 }
6510 break;
6511 case CCommandProcessorFragment_GLBase::CMD_POST_SHUTDOWN:
6512 if(!Cmd_PostShutdown(pCommand: static_cast<const CCommandProcessorFragment_GLBase::SCommand_PostShutdown *>(pBaseCommand)))
6513 {
6514 SetWarningPreMsg("Could not shutdown Vulkan: ");
6515 return RUN_COMMAND_COMMAND_WARNING;
6516 }
6517 break;
6518 default:
6519 return ERunCommandReturnTypes::RUN_COMMAND_COMMAND_UNHANDLED;
6520 }
6521
6522 return ERunCommandReturnTypes::RUN_COMMAND_COMMAND_HANDLED;
6523 }
6524
6525 [[nodiscard]] bool Cmd_Init(const SCommand_Init *pCommand)
6526 {
6527 pCommand->m_pCapabilities->m_TileBuffering = true;
6528 pCommand->m_pCapabilities->m_QuadBuffering = true;
6529 pCommand->m_pCapabilities->m_TextBuffering = true;
6530 pCommand->m_pCapabilities->m_QuadContainerBuffering = true;
6531 pCommand->m_pCapabilities->m_ShaderSupport = true;
6532
6533 pCommand->m_pCapabilities->m_MipMapping = true;
6534 pCommand->m_pCapabilities->m_3DTextures = false;
6535 pCommand->m_pCapabilities->m_2DArrayTextures = true;
6536 pCommand->m_pCapabilities->m_NPOTTextures = true;
6537
6538 pCommand->m_pCapabilities->m_ContextMajor = 1;
6539 pCommand->m_pCapabilities->m_ContextMinor = 1;
6540 pCommand->m_pCapabilities->m_ContextPatch = 0;
6541
6542 pCommand->m_pCapabilities->m_TrianglesAsQuads = true;
6543
6544 m_GlobalTextureLodBIAS = g_Config.m_GfxGLTextureLODBIAS;
6545 m_pTextureMemoryUsage = pCommand->m_pTextureMemoryUsage;
6546 m_pBufferMemoryUsage = pCommand->m_pBufferMemoryUsage;
6547 m_pStreamMemoryUsage = pCommand->m_pStreamMemoryUsage;
6548 m_pStagingMemoryUsage = pCommand->m_pStagingMemoryUsage;
6549
6550 m_MultiSamplingCount = (g_Config.m_GfxFsaaSamples & 0xFFFFFFFE); // ignore the uneven bit, only even multi sampling works
6551
6552 *pCommand->m_pReadPresentedImageDataFunc = [this](uint32_t &Width, uint32_t &Height, CImageInfo::EImageFormat &Format, std::vector<uint8_t> &vDstData) {
6553 return GetPresentedImageData(Width, Height, Format, vDstData);
6554 };
6555
6556 m_pWindow = pCommand->m_pWindow;
6557
6558 *pCommand->m_pInitError = m_VKInstance != VK_NULL_HANDLE ? 0 : -1;
6559
6560 if(m_VKInstance == VK_NULL_HANDLE)
6561 {
6562 *pCommand->m_pInitError = -2;
6563 return false;
6564 }
6565
6566 m_pStorage = pCommand->m_pStorage;
6567 if(InitVulkan<true>() != 0)
6568 {
6569 *pCommand->m_pInitError = -2;
6570 return false;
6571 }
6572
6573 std::array<uint32_t, (size_t)CCommandBuffer::MAX_VERTICES / 4 * 6> aIndices;
6574 int Primq = 0;
6575 for(int i = 0; i < CCommandBuffer::MAX_VERTICES / 4 * 6; i += 6)
6576 {
6577 aIndices[i] = Primq;
6578 aIndices[i + 1] = Primq + 1;
6579 aIndices[i + 2] = Primq + 2;
6580 aIndices[i + 3] = Primq;
6581 aIndices[i + 4] = Primq + 2;
6582 aIndices[i + 5] = Primq + 3;
6583 Primq += 4;
6584 }
6585
6586 if(!PrepareFrame())
6587 return false;
6588 if(m_HasError)
6589 {
6590 *pCommand->m_pInitError = -2;
6591 return false;
6592 }
6593
6594 if(!CreateIndexBuffer(pData: aIndices.data(), DataSize: sizeof(uint32_t) * aIndices.size(), Buffer&: m_IndexBuffer, Memory&: m_IndexBufferMemory))
6595 {
6596 *pCommand->m_pInitError = -2;
6597 return false;
6598 }
6599 if(!CreateIndexBuffer(pData: aIndices.data(), DataSize: sizeof(uint32_t) * aIndices.size(), Buffer&: m_RenderIndexBuffer, Memory&: m_RenderIndexBufferMemory))
6600 {
6601 *pCommand->m_pInitError = -2;
6602 return false;
6603 }
6604 m_CurRenderIndexPrimitiveCount = CCommandBuffer::MAX_VERTICES / 4;
6605
6606 m_CanAssert = true;
6607
6608 return true;
6609 }
6610
6611 [[nodiscard]] bool Cmd_Shutdown(const SCommand_Shutdown *pCommand)
6612 {
6613 vkDeviceWaitIdle(device: m_VKDevice);
6614
6615 DestroyIndexBuffer(Buffer&: m_IndexBuffer, Memory&: m_IndexBufferMemory);
6616 DestroyIndexBuffer(Buffer&: m_RenderIndexBuffer, Memory&: m_RenderIndexBufferMemory);
6617
6618 CleanupVulkan<true>(SwapchainCount: m_SwapChainImageCount);
6619
6620 return true;
6621 }
6622
6623 [[nodiscard]] bool Cmd_Texture_Update(const CCommandBuffer::SCommand_Texture_Update *pCommand)
6624 {
6625 size_t IndexTex = pCommand->m_Slot;
6626 uint8_t *pData = pCommand->m_pData;
6627
6628 if(!UpdateTexture(TextureSlot: IndexTex, Format: VK_FORMAT_B8G8R8A8_UNORM, pData, XOff: pCommand->m_X, YOff: pCommand->m_Y, Width: pCommand->m_Width, Height: pCommand->m_Height))
6629 return false;
6630
6631 free(ptr: pData);
6632
6633 return true;
6634 }
6635
6636 [[nodiscard]] bool Cmd_Texture_Destroy(const CCommandBuffer::SCommand_Texture_Destroy *pCommand)
6637 {
6638 size_t ImageIndex = (size_t)pCommand->m_Slot;
6639 auto &Texture = m_vTextures[ImageIndex];
6640
6641 m_vvFrameDelayedTextureCleanup[m_CurImageIndex].push_back(x: Texture);
6642
6643 Texture = CTexture{};
6644
6645 return true;
6646 }
6647
6648 [[nodiscard]] bool Cmd_Texture_Create(const CCommandBuffer::SCommand_Texture_Create *pCommand)
6649 {
6650 int Slot = pCommand->m_Slot;
6651 int Width = pCommand->m_Width;
6652 int Height = pCommand->m_Height;
6653 int Flags = pCommand->m_Flags;
6654 uint8_t *pData = pCommand->m_pData;
6655
6656 if(!CreateTextureCMD(Slot, Width, Height, Format: VK_FORMAT_R8G8B8A8_UNORM, StoreFormat: VK_FORMAT_R8G8B8A8_UNORM, Flags, pData))
6657 return false;
6658
6659 free(ptr: pData);
6660
6661 return true;
6662 }
6663
6664 [[nodiscard]] bool Cmd_TextTextures_Create(const CCommandBuffer::SCommand_TextTextures_Create *pCommand)
6665 {
6666 int Slot = pCommand->m_Slot;
6667 int SlotOutline = pCommand->m_SlotOutline;
6668 int Width = pCommand->m_Width;
6669 int Height = pCommand->m_Height;
6670
6671 uint8_t *pTmpData = pCommand->m_pTextData;
6672 uint8_t *pTmpData2 = pCommand->m_pTextOutlineData;
6673
6674 if(!CreateTextureCMD(Slot, Width, Height, Format: VK_FORMAT_R8_UNORM, StoreFormat: VK_FORMAT_R8_UNORM, Flags: CCommandBuffer::TEXFLAG_NOMIPMAPS, pData&: pTmpData))
6675 return false;
6676 if(!CreateTextureCMD(Slot: SlotOutline, Width, Height, Format: VK_FORMAT_R8_UNORM, StoreFormat: VK_FORMAT_R8_UNORM, Flags: CCommandBuffer::TEXFLAG_NOMIPMAPS, pData&: pTmpData2))
6677 return false;
6678
6679 if(!CreateNewTextDescriptorSets(Texture: Slot, TextureOutline: SlotOutline))
6680 return false;
6681
6682 free(ptr: pTmpData);
6683 free(ptr: pTmpData2);
6684
6685 return true;
6686 }
6687
6688 [[nodiscard]] bool Cmd_TextTextures_Destroy(const CCommandBuffer::SCommand_TextTextures_Destroy *pCommand)
6689 {
6690 size_t ImageIndex = (size_t)pCommand->m_Slot;
6691 size_t ImageIndexOutline = (size_t)pCommand->m_SlotOutline;
6692 auto &Texture = m_vTextures[ImageIndex];
6693 auto &TextureOutline = m_vTextures[ImageIndexOutline];
6694
6695 m_vvFrameDelayedTextTexturesCleanup[m_CurImageIndex].emplace_back(args&: Texture, args&: TextureOutline);
6696
6697 Texture = {};
6698 TextureOutline = {};
6699
6700 return true;
6701 }
6702
6703 [[nodiscard]] bool Cmd_TextTexture_Update(const CCommandBuffer::SCommand_TextTexture_Update *pCommand)
6704 {
6705 size_t IndexTex = pCommand->m_Slot;
6706 uint8_t *pData = pCommand->m_pData;
6707
6708 if(!UpdateTexture(TextureSlot: IndexTex, Format: VK_FORMAT_R8_UNORM, pData, XOff: pCommand->m_X, YOff: pCommand->m_Y, Width: pCommand->m_Width, Height: pCommand->m_Height))
6709 return false;
6710
6711 free(ptr: pData);
6712
6713 return true;
6714 }
6715
6716 void Cmd_Clear_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_Clear *pCommand)
6717 {
6718 if(!pCommand->m_ForceClear)
6719 {
6720 bool ColorChanged = m_aClearColor[0] != pCommand->m_Color.r || m_aClearColor[1] != pCommand->m_Color.g ||
6721 m_aClearColor[2] != pCommand->m_Color.b || m_aClearColor[3] != pCommand->m_Color.a;
6722 m_aClearColor[0] = pCommand->m_Color.r;
6723 m_aClearColor[1] = pCommand->m_Color.g;
6724 m_aClearColor[2] = pCommand->m_Color.b;
6725 m_aClearColor[3] = pCommand->m_Color.a;
6726 if(ColorChanged)
6727 ExecBuffer.m_ClearColorInRenderThread = true;
6728 }
6729 else
6730 {
6731 ExecBuffer.m_ClearColorInRenderThread = true;
6732 }
6733 ExecBuffer.m_EstimatedRenderCallCount = 0;
6734 }
6735
6736 [[nodiscard]] bool Cmd_Clear(const SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_Clear *pCommand)
6737 {
6738 if(ExecBuffer.m_ClearColorInRenderThread)
6739 {
6740 std::array<VkClearAttachment, 1> aAttachments = {VkClearAttachment{.aspectMask: VK_IMAGE_ASPECT_COLOR_BIT, .colorAttachment: 0, .clearValue: VkClearValue{.color: VkClearColorValue{.float32: {pCommand->m_Color.r, pCommand->m_Color.g, pCommand->m_Color.b, pCommand->m_Color.a}}}}};
6741 std::array<VkClearRect, 1> aClearRects = {VkClearRect{.rect: {.offset: {.x: 0, .y: 0}, .extent: m_VKSwapImgAndViewportExtent.m_SwapImageViewport}, .baseArrayLayer: 0, .layerCount: 1}};
6742
6743 VkCommandBuffer *pCommandBuffer;
6744 if(!GetGraphicCommandBuffer(pDrawCommandBuffer&: pCommandBuffer, RenderThreadIndex: ExecBuffer.m_ThreadIndex))
6745 return false;
6746 auto &CommandBuffer = *pCommandBuffer;
6747 vkCmdClearAttachments(commandBuffer: CommandBuffer, attachmentCount: aAttachments.size(), pAttachments: aAttachments.data(), rectCount: aClearRects.size(), pRects: aClearRects.data());
6748 }
6749
6750 return true;
6751 }
6752
6753 void Cmd_Render_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_Render *pCommand)
6754 {
6755 bool IsTextured = GetIsTextured(State: pCommand->m_State);
6756 if(IsTextured)
6757 {
6758 size_t AddressModeIndex = GetAddressModeIndex(State: pCommand->m_State);
6759 ExecBuffer.m_aDescriptors[0] = m_vTextures[pCommand->m_State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex];
6760 }
6761
6762 ExecBuffer.m_IndexBuffer = m_IndexBuffer;
6763
6764 ExecBuffer.m_EstimatedRenderCallCount = 1;
6765
6766 ExecBufferFillDynamicStates(State: pCommand->m_State, ExecBuffer);
6767 }
6768
6769 [[nodiscard]] bool Cmd_Render(const CCommandBuffer::SCommand_Render *pCommand, SRenderCommandExecuteBuffer &ExecBuffer)
6770 {
6771 return RenderStandard<CCommandBuffer::SVertex, false>(ExecBuffer, State: pCommand->m_State, PrimType: pCommand->m_PrimType, pVertices: pCommand->m_pVertices, PrimitiveCount: pCommand->m_PrimCount);
6772 }
6773
6774 [[nodiscard]] bool Cmd_ReadPixel(const CCommandBuffer::SCommand_TrySwapAndReadPixel *pCommand)
6775 {
6776 if(!*pCommand->m_pSwapped && !NextFrame())
6777 return false;
6778 *pCommand->m_pSwapped = true;
6779
6780 uint32_t Width;
6781 uint32_t Height;
6782 CImageInfo::EImageFormat Format;
6783 if(GetPresentedImageDataImpl(Width, Height, Format, vDstData&: m_vReadPixelHelper, ResetAlpha: false, PixelOffset: pCommand->m_Position))
6784 {
6785 *pCommand->m_pColor = ColorRGBA(m_vReadPixelHelper[0] / 255.0f, m_vReadPixelHelper[1] / 255.0f, m_vReadPixelHelper[2] / 255.0f, 1.0f);
6786 }
6787 else
6788 {
6789 *pCommand->m_pColor = ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);
6790 }
6791
6792 return true;
6793 }
6794
6795 [[nodiscard]] bool Cmd_Screenshot(const CCommandBuffer::SCommand_TrySwapAndScreenshot *pCommand)
6796 {
6797 if(!*pCommand->m_pSwapped && !NextFrame())
6798 return false;
6799 *pCommand->m_pSwapped = true;
6800
6801 uint32_t Width;
6802 uint32_t Height;
6803 CImageInfo::EImageFormat Format;
6804 if(GetPresentedImageDataImpl(Width, Height, Format, vDstData&: m_vScreenshotHelper, ResetAlpha: true, PixelOffset: {}))
6805 {
6806 const size_t ImgSize = (size_t)Width * (size_t)Height * CImageInfo::PixelSize(Format);
6807 pCommand->m_pImage->m_pData = static_cast<uint8_t *>(malloc(size: ImgSize));
6808 mem_copy(dest: pCommand->m_pImage->m_pData, source: m_vScreenshotHelper.data(), size: ImgSize);
6809 }
6810 else
6811 {
6812 pCommand->m_pImage->m_pData = nullptr;
6813 }
6814 pCommand->m_pImage->m_Width = (int)Width;
6815 pCommand->m_pImage->m_Height = (int)Height;
6816 pCommand->m_pImage->m_Format = Format;
6817
6818 return true;
6819 }
6820
6821 void Cmd_RenderTex3D_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_RenderTex3D *pCommand)
6822 {
6823 bool IsTextured = GetIsTextured(State: pCommand->m_State);
6824 if(IsTextured)
6825 {
6826 ExecBuffer.m_aDescriptors[0] = m_vTextures[pCommand->m_State.m_Texture].m_VKStandard3DTexturedDescrSet;
6827 }
6828
6829 ExecBuffer.m_IndexBuffer = m_IndexBuffer;
6830
6831 ExecBuffer.m_EstimatedRenderCallCount = 1;
6832
6833 ExecBufferFillDynamicStates(State: pCommand->m_State, ExecBuffer);
6834 }
6835
6836 [[nodiscard]] bool Cmd_RenderTex3D(const CCommandBuffer::SCommand_RenderTex3D *pCommand, SRenderCommandExecuteBuffer &ExecBuffer)
6837 {
6838 return RenderStandard<CCommandBuffer::SVertexTex3DStream, true>(ExecBuffer, State: pCommand->m_State, PrimType: pCommand->m_PrimType, pVertices: pCommand->m_pVertices, PrimitiveCount: pCommand->m_PrimCount);
6839 }
6840
6841 void Cmd_Update_Viewport_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_Update_Viewport *pCommand)
6842 {
6843 ExecBuffer.m_EstimatedRenderCallCount = 0;
6844 }
6845
6846 [[nodiscard]] bool Cmd_Update_Viewport(const CCommandBuffer::SCommand_Update_Viewport *pCommand)
6847 {
6848 if(pCommand->m_ByResize)
6849 {
6850 if(IsVerbose())
6851 {
6852 dbg_msg(sys: "vulkan", fmt: "queueing swap chain recreation because the viewport changed");
6853 }
6854 m_CanvasWidth = (uint32_t)pCommand->m_Width;
6855 m_CanvasHeight = (uint32_t)pCommand->m_Height;
6856 m_RecreateSwapChain = true;
6857 }
6858 else
6859 {
6860 auto Viewport = m_VKSwapImgAndViewportExtent.GetPresentedImageViewport();
6861 if(pCommand->m_X != 0 || pCommand->m_Y != 0 || (uint32_t)pCommand->m_Width != Viewport.width || (uint32_t)pCommand->m_Height != Viewport.height)
6862 {
6863 m_HasDynamicViewport = true;
6864
6865 // convert viewport from OGL to vulkan
6866 int32_t ViewportY = (int32_t)Viewport.height - ((int32_t)pCommand->m_Y + (int32_t)pCommand->m_Height);
6867 uint32_t ViewportH = (int32_t)pCommand->m_Height;
6868 m_DynamicViewportOffset = {.x: (int32_t)pCommand->m_X, .y: ViewportY};
6869 m_DynamicViewportSize = {.width: (uint32_t)pCommand->m_Width, .height: ViewportH};
6870 }
6871 else
6872 {
6873 m_HasDynamicViewport = false;
6874 }
6875 }
6876
6877 return true;
6878 }
6879
6880 [[nodiscard]] bool Cmd_VSync(const CCommandBuffer::SCommand_VSync *pCommand)
6881 {
6882 if(IsVerbose())
6883 {
6884 dbg_msg(sys: "vulkan", fmt: "queueing swap chain recreation because vsync was changed");
6885 }
6886 m_RecreateSwapChain = true;
6887 *pCommand->m_pRetOk = true;
6888
6889 return true;
6890 }
6891
6892 [[nodiscard]] bool Cmd_MultiSampling(const CCommandBuffer::SCommand_MultiSampling *pCommand)
6893 {
6894 if(IsVerbose())
6895 {
6896 dbg_msg(sys: "vulkan", fmt: "queueing swap chain recreation because multi sampling was changed");
6897 }
6898 m_RecreateSwapChain = true;
6899
6900 uint32_t MSCount = (std::min(a: pCommand->m_RequestedMultiSamplingCount, b: (uint32_t)GetMaxSampleCount()) & 0xFFFFFFFE); // ignore the uneven bits
6901 m_NextMultiSamplingCount = MSCount;
6902
6903 *pCommand->m_pRetMultiSamplingCount = MSCount;
6904 *pCommand->m_pRetOk = true;
6905
6906 return true;
6907 }
6908
6909 [[nodiscard]] bool Cmd_Swap(const CCommandBuffer::SCommand_Swap *pCommand)
6910 {
6911 return NextFrame();
6912 }
6913
6914 [[nodiscard]] bool Cmd_CreateBufferObject(const CCommandBuffer::SCommand_CreateBufferObject *pCommand)
6915 {
6916 bool IsOneFrameBuffer = (pCommand->m_Flags & IGraphics::EBufferObjectCreateFlags::BUFFER_OBJECT_CREATE_FLAGS_ONE_TIME_USE_BIT) != 0;
6917 if(!CreateBufferObject(BufferIndex: (size_t)pCommand->m_BufferIndex, pUploadData: pCommand->m_pUploadData, BufferDataSize: (VkDeviceSize)pCommand->m_DataSize, IsOneFrameBuffer))
6918 return false;
6919 if(pCommand->m_DeletePointer)
6920 free(ptr: pCommand->m_pUploadData);
6921
6922 return true;
6923 }
6924
6925 [[nodiscard]] bool Cmd_UpdateBufferObject(const CCommandBuffer::SCommand_UpdateBufferObject *pCommand)
6926 {
6927 size_t BufferIndex = (size_t)pCommand->m_BufferIndex;
6928 bool DeletePointer = pCommand->m_DeletePointer;
6929 VkDeviceSize Offset = (VkDeviceSize)((intptr_t)pCommand->m_pOffset);
6930 void *pUploadData = pCommand->m_pUploadData;
6931 VkDeviceSize DataSize = (VkDeviceSize)pCommand->m_DataSize;
6932
6933 SMemoryBlock<s_StagingBufferCacheId> StagingBuffer;
6934 if(!GetStagingBuffer(ResBlock&: StagingBuffer, pBufferData: pUploadData, RequiredSize: DataSize))
6935 return false;
6936
6937 const auto &MemBlock = m_vBufferObjects[BufferIndex].m_BufferObject.m_Mem;
6938 VkBuffer VertexBuffer = MemBlock.m_Buffer;
6939 if(!MemoryBarrier(Buffer: VertexBuffer, Offset: Offset + MemBlock.m_HeapData.m_OffsetToAlign, Size: DataSize, BufferAccessType: VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, BeforeCommand: true))
6940 return false;
6941 if(!CopyBuffer(SrcBuffer: StagingBuffer.m_Buffer, DstBuffer: VertexBuffer, SrcOffset: StagingBuffer.m_HeapData.m_OffsetToAlign, DstOffset: Offset + MemBlock.m_HeapData.m_OffsetToAlign, CopySize: DataSize))
6942 return false;
6943 if(!MemoryBarrier(Buffer: VertexBuffer, Offset: Offset + MemBlock.m_HeapData.m_OffsetToAlign, Size: DataSize, BufferAccessType: VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, BeforeCommand: false))
6944 return false;
6945
6946 UploadAndFreeStagingMemBlock(Block&: StagingBuffer);
6947
6948 if(DeletePointer)
6949 free(ptr: pUploadData);
6950
6951 return true;
6952 }
6953
6954 [[nodiscard]] bool Cmd_RecreateBufferObject(const CCommandBuffer::SCommand_RecreateBufferObject *pCommand)
6955 {
6956 DeleteBufferObject(BufferIndex: (size_t)pCommand->m_BufferIndex);
6957 bool IsOneFrameBuffer = (pCommand->m_Flags & IGraphics::EBufferObjectCreateFlags::BUFFER_OBJECT_CREATE_FLAGS_ONE_TIME_USE_BIT) != 0;
6958 return CreateBufferObject(BufferIndex: (size_t)pCommand->m_BufferIndex, pUploadData: pCommand->m_pUploadData, BufferDataSize: (VkDeviceSize)pCommand->m_DataSize, IsOneFrameBuffer);
6959 }
6960
6961 [[nodiscard]] bool Cmd_CopyBufferObject(const CCommandBuffer::SCommand_CopyBufferObject *pCommand)
6962 {
6963 size_t ReadBufferIndex = (size_t)pCommand->m_ReadBufferIndex;
6964 size_t WriteBufferIndex = (size_t)pCommand->m_WriteBufferIndex;
6965 auto &ReadMemBlock = m_vBufferObjects[ReadBufferIndex].m_BufferObject.m_Mem;
6966 auto &WriteMemBlock = m_vBufferObjects[WriteBufferIndex].m_BufferObject.m_Mem;
6967 VkBuffer ReadBuffer = ReadMemBlock.m_Buffer;
6968 VkBuffer WriteBuffer = WriteMemBlock.m_Buffer;
6969
6970 VkDeviceSize DataSize = (VkDeviceSize)pCommand->m_CopySize;
6971 VkDeviceSize ReadOffset = (VkDeviceSize)pCommand->m_ReadOffset + ReadMemBlock.m_HeapData.m_OffsetToAlign;
6972 VkDeviceSize WriteOffset = (VkDeviceSize)pCommand->m_WriteOffset + WriteMemBlock.m_HeapData.m_OffsetToAlign;
6973
6974 if(!MemoryBarrier(Buffer: ReadBuffer, Offset: ReadOffset, Size: DataSize, BufferAccessType: VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, BeforeCommand: true))
6975 return false;
6976 if(!MemoryBarrier(Buffer: WriteBuffer, Offset: WriteOffset, Size: DataSize, BufferAccessType: VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, BeforeCommand: true))
6977 return false;
6978 if(!CopyBuffer(SrcBuffer: ReadBuffer, DstBuffer: WriteBuffer, SrcOffset: ReadOffset, DstOffset: WriteOffset, CopySize: DataSize))
6979 return false;
6980 if(!MemoryBarrier(Buffer: WriteBuffer, Offset: WriteOffset, Size: DataSize, BufferAccessType: VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, BeforeCommand: false))
6981 return false;
6982 if(!MemoryBarrier(Buffer: ReadBuffer, Offset: ReadOffset, Size: DataSize, BufferAccessType: VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT, BeforeCommand: false))
6983 return false;
6984
6985 return true;
6986 }
6987
6988 [[nodiscard]] bool Cmd_DeleteBufferObject(const CCommandBuffer::SCommand_DeleteBufferObject *pCommand)
6989 {
6990 size_t BufferIndex = (size_t)pCommand->m_BufferIndex;
6991 DeleteBufferObject(BufferIndex);
6992
6993 return true;
6994 }
6995
6996 [[nodiscard]] bool Cmd_CreateBufferContainer(const CCommandBuffer::SCommand_CreateBufferContainer *pCommand)
6997 {
6998 size_t ContainerIndex = (size_t)pCommand->m_BufferContainerIndex;
6999 while(ContainerIndex >= m_vBufferContainers.size())
7000 m_vBufferContainers.resize(new_size: (m_vBufferContainers.size() * 2) + 1);
7001
7002 m_vBufferContainers[ContainerIndex].m_BufferObjectIndex = pCommand->m_VertBufferBindingIndex;
7003
7004 return true;
7005 }
7006
7007 [[nodiscard]] bool Cmd_UpdateBufferContainer(const CCommandBuffer::SCommand_UpdateBufferContainer *pCommand)
7008 {
7009 size_t ContainerIndex = (size_t)pCommand->m_BufferContainerIndex;
7010 m_vBufferContainers[ContainerIndex].m_BufferObjectIndex = pCommand->m_VertBufferBindingIndex;
7011
7012 return true;
7013 }
7014
7015 [[nodiscard]] bool Cmd_DeleteBufferContainer(const CCommandBuffer::SCommand_DeleteBufferContainer *pCommand)
7016 {
7017 size_t ContainerIndex = (size_t)pCommand->m_BufferContainerIndex;
7018 bool DeleteAllBO = pCommand->m_DestroyAllBO;
7019 if(DeleteAllBO)
7020 {
7021 size_t BufferIndex = (size_t)m_vBufferContainers[ContainerIndex].m_BufferObjectIndex;
7022 DeleteBufferObject(BufferIndex);
7023 }
7024
7025 return true;
7026 }
7027
7028 [[nodiscard]] bool Cmd_IndicesRequiredNumNotify(const CCommandBuffer::SCommand_IndicesRequiredNumNotify *pCommand)
7029 {
7030 size_t IndicesCount = pCommand->m_RequiredIndicesNum;
7031 if(m_CurRenderIndexPrimitiveCount < IndicesCount / 6)
7032 {
7033 m_vvFrameDelayedBufferCleanup[m_CurImageIndex].push_back(x: {.m_Buffer: m_RenderIndexBuffer, .m_Mem: m_RenderIndexBufferMemory});
7034 std::vector<uint32_t> vIndices(IndicesCount);
7035 uint32_t Primq = 0;
7036 for(size_t i = 0; i < IndicesCount; i += 6)
7037 {
7038 vIndices[i] = Primq;
7039 vIndices[i + 1] = Primq + 1;
7040 vIndices[i + 2] = Primq + 2;
7041 vIndices[i + 3] = Primq;
7042 vIndices[i + 4] = Primq + 2;
7043 vIndices[i + 5] = Primq + 3;
7044 Primq += 4;
7045 }
7046 if(!CreateIndexBuffer(pData: vIndices.data(), DataSize: vIndices.size() * sizeof(uint32_t), Buffer&: m_RenderIndexBuffer, Memory&: m_RenderIndexBufferMemory))
7047 return false;
7048 m_CurRenderIndexPrimitiveCount = IndicesCount / 6;
7049 }
7050
7051 return true;
7052 }
7053
7054 void Cmd_RenderTileLayer_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_RenderTileLayer *pCommand)
7055 {
7056 RenderTileLayer_FillExecuteBuffer(ExecBuffer, DrawCalls: pCommand->m_IndicesDrawNum, State: pCommand->m_State, BufferContainerIndex: pCommand->m_BufferContainerIndex);
7057 }
7058
7059 [[nodiscard]] bool Cmd_RenderTileLayer(const CCommandBuffer::SCommand_RenderTileLayer *pCommand, SRenderCommandExecuteBuffer &ExecBuffer)
7060 {
7061 vec2 Scale{};
7062 vec2 Off{};
7063 return RenderTileLayer(ExecBuffer, State: pCommand->m_State, IsBorder: false, Color: pCommand->m_Color, Scale, Off, IndicesDrawNum: (size_t)pCommand->m_IndicesDrawNum, pIndicesOffsets: pCommand->m_pIndicesOffsets, pDrawCount: pCommand->m_pDrawCount);
7064 }
7065
7066 void Cmd_RenderBorderTile_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_RenderBorderTile *pCommand)
7067 {
7068 RenderTileLayer_FillExecuteBuffer(ExecBuffer, DrawCalls: 1, State: pCommand->m_State, BufferContainerIndex: pCommand->m_BufferContainerIndex);
7069 }
7070
7071 [[nodiscard]] bool Cmd_RenderBorderTile(const CCommandBuffer::SCommand_RenderBorderTile *pCommand, SRenderCommandExecuteBuffer &ExecBuffer)
7072 {
7073 vec2 Scale = pCommand->m_Scale;
7074 vec2 Off = pCommand->m_Offset;
7075 unsigned int DrawNum = pCommand->m_DrawNum * 6;
7076 return RenderTileLayer(ExecBuffer, State: pCommand->m_State, IsBorder: true, Color: pCommand->m_Color, Scale, Off, IndicesDrawNum: 1, pIndicesOffsets: &pCommand->m_pIndicesOffset, pDrawCount: &DrawNum);
7077 }
7078
7079 void Cmd_RenderQuadLayer_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_RenderQuadLayer *pCommand)
7080 {
7081 size_t BufferContainerIndex = (size_t)pCommand->m_BufferContainerIndex;
7082 size_t BufferObjectIndex = (size_t)m_vBufferContainers[BufferContainerIndex].m_BufferObjectIndex;
7083 const auto &BufferObject = m_vBufferObjects[BufferObjectIndex];
7084
7085 ExecBuffer.m_Buffer = BufferObject.m_CurBuffer;
7086 ExecBuffer.m_BufferOff = BufferObject.m_CurBufferOffset;
7087
7088 bool IsTextured = GetIsTextured(State: pCommand->m_State);
7089 if(IsTextured)
7090 {
7091 size_t AddressModeIndex = GetAddressModeIndex(State: pCommand->m_State);
7092 ExecBuffer.m_aDescriptors[0] = m_vTextures[pCommand->m_State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex];
7093 }
7094
7095 ExecBuffer.m_IndexBuffer = m_RenderIndexBuffer;
7096
7097 ExecBuffer.m_EstimatedRenderCallCount = ((pCommand->m_QuadNum - 1) / gs_GraphicsMaxQuadsRenderCount) + 1;
7098
7099 ExecBufferFillDynamicStates(State: pCommand->m_State, ExecBuffer);
7100 }
7101
7102 [[nodiscard]] bool Cmd_RenderQuadLayer(const CCommandBuffer::SCommand_RenderQuadLayer *pCommand, SRenderCommandExecuteBuffer &ExecBuffer)
7103 {
7104 std::array<float, (size_t)4 * 2> m;
7105 GetStateMatrix(State: pCommand->m_State, Matrix&: m);
7106
7107 bool CanBePushed = pCommand->m_QuadNum == 1;
7108
7109 bool IsTextured;
7110 size_t BlendModeIndex;
7111 size_t DynamicIndex;
7112 size_t AddressModeIndex;
7113 GetStateIndices(ExecBuffer, State: pCommand->m_State, IsTextured, BlendModeIndex, DynamicIndex, AddressModeIndex);
7114 auto &PipeLayout = GetPipeLayout(Container&: CanBePushed ? m_QuadPushPipeline : m_QuadPipeline, IsTextured, BlendModeIndex, DynamicIndex);
7115 auto &PipeLine = GetPipeline(Container&: CanBePushed ? m_QuadPushPipeline : m_QuadPipeline, IsTextured, BlendModeIndex, DynamicIndex);
7116
7117 VkCommandBuffer *pCommandBuffer;
7118 if(!GetGraphicCommandBuffer(pDrawCommandBuffer&: pCommandBuffer, RenderThreadIndex: ExecBuffer.m_ThreadIndex))
7119 return false;
7120 auto &CommandBuffer = *pCommandBuffer;
7121
7122 BindPipeline(RenderThreadIndex: ExecBuffer.m_ThreadIndex, CommandBuffer, ExecBuffer, BindingPipe&: PipeLine, State: pCommand->m_State);
7123
7124 std::array<VkBuffer, 1> aVertexBuffers = {ExecBuffer.m_Buffer};
7125 std::array<VkDeviceSize, 1> aOffsets = {(VkDeviceSize)ExecBuffer.m_BufferOff};
7126 vkCmdBindVertexBuffers(commandBuffer: CommandBuffer, firstBinding: 0, bindingCount: 1, pBuffers: aVertexBuffers.data(), pOffsets: aOffsets.data());
7127
7128 vkCmdBindIndexBuffer(commandBuffer: CommandBuffer, buffer: ExecBuffer.m_IndexBuffer, offset: 0, indexType: VK_INDEX_TYPE_UINT32);
7129
7130 if(IsTextured)
7131 {
7132 vkCmdBindDescriptorSets(commandBuffer: CommandBuffer, pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, layout: PipeLayout, firstSet: 0, descriptorSetCount: 1, pDescriptorSets: &ExecBuffer.m_aDescriptors[0].m_Descriptor, dynamicOffsetCount: 0, pDynamicOffsets: nullptr);
7133 }
7134
7135 if(CanBePushed)
7136 {
7137 SUniformQuadPushGPos PushConstantVertex;
7138
7139 mem_copy(dest: &PushConstantVertex.m_BOPush, source: &pCommand->m_pQuadInfo[0], size: sizeof(PushConstantVertex.m_BOPush));
7140
7141 mem_copy(dest: PushConstantVertex.m_aPos, source: m.data(), size: sizeof(PushConstantVertex.m_aPos));
7142 PushConstantVertex.m_QuadOffset = pCommand->m_QuadOffset;
7143
7144 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, offset: 0, size: sizeof(SUniformQuadPushGPos), pValues: &PushConstantVertex);
7145 }
7146 else
7147 {
7148 SUniformQuadGPos PushConstantVertex;
7149 mem_copy(dest: PushConstantVertex.m_aPos, source: m.data(), size: sizeof(PushConstantVertex.m_aPos));
7150 PushConstantVertex.m_QuadOffset = pCommand->m_QuadOffset;
7151
7152 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_VERTEX_BIT, offset: 0, size: sizeof(PushConstantVertex), pValues: &PushConstantVertex);
7153 }
7154
7155 uint32_t DrawCount = (uint32_t)pCommand->m_QuadNum;
7156 size_t RenderOffset = 0;
7157
7158 while(DrawCount > 0)
7159 {
7160 uint32_t RealDrawCount = (DrawCount > gs_GraphicsMaxQuadsRenderCount ? gs_GraphicsMaxQuadsRenderCount : DrawCount);
7161
7162 VkDeviceSize IndexOffset = (VkDeviceSize)((ptrdiff_t)(pCommand->m_QuadOffset + RenderOffset) * 6);
7163 if(!CanBePushed)
7164 {
7165 // create uniform buffer
7166 SDeviceDescriptorSet UniDescrSet;
7167 if(!GetUniformBufferObject(RenderThreadIndex: ExecBuffer.m_ThreadIndex, RequiresSharedStagesDescriptor: true, DescrSet&: UniDescrSet, ParticleCount: RealDrawCount, pData: (const float *)(pCommand->m_pQuadInfo + RenderOffset), DataSize: RealDrawCount * sizeof(SQuadRenderInfo)))
7168 return false;
7169
7170 vkCmdBindDescriptorSets(commandBuffer: CommandBuffer, pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, layout: PipeLayout, firstSet: IsTextured ? 1 : 0, descriptorSetCount: 1, pDescriptorSets: &UniDescrSet.m_Descriptor, dynamicOffsetCount: 0, pDynamicOffsets: nullptr);
7171 if(RenderOffset > 0)
7172 {
7173 int32_t QuadOffset = pCommand->m_QuadOffset + RenderOffset;
7174 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_VERTEX_BIT, offset: sizeof(SUniformQuadGPos) - sizeof(int32_t), size: sizeof(int32_t), pValues: &QuadOffset);
7175 }
7176 }
7177
7178 vkCmdDrawIndexed(commandBuffer: CommandBuffer, indexCount: static_cast<uint32_t>(RealDrawCount * 6), instanceCount: 1, firstIndex: IndexOffset, vertexOffset: 0, firstInstance: 0);
7179
7180 RenderOffset += RealDrawCount;
7181 DrawCount -= RealDrawCount;
7182 }
7183
7184 return true;
7185 }
7186
7187 void Cmd_RenderText_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_RenderText *pCommand)
7188 {
7189 size_t BufferContainerIndex = (size_t)pCommand->m_BufferContainerIndex;
7190 size_t BufferObjectIndex = (size_t)m_vBufferContainers[BufferContainerIndex].m_BufferObjectIndex;
7191 const auto &BufferObject = m_vBufferObjects[BufferObjectIndex];
7192
7193 ExecBuffer.m_Buffer = BufferObject.m_CurBuffer;
7194 ExecBuffer.m_BufferOff = BufferObject.m_CurBufferOffset;
7195
7196 ExecBuffer.m_aDescriptors[0] = m_vTextures[pCommand->m_TextTextureIndex].m_VKTextDescrSet;
7197
7198 ExecBuffer.m_IndexBuffer = m_RenderIndexBuffer;
7199
7200 ExecBuffer.m_EstimatedRenderCallCount = 1;
7201
7202 ExecBufferFillDynamicStates(State: pCommand->m_State, ExecBuffer);
7203 }
7204
7205 [[nodiscard]] bool Cmd_RenderText(const CCommandBuffer::SCommand_RenderText *pCommand, SRenderCommandExecuteBuffer &ExecBuffer)
7206 {
7207 std::array<float, (size_t)4 * 2> m;
7208 GetStateMatrix(State: pCommand->m_State, Matrix&: m);
7209
7210 bool IsTextured;
7211 size_t BlendModeIndex;
7212 size_t DynamicIndex;
7213 size_t AddressModeIndex;
7214 GetStateIndices(ExecBuffer, State: pCommand->m_State, IsTextured, BlendModeIndex, DynamicIndex, AddressModeIndex);
7215 IsTextured = true; // text is always textured
7216 auto &PipeLayout = GetPipeLayout(Container&: m_TextPipeline, IsTextured, BlendModeIndex, DynamicIndex);
7217 auto &PipeLine = GetPipeline(Container&: m_TextPipeline, IsTextured, BlendModeIndex, DynamicIndex);
7218
7219 VkCommandBuffer *pCommandBuffer;
7220 if(!GetGraphicCommandBuffer(pDrawCommandBuffer&: pCommandBuffer, RenderThreadIndex: ExecBuffer.m_ThreadIndex))
7221 return false;
7222 auto &CommandBuffer = *pCommandBuffer;
7223
7224 BindPipeline(RenderThreadIndex: ExecBuffer.m_ThreadIndex, CommandBuffer, ExecBuffer, BindingPipe&: PipeLine, State: pCommand->m_State);
7225
7226 std::array<VkBuffer, 1> aVertexBuffers = {ExecBuffer.m_Buffer};
7227 std::array<VkDeviceSize, 1> aOffsets = {(VkDeviceSize)ExecBuffer.m_BufferOff};
7228 vkCmdBindVertexBuffers(commandBuffer: CommandBuffer, firstBinding: 0, bindingCount: 1, pBuffers: aVertexBuffers.data(), pOffsets: aOffsets.data());
7229
7230 vkCmdBindIndexBuffer(commandBuffer: CommandBuffer, buffer: ExecBuffer.m_IndexBuffer, offset: 0, indexType: VK_INDEX_TYPE_UINT32);
7231
7232 vkCmdBindDescriptorSets(commandBuffer: CommandBuffer, pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, layout: PipeLayout, firstSet: 0, descriptorSetCount: 1, pDescriptorSets: &ExecBuffer.m_aDescriptors[0].m_Descriptor, dynamicOffsetCount: 0, pDynamicOffsets: nullptr);
7233
7234 SUniformGTextPos PosTexSizeConstant;
7235 mem_copy(dest: PosTexSizeConstant.m_aPos, source: m.data(), size: m.size() * sizeof(float));
7236 PosTexSizeConstant.m_TextureSize = pCommand->m_TextureSize;
7237
7238 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_VERTEX_BIT, offset: 0, size: sizeof(SUniformGTextPos), pValues: &PosTexSizeConstant);
7239
7240 SUniformTextFragment FragmentConstants;
7241
7242 FragmentConstants.m_Constants.m_TextColor = pCommand->m_TextColor;
7243 FragmentConstants.m_Constants.m_TextOutlineColor = pCommand->m_TextOutlineColor;
7244 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, offset: sizeof(SUniformGTextPos) + sizeof(SUniformTextGFragmentOffset), size: sizeof(SUniformTextFragment), pValues: &FragmentConstants);
7245
7246 vkCmdDrawIndexed(commandBuffer: CommandBuffer, indexCount: static_cast<uint32_t>(pCommand->m_DrawNum), instanceCount: 1, firstIndex: 0, vertexOffset: 0, firstInstance: 0);
7247
7248 return true;
7249 }
7250
7251 void BufferContainer_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SState &State, size_t BufferContainerIndex, size_t DrawCalls)
7252 {
7253 size_t BufferObjectIndex = (size_t)m_vBufferContainers[BufferContainerIndex].m_BufferObjectIndex;
7254 const auto &BufferObject = m_vBufferObjects[BufferObjectIndex];
7255
7256 ExecBuffer.m_Buffer = BufferObject.m_CurBuffer;
7257 ExecBuffer.m_BufferOff = BufferObject.m_CurBufferOffset;
7258
7259 bool IsTextured = GetIsTextured(State);
7260 if(IsTextured)
7261 {
7262 size_t AddressModeIndex = GetAddressModeIndex(State);
7263 ExecBuffer.m_aDescriptors[0] = m_vTextures[State.m_Texture].m_aVKStandardTexturedDescrSets[AddressModeIndex];
7264 }
7265
7266 ExecBuffer.m_IndexBuffer = m_RenderIndexBuffer;
7267
7268 ExecBuffer.m_EstimatedRenderCallCount = DrawCalls;
7269
7270 ExecBufferFillDynamicStates(State, ExecBuffer);
7271 }
7272
7273 void Cmd_RenderQuadContainer_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_RenderQuadContainer *pCommand)
7274 {
7275 BufferContainer_FillExecuteBuffer(ExecBuffer, State: pCommand->m_State, BufferContainerIndex: (size_t)pCommand->m_BufferContainerIndex, DrawCalls: 1);
7276 }
7277
7278 [[nodiscard]] bool Cmd_RenderQuadContainer(const CCommandBuffer::SCommand_RenderQuadContainer *pCommand, SRenderCommandExecuteBuffer &ExecBuffer)
7279 {
7280 std::array<float, (size_t)4 * 2> m;
7281 GetStateMatrix(State: pCommand->m_State, Matrix&: m);
7282
7283 bool IsTextured;
7284 size_t BlendModeIndex;
7285 size_t DynamicIndex;
7286 size_t AddressModeIndex;
7287 GetStateIndices(ExecBuffer, State: pCommand->m_State, IsTextured, BlendModeIndex, DynamicIndex, AddressModeIndex);
7288 auto &PipeLayout = GetStandardPipeLayout(IsLineGeometry: false, IsTextured, BlendModeIndex, DynamicIndex);
7289 auto &PipeLine = GetStandardPipe(IsLineGeometry: false, IsTextured, BlendModeIndex, DynamicIndex);
7290
7291 VkCommandBuffer *pCommandBuffer;
7292 if(!GetGraphicCommandBuffer(pDrawCommandBuffer&: pCommandBuffer, RenderThreadIndex: ExecBuffer.m_ThreadIndex))
7293 return false;
7294 auto &CommandBuffer = *pCommandBuffer;
7295
7296 BindPipeline(RenderThreadIndex: ExecBuffer.m_ThreadIndex, CommandBuffer, ExecBuffer, BindingPipe&: PipeLine, State: pCommand->m_State);
7297
7298 std::array<VkBuffer, 1> aVertexBuffers = {ExecBuffer.m_Buffer};
7299 std::array<VkDeviceSize, 1> aOffsets = {(VkDeviceSize)ExecBuffer.m_BufferOff};
7300 vkCmdBindVertexBuffers(commandBuffer: CommandBuffer, firstBinding: 0, bindingCount: 1, pBuffers: aVertexBuffers.data(), pOffsets: aOffsets.data());
7301
7302 VkDeviceSize IndexOffset = (VkDeviceSize)((ptrdiff_t)pCommand->m_pOffset);
7303
7304 vkCmdBindIndexBuffer(commandBuffer: CommandBuffer, buffer: ExecBuffer.m_IndexBuffer, offset: IndexOffset, indexType: VK_INDEX_TYPE_UINT32);
7305
7306 if(IsTextured)
7307 {
7308 vkCmdBindDescriptorSets(commandBuffer: CommandBuffer, pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, layout: PipeLayout, firstSet: 0, descriptorSetCount: 1, pDescriptorSets: &ExecBuffer.m_aDescriptors[0].m_Descriptor, dynamicOffsetCount: 0, pDynamicOffsets: nullptr);
7309 }
7310
7311 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_VERTEX_BIT, offset: 0, size: sizeof(SUniformGPos), pValues: m.data());
7312
7313 vkCmdDrawIndexed(commandBuffer: CommandBuffer, indexCount: static_cast<uint32_t>(pCommand->m_DrawNum), instanceCount: 1, firstIndex: 0, vertexOffset: 0, firstInstance: 0);
7314
7315 return true;
7316 }
7317
7318 void Cmd_RenderQuadContainerEx_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_RenderQuadContainerEx *pCommand)
7319 {
7320 BufferContainer_FillExecuteBuffer(ExecBuffer, State: pCommand->m_State, BufferContainerIndex: (size_t)pCommand->m_BufferContainerIndex, DrawCalls: 1);
7321 }
7322
7323 [[nodiscard]] bool Cmd_RenderQuadContainerEx(const CCommandBuffer::SCommand_RenderQuadContainerEx *pCommand, SRenderCommandExecuteBuffer &ExecBuffer)
7324 {
7325 std::array<float, (size_t)4 * 2> m;
7326 GetStateMatrix(State: pCommand->m_State, Matrix&: m);
7327
7328 bool IsRotationless = !(pCommand->m_Rotation != 0);
7329 bool IsTextured;
7330 size_t BlendModeIndex;
7331 size_t DynamicIndex;
7332 size_t AddressModeIndex;
7333 GetStateIndices(ExecBuffer, State: pCommand->m_State, IsTextured, BlendModeIndex, DynamicIndex, AddressModeIndex);
7334 auto &PipeLayout = GetPipeLayout(Container&: IsRotationless ? m_PrimExRotationlessPipeline : m_PrimExPipeline, IsTextured, BlendModeIndex, DynamicIndex);
7335 auto &PipeLine = GetPipeline(Container&: IsRotationless ? m_PrimExRotationlessPipeline : m_PrimExPipeline, IsTextured, BlendModeIndex, DynamicIndex);
7336
7337 VkCommandBuffer *pCommandBuffer;
7338 if(!GetGraphicCommandBuffer(pDrawCommandBuffer&: pCommandBuffer, RenderThreadIndex: ExecBuffer.m_ThreadIndex))
7339 return false;
7340 auto &CommandBuffer = *pCommandBuffer;
7341
7342 BindPipeline(RenderThreadIndex: ExecBuffer.m_ThreadIndex, CommandBuffer, ExecBuffer, BindingPipe&: PipeLine, State: pCommand->m_State);
7343
7344 std::array<VkBuffer, 1> aVertexBuffers = {ExecBuffer.m_Buffer};
7345 std::array<VkDeviceSize, 1> aOffsets = {(VkDeviceSize)ExecBuffer.m_BufferOff};
7346 vkCmdBindVertexBuffers(commandBuffer: CommandBuffer, firstBinding: 0, bindingCount: 1, pBuffers: aVertexBuffers.data(), pOffsets: aOffsets.data());
7347
7348 VkDeviceSize IndexOffset = (VkDeviceSize)((ptrdiff_t)pCommand->m_pOffset);
7349
7350 vkCmdBindIndexBuffer(commandBuffer: CommandBuffer, buffer: ExecBuffer.m_IndexBuffer, offset: IndexOffset, indexType: VK_INDEX_TYPE_UINT32);
7351
7352 if(IsTextured)
7353 {
7354 vkCmdBindDescriptorSets(commandBuffer: CommandBuffer, pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, layout: PipeLayout, firstSet: 0, descriptorSetCount: 1, pDescriptorSets: &ExecBuffer.m_aDescriptors[0].m_Descriptor, dynamicOffsetCount: 0, pDynamicOffsets: nullptr);
7355 }
7356
7357 SUniformPrimExGVertColor PushConstantColor;
7358 SUniformPrimExGPos PushConstantVertex;
7359 size_t VertexPushConstantSize = sizeof(PushConstantVertex);
7360
7361 PushConstantColor = pCommand->m_VertexColor;
7362 mem_copy(dest: PushConstantVertex.m_aPos, source: m.data(), size: sizeof(PushConstantVertex.m_aPos));
7363
7364 if(!IsRotationless)
7365 {
7366 PushConstantVertex.m_Rotation = pCommand->m_Rotation;
7367 PushConstantVertex.m_Center = {pCommand->m_Center.x, pCommand->m_Center.y};
7368 }
7369 else
7370 {
7371 VertexPushConstantSize = sizeof(SUniformPrimExGPosRotationless);
7372 }
7373
7374 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_VERTEX_BIT, offset: 0, size: VertexPushConstantSize, pValues: &PushConstantVertex);
7375 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, offset: sizeof(SUniformPrimExGPos) + sizeof(SUniformPrimExGVertColorAlign), size: sizeof(PushConstantColor), pValues: &PushConstantColor);
7376
7377 vkCmdDrawIndexed(commandBuffer: CommandBuffer, indexCount: static_cast<uint32_t>(pCommand->m_DrawNum), instanceCount: 1, firstIndex: 0, vertexOffset: 0, firstInstance: 0);
7378
7379 return true;
7380 }
7381
7382 void Cmd_RenderQuadContainerAsSpriteMultiple_FillExecuteBuffer(SRenderCommandExecuteBuffer &ExecBuffer, const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *pCommand)
7383 {
7384 BufferContainer_FillExecuteBuffer(ExecBuffer, State: pCommand->m_State, BufferContainerIndex: (size_t)pCommand->m_BufferContainerIndex, DrawCalls: ((pCommand->m_DrawCount - 1) / gs_GraphicsMaxParticlesRenderCount) + 1);
7385 }
7386
7387 [[nodiscard]] bool Cmd_RenderQuadContainerAsSpriteMultiple(const CCommandBuffer::SCommand_RenderQuadContainerAsSpriteMultiple *pCommand, SRenderCommandExecuteBuffer &ExecBuffer)
7388 {
7389 std::array<float, (size_t)4 * 2> m;
7390 GetStateMatrix(State: pCommand->m_State, Matrix&: m);
7391
7392 bool CanBePushed = pCommand->m_DrawCount <= 1;
7393
7394 bool IsTextured;
7395 size_t BlendModeIndex;
7396 size_t DynamicIndex;
7397 size_t AddressModeIndex;
7398 GetStateIndices(ExecBuffer, State: pCommand->m_State, IsTextured, BlendModeIndex, DynamicIndex, AddressModeIndex);
7399 auto &PipeLayout = GetPipeLayout(Container&: CanBePushed ? m_SpriteMultiPushPipeline : m_SpriteMultiPipeline, IsTextured, BlendModeIndex, DynamicIndex);
7400 auto &PipeLine = GetPipeline(Container&: CanBePushed ? m_SpriteMultiPushPipeline : m_SpriteMultiPipeline, IsTextured, BlendModeIndex, DynamicIndex);
7401
7402 VkCommandBuffer *pCommandBuffer;
7403 if(!GetGraphicCommandBuffer(pDrawCommandBuffer&: pCommandBuffer, RenderThreadIndex: ExecBuffer.m_ThreadIndex))
7404 return false;
7405 auto &CommandBuffer = *pCommandBuffer;
7406
7407 BindPipeline(RenderThreadIndex: ExecBuffer.m_ThreadIndex, CommandBuffer, ExecBuffer, BindingPipe&: PipeLine, State: pCommand->m_State);
7408
7409 std::array<VkBuffer, 1> aVertexBuffers = {ExecBuffer.m_Buffer};
7410 std::array<VkDeviceSize, 1> aOffsets = {(VkDeviceSize)ExecBuffer.m_BufferOff};
7411 vkCmdBindVertexBuffers(commandBuffer: CommandBuffer, firstBinding: 0, bindingCount: 1, pBuffers: aVertexBuffers.data(), pOffsets: aOffsets.data());
7412
7413 VkDeviceSize IndexOffset = (VkDeviceSize)((ptrdiff_t)pCommand->m_pOffset);
7414 vkCmdBindIndexBuffer(commandBuffer: CommandBuffer, buffer: ExecBuffer.m_IndexBuffer, offset: IndexOffset, indexType: VK_INDEX_TYPE_UINT32);
7415
7416 vkCmdBindDescriptorSets(commandBuffer: CommandBuffer, pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, layout: PipeLayout, firstSet: 0, descriptorSetCount: 1, pDescriptorSets: &ExecBuffer.m_aDescriptors[0].m_Descriptor, dynamicOffsetCount: 0, pDynamicOffsets: nullptr);
7417
7418 if(CanBePushed)
7419 {
7420 SUniformSpriteMultiPushGVertColor PushConstantColor;
7421 SUniformSpriteMultiPushGPos PushConstantVertex;
7422
7423 PushConstantColor = pCommand->m_VertexColor;
7424
7425 mem_copy(dest: PushConstantVertex.m_aPos, source: m.data(), size: sizeof(PushConstantVertex.m_aPos));
7426 PushConstantVertex.m_Center = pCommand->m_Center;
7427
7428 for(size_t i = 0; i < pCommand->m_DrawCount; ++i)
7429 PushConstantVertex.m_aPSR[i] = vec4(pCommand->m_pRenderInfo[i].m_Pos.x, pCommand->m_pRenderInfo[i].m_Pos.y, pCommand->m_pRenderInfo[i].m_Scale, pCommand->m_pRenderInfo[i].m_Rotation);
7430
7431 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_VERTEX_BIT, offset: 0, size: sizeof(SUniformSpriteMultiPushGPosBase) + sizeof(vec4) * pCommand->m_DrawCount, pValues: &PushConstantVertex);
7432 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, offset: sizeof(SUniformSpriteMultiPushGPos), size: sizeof(PushConstantColor), pValues: &PushConstantColor);
7433 }
7434 else
7435 {
7436 SUniformSpriteMultiGVertColor PushConstantColor;
7437 SUniformSpriteMultiGPos PushConstantVertex;
7438
7439 PushConstantColor = pCommand->m_VertexColor;
7440
7441 mem_copy(dest: PushConstantVertex.m_aPos, source: m.data(), size: sizeof(PushConstantVertex.m_aPos));
7442 PushConstantVertex.m_Center = pCommand->m_Center;
7443
7444 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_VERTEX_BIT, offset: 0, size: sizeof(PushConstantVertex), pValues: &PushConstantVertex);
7445 vkCmdPushConstants(commandBuffer: CommandBuffer, layout: PipeLayout, stageFlags: VK_SHADER_STAGE_FRAGMENT_BIT, offset: sizeof(SUniformSpriteMultiGPos) + sizeof(SUniformSpriteMultiGVertColorAlign), size: sizeof(PushConstantColor), pValues: &PushConstantColor);
7446 }
7447
7448 const int RSPCount = 512;
7449 int DrawCount = pCommand->m_DrawCount;
7450 size_t RenderOffset = 0;
7451
7452 while(DrawCount > 0)
7453 {
7454 int UniformCount = (DrawCount > RSPCount ? RSPCount : DrawCount);
7455
7456 if(!CanBePushed)
7457 {
7458 // create uniform buffer
7459 SDeviceDescriptorSet UniDescrSet;
7460 if(!GetUniformBufferObject(RenderThreadIndex: ExecBuffer.m_ThreadIndex, RequiresSharedStagesDescriptor: false, DescrSet&: UniDescrSet, ParticleCount: UniformCount, pData: (const float *)(pCommand->m_pRenderInfo + RenderOffset), DataSize: UniformCount * sizeof(IGraphics::SRenderSpriteInfo)))
7461 return false;
7462
7463 vkCmdBindDescriptorSets(commandBuffer: CommandBuffer, pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, layout: PipeLayout, firstSet: 1, descriptorSetCount: 1, pDescriptorSets: &UniDescrSet.m_Descriptor, dynamicOffsetCount: 0, pDynamicOffsets: nullptr);
7464 }
7465
7466 vkCmdDrawIndexed(commandBuffer: CommandBuffer, indexCount: static_cast<uint32_t>(pCommand->m_DrawNum), instanceCount: UniformCount, firstIndex: 0, vertexOffset: 0, firstInstance: 0);
7467
7468 RenderOffset += RSPCount;
7469 DrawCount -= RSPCount;
7470 }
7471
7472 return true;
7473 }
7474
7475 [[nodiscard]] bool Cmd_WindowCreateNtf(const CCommandBuffer::SCommand_WindowCreateNtf *pCommand)
7476 {
7477 log_debug("vulkan", "creating new surface.");
7478 m_pWindow = SDL_GetWindowFromID(id: pCommand->m_WindowId);
7479 if(m_RenderingPaused)
7480 {
7481#ifdef CONF_PLATFORM_ANDROID
7482 if(!CreateSurface(m_pWindow))
7483 return false;
7484 m_RecreateSwapChain = true;
7485#endif
7486 m_RenderingPaused = false;
7487 if(!PureMemoryFrame())
7488 return false;
7489 if(!PrepareFrame())
7490 return false;
7491 }
7492
7493 return true;
7494 }
7495
7496 [[nodiscard]] bool Cmd_WindowDestroyNtf(const CCommandBuffer::SCommand_WindowDestroyNtf *pCommand)
7497 {
7498 log_debug("vulkan", "surface got destroyed.");
7499 if(!m_RenderingPaused)
7500 {
7501 if(!WaitFrame())
7502 return false;
7503 m_RenderingPaused = true;
7504 vkDeviceWaitIdle(device: m_VKDevice);
7505#ifdef CONF_PLATFORM_ANDROID
7506 CleanupVulkanSwapChain(true);
7507#endif
7508 }
7509
7510 return true;
7511 }
7512
7513 [[nodiscard]] bool Cmd_PreInit(const CCommandProcessorFragment_GLBase::SCommand_PreInit *pCommand)
7514 {
7515 m_pGpuList = pCommand->m_pGpuList;
7516 if(InitVulkanSDL(pWindow: pCommand->m_pWindow, CanvasWidth: pCommand->m_Width, CanvasHeight: pCommand->m_Height, pRendererString: pCommand->m_pRendererString, pVendorString: pCommand->m_pVendorString, pVersionString: pCommand->m_pVersionString) != 0)
7517 {
7518 m_VKInstance = VK_NULL_HANDLE;
7519 }
7520
7521 RegisterCommands();
7522
7523 m_ThreadCount = g_Config.m_GfxRenderThreadCount;
7524 if(m_ThreadCount <= 1)
7525 m_ThreadCount = 1;
7526 else
7527 {
7528 m_ThreadCount = clamp<decltype(m_ThreadCount)>(val: m_ThreadCount, lo: 3, hi: std::max<decltype(m_ThreadCount)>(a: 3, b: std::thread::hardware_concurrency()));
7529 }
7530
7531 // start threads
7532 dbg_assert(m_ThreadCount != 2, "Either use 1 main thread or at least 2 extra rendering threads.");
7533 if(m_ThreadCount > 1)
7534 {
7535 m_vvThreadCommandLists.resize(new_size: m_ThreadCount - 1);
7536 m_vThreadHelperHadCommands.resize(new_size: m_ThreadCount - 1, x: false);
7537 for(auto &ThreadCommandList : m_vvThreadCommandLists)
7538 {
7539 ThreadCommandList.reserve(n: 256);
7540 }
7541
7542 for(size_t i = 0; i < m_ThreadCount - 1; ++i)
7543 {
7544 auto *pRenderThread = new SRenderThread();
7545 std::unique_lock<std::mutex> Lock(pRenderThread->m_Mutex);
7546 m_vpRenderThreads.emplace_back(args&: pRenderThread);
7547 pRenderThread->m_Thread = std::thread([this, i]() { RunThread(ThreadIndex: i); });
7548 // wait until thread started
7549 pRenderThread->m_Cond.wait(lock&: Lock, p: [pRenderThread]() -> bool { return pRenderThread->m_Started; });
7550 }
7551 }
7552
7553 return true;
7554 }
7555
7556 [[nodiscard]] bool Cmd_PostShutdown(const CCommandProcessorFragment_GLBase::SCommand_PostShutdown *pCommand)
7557 {
7558 for(size_t i = 0; i < m_ThreadCount - 1; ++i)
7559 {
7560 auto *pThread = m_vpRenderThreads[i].get();
7561 {
7562 std::unique_lock<std::mutex> Lock(pThread->m_Mutex);
7563 pThread->m_Finished = true;
7564 pThread->m_Cond.notify_one();
7565 }
7566 pThread->m_Thread.join();
7567 }
7568 m_vpRenderThreads.clear();
7569 m_vvThreadCommandLists.clear();
7570 m_vThreadHelperHadCommands.clear();
7571
7572 m_ThreadCount = 1;
7573
7574 CleanupVulkanSDL();
7575
7576 return true;
7577 }
7578
7579 void StartCommands(size_t CommandCount, size_t EstimatedRenderCallCount) override
7580 {
7581 m_CommandsInPipe = CommandCount;
7582 m_RenderCallsInPipe = EstimatedRenderCallCount;
7583 m_CurCommandInPipe = 0;
7584 m_CurRenderCallCountInPipe = 0;
7585 }
7586
7587 void EndCommands() override
7588 {
7589 FinishRenderThreads();
7590 m_CommandsInPipe = 0;
7591 m_RenderCallsInPipe = 0;
7592 }
7593
7594 /****************
7595 * RENDER THREADS
7596 *****************/
7597
7598 void RunThread(size_t ThreadIndex)
7599 {
7600 auto *pThread = m_vpRenderThreads[ThreadIndex].get();
7601 std::unique_lock<std::mutex> Lock(pThread->m_Mutex);
7602 pThread->m_Started = true;
7603 pThread->m_Cond.notify_one();
7604
7605 while(!pThread->m_Finished)
7606 {
7607 pThread->m_Cond.wait(lock&: Lock, p: [pThread]() -> bool { return pThread->m_IsRendering || pThread->m_Finished; });
7608 pThread->m_Cond.notify_one();
7609
7610 // set this to true, if you want to benchmark the render thread times
7611 static constexpr bool s_BenchmarkRenderThreads = false;
7612 std::chrono::nanoseconds ThreadRenderTime = 0ns;
7613 if(IsVerbose() && s_BenchmarkRenderThreads)
7614 {
7615 ThreadRenderTime = time_get_nanoseconds();
7616 }
7617
7618 if(!pThread->m_Finished)
7619 {
7620 bool HasErrorFromCmd = false;
7621 for(auto &NextCmd : m_vvThreadCommandLists[ThreadIndex])
7622 {
7623 if(!m_aCommandCallbacks[CommandBufferCMDOff(CommandBufferCMD: NextCmd.m_Command)].m_CommandCB(NextCmd.m_pRawCommand, NextCmd))
7624 {
7625 // an error occurred, the thread will not continue execution
7626 HasErrorFromCmd = true;
7627 break;
7628 }
7629 }
7630 m_vvThreadCommandLists[ThreadIndex].clear();
7631
7632 if(!HasErrorFromCmd && m_vvUsedThreadDrawCommandBuffer[ThreadIndex + 1][m_CurImageIndex])
7633 {
7634 auto &GraphicThreadCommandBuffer = m_vvThreadDrawCommandBuffers[ThreadIndex + 1][m_CurImageIndex];
7635 vkEndCommandBuffer(commandBuffer: GraphicThreadCommandBuffer);
7636 }
7637 }
7638
7639 if(IsVerbose() && s_BenchmarkRenderThreads)
7640 {
7641 dbg_msg(sys: "vulkan", fmt: "render thread %" PRIzu " took %d ns to finish", ThreadIndex, (int)(time_get_nanoseconds() - ThreadRenderTime).count());
7642 }
7643
7644 pThread->m_IsRendering = false;
7645 }
7646 }
7647};
7648
7649CCommandProcessorFragment_GLBase *CreateVulkanCommandProcessorFragment()
7650{
7651 return new CCommandProcessorFragment_Vulkan();
7652}
7653
7654#endif
7655