From 547d3d04183ab38886d3b6f750fd162c8d5a8e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 06:58:27 +0200 Subject: [PATCH 01/23] Fix data pool commit range --- src/Elemental/Common/SystemDataPool.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Elemental/Common/SystemDataPool.cpp b/src/Elemental/Common/SystemDataPool.cpp index 4a374128..ae045a68 100644 --- a/src/Elemental/Common/SystemDataPool.cpp +++ b/src/Elemental/Common/SystemDataPool.cpp @@ -93,11 +93,13 @@ ElemHandle SystemAddDataPoolItem(SystemDataPool dataPool, T data) index = SystemAtomicAdd(storage->CurrentIndex, 1); - SystemCommitMemory>(storage->MemoryArena, storage->Data.Slice(index, 1000), true); + auto remainingItemCount = storage->Data.Length - index; + auto itemCountToCommit = remainingItemCount > 1000 ? 1000 : remainingItemCount; + SystemCommitMemory>(storage->MemoryArena, storage->Data.Slice(index, itemCountToCommit), true); if (!IsTypeEmpty()) { - SystemCommitMemory(storage->MemoryArena, storage->DataFull.Slice(index, 1000), true); + SystemCommitMemory(storage->MemoryArena, storage->DataFull.Slice(index, itemCountToCommit), true); } } From 2e34aeb56eac6c63a6a100970314888b14e5efcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:08:03 +0200 Subject: [PATCH 02/23] Synchronize Vulkan acceleration structure builds --- .../Graphics/Vulkan/VulkanResourceBarrier.cpp | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp index 0c065fb3..130deda9 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp @@ -102,8 +102,9 @@ void InsertVulkanResourceBarriersIfNeeded(ElemCommandList commandList, ElemGraph SystemAssert(commandListData); auto barriersInfo = GenerateBarrierCommands(stackMemoryArena, commandListData->ResourceBarrierPool, currentStage, VulkanDebugBarrierInfoEnabled); + auto needsRaytracingBuildBarrier = currentStage == ElemGraphicsResourceBarrierSyncType_BuildRaytracingAccelerationStructure; - if (barriersInfo.BufferBarriers.Length == 0 && barriersInfo.TextureBarriers.Length == 0) + if (barriersInfo.BufferBarriers.Length == 0 && barriersInfo.TextureBarriers.Length == 0 && !needsRaytracingBuildBarrier) { return; } @@ -112,7 +113,7 @@ void InsertVulkanResourceBarriersIfNeeded(ElemCommandList commandList, ElemGraph dependencyInfo.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; uint32_t vulkanBufferBarrierCount = 0; - bool hasAccelerationStructureBarrier = false; + bool hasAccelerationStructureBarrier = needsRaytracingBuildBarrier; for (uint32_t i = 0; i < barriersInfo.BufferBarriers.Length; i++) { @@ -137,6 +138,23 @@ void InsertVulkanResourceBarriersIfNeeded(ElemCommandList commandList, ElemGraph VkMemoryBarrier2 accelerationStructureMemoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 }; + if (needsRaytracingBuildBarrier) + { + // Acceleration-structure builds implicitly read geometry/instance buffers and write + // acceleration-structure/scratch memory. Until the common barrier model records those + // command accesses explicitly, use one coarse phase barrier here so uploads and previous + // builds are visible to the next build. This intentionally favors correctness over + // per-resource precision and can be replaced by the planned renderer-level sync model. + accelerationStructureMemoryBarrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT | + VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; + accelerationStructureMemoryBarrier.dstStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; + accelerationStructureMemoryBarrier.srcAccessMask = VK_ACCESS_2_TRANSFER_WRITE_BIT | + VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; + accelerationStructureMemoryBarrier.dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT | + VK_ACCESS_2_ACCELERATION_STRUCTURE_READ_BIT_KHR | + VK_ACCESS_2_ACCELERATION_STRUCTURE_WRITE_BIT_KHR; + } + if (hasAccelerationStructureBarrier) { dependencyInfo.pMemoryBarriers = &accelerationStructureMemoryBarrier; From 15f90e8cf6ff440cfd0f4c0563e02f60da607e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:09:18 +0200 Subject: [PATCH 03/23] Clarify temporary Vulkan raytracing build barrier --- .../Common/Graphics/Vulkan/VulkanResourceBarrier.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp index 130deda9..80efc2fe 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp @@ -140,11 +140,11 @@ void InsertVulkanResourceBarriersIfNeeded(ElemCommandList commandList, ElemGraph if (needsRaytracingBuildBarrier) { - // Acceleration-structure builds implicitly read geometry/instance buffers and write - // acceleration-structure/scratch memory. Until the common barrier model records those - // command accesses explicitly, use one coarse phase barrier here so uploads and previous - // builds are visible to the next build. This intentionally favors correctness over - // per-resource precision and can be replaced by the planned renderer-level sync model. + // HACK: Acceleration-structure builds implicitly read geometry/instance buffers and + // write acceleration-structure/scratch memory. Until the common barrier model records + // those command accesses explicitly, use one coarse phase barrier here so uploads and + // previous builds are visible to the next build. Replace this with the planned + // renderer-level/global synchronization model rather than growing per-BLAS tracking. accelerationStructureMemoryBarrier.srcStageMask = VK_PIPELINE_STAGE_2_COPY_BIT | VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; accelerationStructureMemoryBarrier.dstStageMask = VK_PIPELINE_STAGE_2_ACCELERATION_STRUCTURE_BUILD_BIT_KHR; From 6a6e333332090c0e7d006387ab07ec84c2bbef48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:09:46 +0200 Subject: [PATCH 04/23] No-op sync checkpoint From e485b06f499e8affc7b41663c9e4018923f90999 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:10:07 +0200 Subject: [PATCH 05/23] Keep data-pool fix unchanged From 7bf4201848ac2afb1b162c6d937107f6161f3ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:10:28 +0200 Subject: [PATCH 06/23] Preserve data-pool fix From 0a65a5b66391d8bc1acdf5c514d34e11208053bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:20:25 +0200 Subject: [PATCH 07/23] Fix Vulkan swapchain semaphore ownership --- src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.h b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.h index edfd492c..c92a21cc 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.h @@ -23,6 +23,7 @@ struct VulkanCommandQueueData ElemGraphicsDevice GraphicsDevice; VkSemaphore Fence; uint64_t FenceValue; + VkSemaphore AcquireSemaphore; VkSemaphore PresentSemaphore; bool SignalPresentSemaphore; uint64_t LastCompletedFenceValue; From 52708e8d57149a988e788bb4b19d252596d69af6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:20:35 +0200 Subject: [PATCH 08/23] Track Vulkan swapchain frame semaphores --- src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h index 251beffa..c35b28db 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.h @@ -19,7 +19,10 @@ struct VulkanSwapChainData ElemWindow Window; ElemGraphicsResource BackBufferTextures[VULKAN_MAX_SWAPCHAIN_BUFFERS]; uint32_t CurrentImageIndex; + uint32_t CurrentFrameIndex; VkSemaphore BackBufferAcquireSemaphores[VULKAN_MAX_SWAPCHAIN_BUFFERS]; + ElemFence BackBufferAcquireFences[VULKAN_MAX_SWAPCHAIN_BUFFERS]; + VkSemaphore BackBufferPresentSemaphores[VULKAN_MAX_SWAPCHAIN_BUFFERS]; ElemSwapChainUpdateHandlerPtr UpdateHandler; void* UpdatePayload; uint64_t CreationTimestamp; From 7abbb8f78ea61ffbed2007abf0bf1ba18562d602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:21:33 +0200 Subject: [PATCH 09/23] Synchronize Vulkan swapchain acquire and submit --- .../Graphics/Vulkan/VulkanCommandList.cpp | 52 ++++++++++++------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp index af010d9a..e9e564c1 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp @@ -121,11 +121,6 @@ ElemCommandQueue VulkanCreateCommandQueue(ElemGraphicsDevice graphicsDevice, Ele VkSemaphore fence; AssertIfFailed(vkCreateSemaphore(graphicsDeviceData->Device, &createInfo, NULL, &fence)); - createInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; - - VkSemaphore presentSemaphore; - AssertIfFailed(vkCreateSemaphore(graphicsDeviceData->Device, &createInfo, NULL, &presentSemaphore)); - if (VulkanDebugLayerEnabled && options && options->DebugName) { VkDebugUtilsObjectNameInfoEXT nameInfo = { VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT }; @@ -151,7 +146,8 @@ ElemCommandQueue VulkanCreateCommandQueue(ElemGraphicsDevice graphicsDevice, Ele .GraphicsDevice = graphicsDevice, .Fence = fence, .FenceValue = 0, - .PresentSemaphore = presentSemaphore, + .AcquireSemaphore = VK_NULL_HANDLE, + .PresentSemaphore = VK_NULL_HANDLE, .LastCompletedFenceValue = 0, .CommandQueueFrequency = queueFrequency }); @@ -190,7 +186,6 @@ void VulkanFreeCommandQueue(ElemCommandQueue commandQueue) } // TODO: Free allocators and command buffers - vkDestroySemaphore(graphicsDeviceData->Device, commandQueueData->PresentSemaphore, nullptr); vkDestroySemaphore(graphicsDeviceData->Device, commandQueueData->Fence, nullptr); auto graphicsIdUnpacked = UnpackSystemDataPoolHandle(commandQueueData->GraphicsDevice); @@ -405,17 +400,21 @@ ElemFence VulkanExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandLi auto commandQueueData = GetVulkanCommandQueueData(commandQueue); SystemAssert(commandQueueData); + auto fencesToWaitCount = options ? options->FencesToWait.Length : 0; + auto hasAcquireSemaphore = commandQueueData->AcquireSemaphore != VK_NULL_HANDLE; + auto waitSemaphoreCount = fencesToWaitCount + (hasAcquireSemaphore ? 1 : 0); + Span submitStageMasks = {}; Span waitSemaphores = {}; Span waitSemaphoreValues = {}; - if (options && options->FencesToWait.Length > 0) + if (waitSemaphoreCount > 0) { - submitStageMasks = SystemPushArray(stackMemoryArena, options->FencesToWait.Length); - waitSemaphores = SystemPushArray(stackMemoryArena, options->FencesToWait.Length); - waitSemaphoreValues = SystemPushArray(stackMemoryArena, options->FencesToWait.Length); + submitStageMasks = SystemPushArray(stackMemoryArena, waitSemaphoreCount); + waitSemaphores = SystemPushArray(stackMemoryArena, waitSemaphoreCount); + waitSemaphoreValues = SystemPushArray(stackMemoryArena, waitSemaphoreCount); - for (uint32_t i = 0; i < options->FencesToWait.Length; i++) + for (uint32_t i = 0; i < fencesToWaitCount; i++) { auto fenceToWait = options->FencesToWait.Items[i]; @@ -431,6 +430,14 @@ ElemFence VulkanExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandLi SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Waiting for fence before ExecuteCommandLists. (CommandQueue=%d, Value=%d)", fenceToWait.CommandQueue, fenceToWait.FenceValue); } } + + if (hasAcquireSemaphore) + { + auto acquireSemaphoreIndex = fencesToWaitCount; + submitStageMasks[acquireSemaphoreIndex] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + waitSemaphores[acquireSemaphoreIndex] = commandQueueData->AcquireSemaphore; + waitSemaphoreValues[acquireSemaphoreIndex] = 0; + } } bool hasError = false; @@ -455,20 +462,18 @@ ElemFence VulkanExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandLi if (!hasError) { uint32_t signalCount = 1u; + auto signalPresentSemaphore = commandQueueData->SignalPresentSemaphore; - // TODO: Here we signal the present semaphore. We should do the same for the wait semaphore that we set during the acquire - // It is the same logic - // For both signalpresent and waitacquire, we need to have one per frame in flight - if (commandQueueData->SignalPresentSemaphore) + if (signalPresentSemaphore) { + SystemAssert(commandQueueData->PresentSemaphore != VK_NULL_HANDLE); signalCount = 2u; - commandQueueData->SignalPresentSemaphore = false; } uint64_t signalValues[] = { fenceValue, 0u }; VkTimelineSemaphoreSubmitInfo timelineInfo = { VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO }; - timelineInfo.waitSemaphoreValueCount = waitSemaphoreValues.Length; + timelineInfo.waitSemaphoreValueCount = waitSemaphores.Length; timelineInfo.pWaitSemaphoreValues = waitSemaphoreValues.Pointer; timelineInfo.signalSemaphoreValueCount = signalCount; timelineInfo.pSignalSemaphoreValues = signalValues; @@ -486,6 +491,17 @@ ElemFence VulkanExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandLi submitInfo.pNext = &timelineInfo; AssertIfFailed(vkQueueSubmit(commandQueueData->DeviceObject, 1, &submitInfo, VK_NULL_HANDLE)); + + if (hasAcquireSemaphore) + { + commandQueueData->AcquireSemaphore = VK_NULL_HANDLE; + } + + if (signalPresentSemaphore) + { + commandQueueData->SignalPresentSemaphore = false; + commandQueueData->PresentSemaphore = VK_NULL_HANDLE; + } } auto fence = ElemFence(); From 7dd0e3d75646543af1dfae2583481172862579dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:22:26 +0200 Subject: [PATCH 10/23] Fix Vulkan swapchain semaphore reuse --- .../Graphics/Vulkan/VulkanSwapChain.cpp | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp index d7dfece0..3edddc9e 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp @@ -191,6 +191,9 @@ void CheckVulkanAvailableSwapChain(ElemHandle handle) auto graphicsDeviceData = GetVulkanGraphicsDeviceData(swapChainData->GraphicsDevice); SystemAssert(graphicsDeviceData); + auto commandQueueData = GetVulkanCommandQueueData(swapChainData->CommandQueue); + SystemAssert(commandQueueData); + // HACK: To Debug //vkDeviceWaitIdle(graphicsDeviceData->Device); @@ -232,12 +235,19 @@ void CheckVulkanAvailableSwapChain(ElemHandle handle) sizeChanged = true; } - AssertIfFailed(vkAcquireNextImageKHR(graphicsDeviceData->Device, swapChainData->DeviceObject, UINT64_MAX, swapChainData->BackBufferAcquireSemaphores[swapChainData->CurrentImageIndex], VK_NULL_HANDLE, &swapChainData->CurrentImageIndex)); - /* - AssertIfFailed(vkAcquireNextImageKHR(graphicsDeviceData->Device, swapChainData->DeviceObject, UINT64_MAX, VK_NULL_HANDLE, swapChainData->BackBufferAcquireFences[swapChainData->CurrentImageIndex], &swapChainData->CurrentImageIndex)); - vkWaitForFences(graphicsDeviceData->Device, 1, &swapChainData->BackBufferAcquireFences[swapChainData->CurrentImageIndex], true, UINT64_MAX); - vkResetFences(graphicsDeviceData->Device, 1, &swapChainData->BackBufferAcquireFences[swapChainData->CurrentImageIndex]); -*/ + auto frameIndex = swapChainData->CurrentFrameIndex; + auto acquireFence = swapChainData->BackBufferAcquireFences[frameIndex]; + + if (acquireFence.FenceValue > 0) + { + VulkanWaitForFenceOnCpu(acquireFence); + } + + auto acquireSemaphore = swapChainData->BackBufferAcquireSemaphores[frameIndex]; + AssertIfFailed(vkAcquireNextImageKHR(graphicsDeviceData->Device, swapChainData->DeviceObject, UINT64_MAX, acquireSemaphore, VK_NULL_HANDLE, &swapChainData->CurrentImageIndex)); + + commandQueueData->AcquireSemaphore = acquireSemaphore; + commandQueueData->PresentSemaphore = swapChainData->BackBufferPresentSemaphores[swapChainData->CurrentImageIndex]; swapChainData->PresentCalled = false; auto backBuffer = swapChainData->BackBufferTextures[swapChainData->CurrentImageIndex]; @@ -252,6 +262,7 @@ void CheckVulkanAvailableSwapChain(ElemHandle handle) }; swapChainData->UpdateHandler(&updateParameters, swapChainData->UpdatePayload); + swapChainData->CurrentFrameIndex = (frameIndex + 1) % VULKAN_MAX_SWAPCHAIN_BUFFERS; ResetInputsFrame(); /* if (!swapChainData->PresentCalled) @@ -446,6 +457,7 @@ ElemSwapChain VulkanCreateSwapChain(ElemCommandQueue commandQueue, ElemWindow wi { VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; AssertIfFailed(vkCreateSemaphore(graphicsDeviceData->Device, &semaphoreCreateInfo, nullptr, &swapChainData->BackBufferAcquireSemaphores[i])); + AssertIfFailed(vkCreateSemaphore(graphicsDeviceData->Device, &semaphoreCreateInfo, nullptr, &swapChainData->BackBufferPresentSemaphores[i])); } #ifdef _WIN32 @@ -484,6 +496,7 @@ void VulkanFreeSwapChain(ElemSwapChain swapChain) VulkanFreeGraphicsResource(swapChainData->BackBufferTextures[i], nullptr); vkDestroySemaphore(graphicsDeviceData->Device, swapChainData->BackBufferAcquireSemaphores[i], nullptr); + vkDestroySemaphore(graphicsDeviceData->Device, swapChainData->BackBufferPresentSemaphores[i], nullptr); } vkDestroySwapchainKHR(graphicsDeviceData->Device, swapChainData->DeviceObject, nullptr); @@ -528,6 +541,7 @@ void VulkanPresentSwapChain(ElemSwapChain swapChain) SystemAssert(commandQueueData); auto presentId = swapChainData->PresentId++; + auto presentSemaphore = swapChainData->BackBufferPresentSemaphores[swapChainData->CurrentImageIndex]; VkPresentIdKHR presentIdInfo = { VK_STRUCTURE_TYPE_PRESENT_ID_KHR }; presentIdInfo.swapchainCount = 1; @@ -535,15 +549,21 @@ void VulkanPresentSwapChain(ElemSwapChain swapChain) VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; presentInfo.waitSemaphoreCount = 1; - presentInfo.pWaitSemaphores = &commandQueueData->PresentSemaphore; + presentInfo.pWaitSemaphores = &presentSemaphore; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = &swapChainData->DeviceObject; presentInfo.pImageIndices = &swapChainData->CurrentImageIndex; presentInfo.pNext = &presentIdInfo; + swapChainData->BackBufferAcquireFences[swapChainData->CurrentFrameIndex] = + { + .CommandQueue = swapChainData->CommandQueue, + .FenceValue = commandQueueData->FenceValue + }; + AssertIfFailed(vkQueuePresentKHR(commandQueueData->DeviceObject, &presentInfo)); + swapChainData->PresentCalled = true; VulkanResetCommandAllocation(swapChainData->GraphicsDevice); VulkanProcessGraphicsResourceDeleteQueue(swapChainData->GraphicsDevice); } - From 36750c67fe6c934532fb9894f54f2ef9cd46bd25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:35:49 +0200 Subject: [PATCH 11/23] Synchronize swapchain acquire before all commands --- src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp index e9e564c1..94a13dfb 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp @@ -434,7 +434,7 @@ ElemFence VulkanExecuteCommandLists(ElemCommandQueue commandQueue, ElemCommandLi if (hasAcquireSemaphore) { auto acquireSemaphoreIndex = fencesToWaitCount; - submitStageMasks[acquireSemaphoreIndex] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + submitStageMasks[acquireSemaphoreIndex] = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; waitSemaphores[acquireSemaphoreIndex] = commandQueueData->AcquireSemaphore; waitSemaphoreValues[acquireSemaphoreIndex] = 0; } From 1f6c24071ea2d09cc204b66ed5a64e1f61c0f8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 07:43:35 +0200 Subject: [PATCH 12/23] Simplify Vulkan CPU timeline waits --- .../Graphics/Vulkan/VulkanCommandList.cpp | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp index 94a13dfb..ce856694 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp @@ -364,10 +364,10 @@ void VulkanCommitCommandList(ElemCommandList commandList) vkCmdCopyQueryPoolResults(commandListData->DeviceObject, graphicsDeviceData->QueryHeap.Storage->QueryHeap, index, - count, + count, graphicsDeviceData->QueryHeap.Storage->QueryHeapReadbackBuffer.Buffer, index * sizeof(uint64_t), - sizeof(uint64_t), + sizeof(uint64_t), VK_QUERY_RESULT_64_BIT);// | VK_QUERY_RESULT_WAIT_BIT);*/ //CreateVulkanGraphicsBufferBarrier(commandListData->DeviceObject, graphicsDeviceData->QueryHeap.Storage->QueryHeapReadbackBuffer.Buffer, false); @@ -542,25 +542,15 @@ void VulkanWaitForFenceOnCpu(ElemFence fence) auto graphicsDeviceData = GetVulkanGraphicsDeviceData(commandQueueToWaitData->GraphicsDevice); SystemAssert(graphicsDeviceData); - if (fence.FenceValue > commandQueueToWaitData->LastCompletedFenceValue) - { - uint64_t semaphoreValue; - vkGetSemaphoreCounterValue(graphicsDeviceData->Device, commandQueueToWaitData->Fence, &semaphoreValue); - - commandQueueToWaitData->LastCompletedFenceValue = SystemMax(commandQueueToWaitData->LastCompletedFenceValue, semaphoreValue); - } - if (fence.FenceValue > commandQueueToWaitData->LastCompletedFenceValue) { - // TODO: Activate it in a special debug mode - //SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Wait for fence on CPU..."); - VkSemaphoreWaitInfo waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO }; waitInfo.semaphoreCount = 1; waitInfo.pSemaphores = &commandQueueToWaitData->Fence; waitInfo.pValues = &fence.FenceValue; AssertIfFailed(vkWaitSemaphores(graphicsDeviceData->Device, &waitInfo, UINT64_MAX)); + commandQueueToWaitData->LastCompletedFenceValue = fence.FenceValue; } } @@ -660,4 +650,4 @@ void VulkanInsertGraphicsTimestamp(ElemCommandList commandList, ElemGraphicsTime commandListData->MaxResolveQueryIndex = SystemMax(timestampData->QueryHeapIndex, commandListData->MaxResolveQueryIndex); //vkCmdWriteTimestamp(commandListData->DeviceObject, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, graphicsDeviceData->QueryHeap.Storage->QueryHeap, timestampData->QueryHeapIndex); -} +} \ No newline at end of file From 6bb73298916fbb559a02974370e78584fc44a596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 08:21:27 +0200 Subject: [PATCH 13/23] Test Vulkan command allocator reuse --- src/Elemental/Common/Graphics/CommandAllocatorPool.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Elemental/Common/Graphics/CommandAllocatorPool.h b/src/Elemental/Common/Graphics/CommandAllocatorPool.h index 9138ec2c..9027b6ff 100644 --- a/src/Elemental/Common/Graphics/CommandAllocatorPool.h +++ b/src/Elemental/Common/Graphics/CommandAllocatorPool.h @@ -10,7 +10,7 @@ enum CommandAllocatorQueueType CommandAllocatorQueueType_Max = 3 }; -#define MAX_COMMANDALLOCATOR 3u +#define MAX_COMMANDALLOCATOR 8u #define MAX_COMMANDLIST 64u template @@ -49,4 +49,4 @@ template void ReleaseCommandListPoolItem(CommandListPoolItem* commandListPoolItem); template -void UpdateCommandAllocatorPoolItemFence(CommandAllocatorPoolItem* commandAllocatorPoolItem, ElemFence fence); +void UpdateCommandAllocatorPoolItemFence(CommandAllocatorPoolItem* commandAllocatorPoolItem, ElemFence fence); \ No newline at end of file From 734620e967f0967e61a44ca875766b996214aa69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 08:26:43 +0200 Subject: [PATCH 14/23] Revert Vulkan command allocator diagnostic --- src/Elemental/Common/Graphics/CommandAllocatorPool.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elemental/Common/Graphics/CommandAllocatorPool.h b/src/Elemental/Common/Graphics/CommandAllocatorPool.h index 9027b6ff..cd4673ec 100644 --- a/src/Elemental/Common/Graphics/CommandAllocatorPool.h +++ b/src/Elemental/Common/Graphics/CommandAllocatorPool.h @@ -10,7 +10,7 @@ enum CommandAllocatorQueueType CommandAllocatorQueueType_Max = 3 }; -#define MAX_COMMANDALLOCATOR 8u +#define MAX_COMMANDALLOCATOR 3u #define MAX_COMMANDLIST 64u template From 1998498ab1ebd3eac1c5abae985b4093b0a22fed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 08:46:34 +0200 Subject: [PATCH 15/23] Fix Vulkan swapchain composite alpha --- .../Common/Graphics/Vulkan/VulkanSwapChain.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp index 3edddc9e..4af95f7d 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanSwapChain.cpp @@ -96,13 +96,21 @@ VkSwapchainKHR CreateVulkanSwapChainObject(ElemGraphicsDevice graphicsDevice, Vk SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Present Mode count: %d", presentModeCount); - VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + VkCompositeAlphaFlagBitsKHR compositeAlpha; - if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) + if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) + { + compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + } + else if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) { compositeAlpha = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR; } - else if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) + else if (surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR) + { + compositeAlpha = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR; + } + else { compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; } @@ -566,4 +574,4 @@ void VulkanPresentSwapChain(ElemSwapChain swapChain) VulkanResetCommandAllocation(swapChainData->GraphicsDevice); VulkanProcessGraphicsResourceDeleteQueue(swapChainData->GraphicsDevice); -} +} \ No newline at end of file From d5ee0748e18880017a2e5a51c3ad9e8cee5a149a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 09:11:47 +0200 Subject: [PATCH 16/23] Preserve swapchain alpha in renderer compositing --- samples/Demos/01-Renderer/main.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/samples/Demos/01-Renderer/main.c b/samples/Demos/01-Renderer/main.c index 56f20719..ebd452a2 100644 --- a/samples/Demos/01-Renderer/main.c +++ b/samples/Demos/01-Renderer/main.c @@ -196,7 +196,7 @@ void InitSample(void* payload) ElemSetGraphicsOptions(&(ElemGraphicsOptions) { .EnableDebugLayer = applicationPayload->AppSettings.GpuDebug, .EnableGpuValidation = false, - .EnableDebugBarrierInfo = false, + .EnableDebugBarrierInfo = false, .EnableDebugStablePowerState = true, .PreferVulkan = applicationPayload->AppSettings.PreferVulkan }); @@ -264,6 +264,9 @@ void InitSample(void* payload) .BlendOperation = ElemGraphicsBlendOperation_Add, .SourceBlendFactor = ElemGraphicsBlendFactor_SourceAlpha, .DestinationBlendFactor = ElemGraphicsBlendFactor_InverseSourceAlpha, + .BlendOperationAlpha = ElemGraphicsBlendOperation_Add, + .SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero, + .DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_One, }}, .Length = 1 }, }); @@ -277,6 +280,9 @@ void InitSample(void* payload) .BlendOperation = ElemGraphicsBlendOperation_Add, .SourceBlendFactor = ElemGraphicsBlendFactor_SourceAlpha, .DestinationBlendFactor = ElemGraphicsBlendFactor_InverseSourceAlpha, + .BlendOperationAlpha = ElemGraphicsBlendOperation_Add, + .SourceBlendFactorAlpha = ElemGraphicsBlendFactor_Zero, + .DestinationBlendFactorAlpha = ElemGraphicsBlendFactor_One, }}, .Length = 1 }, }); @@ -597,7 +603,6 @@ void UpdateSwapChain(const ElemSwapChainUpdateParameters* updateParameters, void ElemGraphicsResourceBarrier(commandList, applicationPayload->RenderTargetTextureReadDescriptor, NULL); ElemGraphicsResourceBarrier(commandList, applicationPayload->DebugUIData.UIRenderTargetTextureReadDescriptor, NULL); - // TODO: Refactor that ElemInsertGraphicsTimestamp(commandList, applicationPayload->GpuTimestampData.TonemapTimestamp.StartGpuTimestamp); applicationPayload->GpuTimestampData.TonemapTimestamp.IsActive = true; @@ -687,4 +692,4 @@ int main(int argc, const char* argv[]) .Payload = &payload }); -} +} \ No newline at end of file From 1f6f2ab7b39cf81edec851f352011831b991433d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 10:22:12 +0200 Subject: [PATCH 17/23] Test Vulkan base validation layer --- .../Graphics/Vulkan/VulkanGraphicsDevice.cpp | 707 +----------------- 1 file changed, 3 insertions(+), 704 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index a0afbe0f..15448b7a 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -121,7 +121,9 @@ void InitVulkan() validationFeatures.enabledValidationFeatureCount = currentEnabledValidationFeaturesIndex; validationFeatures.pEnabledValidationFeatures = enabledValidationFeatures.Pointer; - createInfo.pNext = &validationFeatures; + // Test B: keep VK_LAYER_KHRONOS_validation enabled without additional validation features. + // This isolates the base validation layer from Best Practices / Synchronization Validation. + createInfo.pNext = nullptr; AssertIfFailed(vkCreateInstance(&createInfo, nullptr, &VulkanInstance)); instanceCreated = true; @@ -165,706 +167,3 @@ void InitVulkan() AssertIfFailed(vkCreateDebugReportCallbackEXT(VulkanInstance, &debugCreateInfo, 0, &vulkanDebugCallback)); } } - -void InitVulkanGraphicsDeviceMemory() -{ - if (!VulkanGraphicsMemoryArena.Storage) - { - // TODO: To Review - VulkanGraphicsMemoryArena = SystemAllocateMemoryArena(256 * 1024 * 1024); - vulkanGraphicsDevicePool = SystemCreateDataPool(VulkanGraphicsMemoryArena, VULKAN_MAX_DEVICES); - - InitVulkan(); - } -} - -VkCompareOp ConvertToVulkanCompareFunction(ElemGraphicsCompareFunction compareFunction) -{ - switch (compareFunction) - { - case ElemGraphicsCompareFunction_Never: - return VK_COMPARE_OP_NEVER; - - case ElemGraphicsCompareFunction_Less: - return VK_COMPARE_OP_LESS; - - case ElemGraphicsCompareFunction_Equal: - return VK_COMPARE_OP_EQUAL; - - case ElemGraphicsCompareFunction_LessEqual: - return VK_COMPARE_OP_LESS_OR_EQUAL; - - case ElemGraphicsCompareFunction_Greater: - return VK_COMPARE_OP_GREATER; - - case ElemGraphicsCompareFunction_NotEqual: - return VK_COMPARE_OP_NOT_EQUAL; - - case ElemGraphicsCompareFunction_GreaterEqual: - return VK_COMPARE_OP_GREATER_OR_EQUAL; - - case ElemGraphicsCompareFunction_Always: - return VK_COMPARE_OP_ALWAYS; - } -} - -ElemGraphicsDeviceInfo VulkanConstructGraphicsDeviceInfo(MemoryArena memoryArena, VkPhysicalDeviceProperties deviceProperties, VkPhysicalDeviceMemoryProperties deviceMemoryProperties) -{ - auto deviceName = ReadOnlySpan(deviceProperties.deviceName); - auto destinationDeviceName = SystemPushArray(memoryArena, deviceName.Length); - SystemCopyBuffer(destinationDeviceName, deviceName); - - return - { - .DeviceName = destinationDeviceName.Pointer, - .GraphicsApi = ElemGraphicsApi_Vulkan, - .DeviceId = deviceProperties.deviceID, - .AvailableMemory = deviceMemoryProperties.memoryHeaps[0].size - }; -} - -VulkanDescriptorSet* CreateVulkanDescriptorSet(MemoryArena memoryArena, VkDevice graphicsDevice, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorCount) -{ - VkDescriptorPoolSize poolSize - { - .type = VK_DESCRIPTOR_TYPE_MUTABLE_EXT, - .descriptorCount = descriptorCount - }; - - VkDescriptorPoolCreateInfo createInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; - createInfo.poolSizeCount = 1; - createInfo.pPoolSizes = &poolSize; - createInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT; - createInfo.maxSets = 1; - - VkDescriptorPool descriptorPool; - AssertIfFailed(vkCreateDescriptorPool(graphicsDevice, &createInfo, nullptr, &descriptorPool)); - - VkDescriptorSetVariableDescriptorCountAllocateInfo descriptorSetLayoutCount = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO }; - descriptorSetLayoutCount.descriptorSetCount = 1; - descriptorSetLayoutCount.pDescriptorCounts = &descriptorCount; - - VkDescriptorSetAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; - allocateInfo.pSetLayouts = &descriptorSetLayout; - allocateInfo.descriptorSetCount = 1; - allocateInfo.descriptorPool = descriptorPool; - allocateInfo.pNext = &descriptorSetLayoutCount; - - VkDescriptorSet descriptorSet; - AssertIfFailed(vkAllocateDescriptorSets(graphicsDevice, &allocateInfo, &descriptorSet)); - - auto result = SystemPushStruct(memoryArena); - result->DescriptorPool = descriptorPool; - result->DescriptorSet = descriptorSet; - - return result; -} - -void FreeVulkanDescriptorSet(VkDevice device, const VulkanDescriptorSet* descriptorSet) -{ - SystemAssert(descriptorSet); - vkDestroyDescriptorPool(device, descriptorSet->DescriptorPool, nullptr); -} - -VulkanDescriptorHeap CreateVulkanDescriptorHeap(MemoryArena memoryArena, VkDevice graphicsDevice, VkDescriptorSetLayout descriptorSetLayout, uint32_t length) -{ - auto descriptorSet = CreateVulkanDescriptorSet(memoryArena, graphicsDevice, descriptorSetLayout, VULKAN_MAX_RESOURCES); - - auto descriptorStorage = SystemPushStruct(memoryArena); - descriptorStorage->DescriptorSet = descriptorSet; - descriptorStorage->Items = SystemPushArray(memoryArena, length); - descriptorStorage->CurrentIndex = 0; - descriptorStorage->FreeListIndex = UINT32_MAX; - - return - { - .Storage = descriptorStorage - }; -} - -void FreeVulkanDescriptorHeap(VkDevice device, const VulkanDescriptorHeap descriptorHeap) -{ - SystemAssert(descriptorHeap.Storage); - vkDestroyDescriptorPool(device, descriptorHeap.Storage->DescriptorSet->DescriptorPool, nullptr); -} - -uint32_t CreateVulkanDescriptorHandle(VulkanDescriptorHeap descriptorHeap) -{ - SystemAssert(descriptorHeap.Storage); - - auto storage = descriptorHeap.Storage; - auto descriptorIndex = UINT32_MAX; - - do - { - if (storage->FreeListIndex == UINT32_MAX) - { - descriptorIndex = UINT32_MAX; - break; - } - - descriptorIndex = storage->FreeListIndex; - } while (!SystemAtomicCompareExchange(storage->FreeListIndex, descriptorIndex, storage->Items[storage->FreeListIndex].Next)); - - if (descriptorIndex == UINT32_MAX) - { - descriptorIndex = SystemAtomicAdd(storage->CurrentIndex, 1); - } - - return descriptorIndex; -} - -void FreeVulkanDescriptorHandle(VulkanDescriptorHeap descriptorHeap, uint32_t handle) -{ - auto storage = descriptorHeap.Storage; - - do - { - storage->Items[handle].Next = storage->FreeListIndex; - } while (!SystemAtomicCompareExchange(storage->FreeListIndex, storage->FreeListIndex, handle)); -} - -VulkanQueryHeap CreateVulkanQueryHeap(ElemGraphicsDevice graphicsDevice, MemoryArena memoryArena, VkQueryType type, uint32_t length) -{ - auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); - SystemAssert(graphicsDeviceData); - - VkQueryPoolCreateInfo createInfo = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO }; - createInfo.queryType = type; - createInfo.queryCount = length; - - VkQueryPool queryPool; - AssertIfFailed(vkCreateQueryPool(graphicsDeviceData->Device, &createInfo, nullptr, &queryPool)); - vkResetQueryPool(graphicsDeviceData->Device, queryPool, 0, length); - - auto queryHeapReadbackBuffer = CreateVulkanGraphicsBufferCpu(graphicsDevice, ElemGraphicsHeapType_Readback, sizeof(uint64_t) * VULKAN_MAX_QUERYHEAP_ITEMS, "QueryHeapReadbackBuffer"); - - uint8_t* cpuPointer = nullptr; - AssertIfFailed(vkMapMemory(graphicsDeviceData->Device, queryHeapReadbackBuffer.DeviceMemory, 0, VK_WHOLE_SIZE, 0, (void**)&cpuPointer)); - - auto descriptorStorage = SystemPushStruct(memoryArena); - descriptorStorage->QueryHeap = queryPool; - descriptorStorage->QueryHeapReadbackBuffer = queryHeapReadbackBuffer; - descriptorStorage->ReadbackCpuPointer = cpuPointer; - descriptorStorage->Type = type; - descriptorStorage->Items = SystemPushArray(memoryArena, length); - descriptorStorage->CurrentIndex = 0; - descriptorStorage->InitializationIndex = 0; - descriptorStorage->FreeListIndex = UINT32_MAX; - - return - { - .Storage = descriptorStorage - }; -} - -void FreeVulkanQueryHeap(VkDevice device, VulkanQueryHeap queryHeap) -{ - SystemAssert(queryHeap.Storage); - - vkDestroyQueryPool(device, queryHeap.Storage->QueryHeap, nullptr); - vkDestroyBuffer(device, queryHeap.Storage->QueryHeapReadbackBuffer.Buffer, nullptr); - vkFreeMemory(device, queryHeap.Storage->QueryHeapReadbackBuffer.DeviceMemory, nullptr); -} - -uint32_t CreateVulkanQueryHeapIndex(VulkanQueryHeap queryHeap) -{ - SystemAssert(queryHeap.Storage); - - auto storage = queryHeap.Storage; - auto index = UINT32_MAX; - - do - { - if (storage->FreeListIndex == UINT32_MAX) - { - index = UINT32_MAX; - break; - } - - index = storage->FreeListIndex; - } while (!SystemAtomicCompareExchange(storage->FreeListIndex, index, storage->Items[storage->FreeListIndex].Next)); - - if (index == UINT32_MAX) - { - index = SystemAtomicAdd(storage->CurrentIndex, 1); - } - - return index; -} - -void FreeVulkanQueryHeapIndex(VulkanQueryHeap queryHeap, uint32_t index) -{ - auto storage = queryHeap.Storage; - - do - { - storage->Items[index].Next = storage->FreeListIndex; - } while (!SystemAtomicCompareExchange(storage->FreeListIndex, storage->FreeListIndex, index)); -} - -bool VulkanCheckGraphicsDeviceCompatibility(VkPhysicalDevice device) -{ - VkPhysicalDeviceFeatures deviceFeatures; - vkGetPhysicalDeviceFeatures(device, &deviceFeatures); - - VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR }; - - VkPhysicalDeviceMeshShaderFeaturesEXT meshShaderFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT }; - meshShaderFeatures.pNext = &presentIdFeatures; - - VkPhysicalDeviceFeatures2 features2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR }; - features2.pNext = &meshShaderFeatures; - - vkGetPhysicalDeviceFeatures2(device, &features2); - - if (meshShaderFeatures.meshShader && presentIdFeatures.presentId) - { - return true; - } - - return false; -} - -VulkanGraphicsDeviceData* GetVulkanGraphicsDeviceData(ElemGraphicsDevice graphicsDevice) -{ - return SystemGetDataPoolItem(vulkanGraphicsDevicePool, graphicsDevice); -} - -VulkanGraphicsDeviceDataFull* GetVulkanGraphicsDeviceDataFull(ElemGraphicsDevice graphicsDevice) -{ - return SystemGetDataPoolItemFull(vulkanGraphicsDevicePool, graphicsDevice); -} - -VkDescriptorSetLayout CreateVulkanDescriptorSetLayout(ElemGraphicsDevice graphicsDevice, VkDescriptorType* descriptorTypes, uint32_t descriptorTypeCount) -{ - auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); - SystemAssert(graphicsDeviceData); - - VkMutableDescriptorTypeListEXT descriptorSetTypes = - { - .descriptorTypeCount = descriptorTypeCount, - .pDescriptorTypes = descriptorTypes - }; - - VkMutableDescriptorTypeCreateInfoEXT mutableDescriptorTypeCreateInfo = { VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT }; - mutableDescriptorTypeCreateInfo.pMutableDescriptorTypeLists = &descriptorSetTypes; - mutableDescriptorTypeCreateInfo.mutableDescriptorTypeListCount = 1; - - VkDescriptorBindingFlags descriptorBindingFlags[] = - { - VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT - }; - - VkDescriptorSetLayoutBindingFlagsCreateInfo descriptorBindingFlagsCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO }; - descriptorBindingFlagsCreateInfo.bindingCount = 1; - descriptorBindingFlagsCreateInfo.pBindingFlags = descriptorBindingFlags; - descriptorBindingFlagsCreateInfo.pNext = &mutableDescriptorTypeCreateInfo; - - VkDescriptorSetLayoutBinding descriptorBinding = - { - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_MUTABLE_EXT, - .descriptorCount = VULKAN_MAX_RESOURCES, - .stageFlags = VK_SHADER_STAGE_ALL - }; - - VkDescriptorSetLayoutCreateInfo descriptorSetCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; - descriptorSetCreateInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT; - descriptorSetCreateInfo.bindingCount = 1; - descriptorSetCreateInfo.pBindings = &descriptorBinding; - descriptorSetCreateInfo.pNext = &descriptorBindingFlagsCreateInfo; - - VkDescriptorSetLayout result; - AssertIfFailed(vkCreateDescriptorSetLayout(graphicsDeviceData->Device, &descriptorSetCreateInfo, 0, &result)); - - return result; -} - -void CreateVulkanPipelineLayout(ElemGraphicsDevice graphicsDevice) -{ - auto stackMemoryArena = SystemGetStackMemoryArena(); - - auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); - SystemAssert(graphicsDeviceData); - - auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(graphicsDevice); - SystemAssert(graphicsDeviceDataFull); - - VkPipelineLayoutCreateInfo layoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; - - VkPushConstantRange push_constant; - push_constant.offset = 0; - push_constant.size = 24 * 4; - push_constant.stageFlags = VK_SHADER_STAGE_ALL; - - layoutCreateInfo.pPushConstantRanges = &push_constant; - layoutCreateInfo.pushConstantRangeCount = 1; - - // TODO: Recheck those - VkDescriptorType resourceDescriptorTypes[] = { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR }; - graphicsDeviceDataFull->ResourceDescriptorSetLayout = CreateVulkanDescriptorSetLayout(graphicsDevice, resourceDescriptorTypes, ARRAYSIZE(resourceDescriptorTypes)); - - VkDescriptorType samplerDescriptorTypes[] = { VK_DESCRIPTOR_TYPE_SAMPLER }; - graphicsDeviceDataFull->SamplerDescriptorSetLayout = CreateVulkanDescriptorSetLayout(graphicsDevice, samplerDescriptorTypes, ARRAYSIZE(samplerDescriptorTypes)); - - VkDescriptorSetLayout descriptorSetLayouts[] { graphicsDeviceDataFull->ResourceDescriptorSetLayout, graphicsDeviceDataFull->SamplerDescriptorSetLayout }; - layoutCreateInfo.pSetLayouts = descriptorSetLayouts; - layoutCreateInfo.setLayoutCount = ARRAYSIZE(descriptorSetLayouts); - - AssertIfFailed(vkCreatePipelineLayout(graphicsDeviceData->Device, &layoutCreateInfo, 0, &graphicsDeviceData->PipelineLayout)); -} - -void VulkanSetGraphicsOptions(const ElemGraphicsOptions* options) -{ - SystemAssert(options); - - if (options->EnableDebugLayer) - { - VulkanDebugLayerEnabled = options->EnableDebugLayer; - } - - if (options->EnableGpuValidation) - { - vulkanDebugGpuValidationEnabled = options->EnableGpuValidation; - } - - VulkanDebugBarrierInfoEnabled = options->EnableDebugBarrierInfo; -} - -ElemGraphicsDeviceInfoSpan VulkanGetAvailableGraphicsDevices() -{ - InitVulkanGraphicsDeviceMemory(); - - auto stackMemoryArena = SystemGetStackMemoryArena(); - auto deviceInfos = SystemPushArray(stackMemoryArena, VULKAN_MAX_DEVICES); - auto currentDeviceInfoIndex = 0u; - - uint32_t deviceCount = VULKAN_MAX_DEVICES; - AssertIfFailed(vkEnumeratePhysicalDevices(VulkanInstance, &deviceCount, nullptr)); - - auto devices = SystemPushArray(stackMemoryArena, deviceCount); - AssertIfFailed(vkEnumeratePhysicalDevices(VulkanInstance, &deviceCount, devices.Pointer)); - - for (uint32_t i = 0; i < deviceCount; i++) - { - VkPhysicalDeviceProperties deviceProperties; - vkGetPhysicalDeviceProperties(devices[i], &deviceProperties); - - VkPhysicalDeviceMemoryProperties deviceMemoryProperties; - vkGetPhysicalDeviceMemoryProperties(devices[i], &deviceMemoryProperties); - - if (VulkanCheckGraphicsDeviceCompatibility(devices[i])) - { - deviceInfos[currentDeviceInfoIndex++] = VulkanConstructGraphicsDeviceInfo(stackMemoryArena, deviceProperties, deviceMemoryProperties); - } - } - - return - { - .Items = deviceInfos.Pointer, - .Length = currentDeviceInfoIndex - }; -} - -ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* options) -{ - // TODO: Review features selection - InitVulkanGraphicsDeviceMemory(); - - auto stackMemoryArena = SystemGetStackMemoryArena(); - - VkPhysicalDevice physicalDevice = {}; - VkPhysicalDeviceProperties deviceProperties = {}; - VkPhysicalDeviceMemoryProperties deviceMemoryProperties {}; - auto foundDevice = false; - - uint32_t deviceCount; - AssertIfFailed(vkEnumeratePhysicalDevices(VulkanInstance, &deviceCount, nullptr)); - - auto devices = SystemPushArray(stackMemoryArena, deviceCount); - AssertIfFailed(vkEnumeratePhysicalDevices(VulkanInstance, &deviceCount, devices.Pointer)); - - for (uint32_t i = 0; i < deviceCount; i++) - { - if (VulkanCheckGraphicsDeviceCompatibility(devices[i])) - { - vkGetPhysicalDeviceProperties(devices[i], &deviceProperties); - vkGetPhysicalDeviceMemoryProperties(devices[i], &deviceMemoryProperties); - - if ((options != nullptr && options->DeviceId == deviceProperties.deviceID) || options == nullptr || options->DeviceId == 0) - { - physicalDevice = devices[i]; - foundDevice = true; - break; - } - } - } - - SystemAssertReturnNullHandle(foundDevice); - - uint32_t queueFamilyCount = 0; - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); - - auto queueFamilies = SystemPushArray(stackMemoryArena, queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.Pointer); - - VkDeviceQueueCreateInfo queueCreateInfos[3]; - uint32_t renderCommandQueueIndex = UINT32_MAX; - uint32_t computeCommandQueueIndex = UINT32_MAX; - uint32_t copyCommandQueueIndex = UINT32_MAX; - float queuePriority[3] = { 1.0f, 1.0f, 1.0f }; - - for (uint32_t i = 0; i < 3; i++) - { - uint32_t queueCount = 1; - - if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT && renderCommandQueueIndex == UINT32_MAX) - { - renderCommandQueueIndex = i; - queueCount = SystemMin(queueFamilies[i].queueCount, 3u); - } - else if (queueFamilies[i].queueFlags & VK_QUEUE_COMPUTE_BIT && computeCommandQueueIndex == UINT32_MAX) - { - computeCommandQueueIndex = i; - queueCount = SystemMin(queueFamilies[i].queueCount, 2u); - } - else if (queueFamilies[i].queueFlags & VK_QUEUE_TRANSFER_BIT && copyCommandQueueIndex == UINT32_MAX) - { - copyCommandQueueIndex = i; - queueCount = SystemMin(queueFamilies[i].queueCount, 2u); - } - else - { - SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Wrong queue type."); - } - - VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; - queueCreateInfo.pQueuePriorities = queuePriority; - queueCreateInfo.queueCount = queueCount; - queueCreateInfo.queueFamilyIndex = i; - - queueCreateInfos[i] = queueCreateInfo; - } - - int32_t gpuMemoryTypeIndex = -1; - int32_t gpuUploadMemoryTypeIndex = -1; - int32_t readBackMemoryTypeIndex = -1; - int32_t uploadMemoryTypeIndex = -1; - - for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++) - { - auto memoryPropertyFlags = deviceMemoryProperties.memoryTypes[i].propertyFlags; - - if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && - (memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) - { - gpuMemoryTypeIndex = i; - } - if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && - (memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) - { - gpuUploadMemoryTypeIndex = i; - } - else if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) && - (memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && - (memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)) - { - readBackMemoryTypeIndex = i; - } - - else if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && - (memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) - { - uploadMemoryTypeIndex = i; - } - } - - SystemAssert(gpuMemoryTypeIndex != -1 && gpuUploadMemoryTypeIndex != -1 && readBackMemoryTypeIndex != -1); - - VkDeviceCreateInfo createInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; - createInfo.queueCreateInfoCount = 3; - createInfo.pQueueCreateInfos = queueCreateInfos; - - const char* extensions[] = - { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, // TODO: To review - VK_KHR_PRESENT_ID_EXTENSION_NAME, // TODO: To review - VK_KHR_PRESENT_WAIT_EXTENSION_NAME, // TODO: To review - VK_EXT_MESH_SHADER_EXTENSION_NAME, - VK_EXT_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME, - VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, - VK_KHR_RAY_QUERY_EXTENSION_NAME, - VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, - VK_EXT_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_EXTENSION_NAME - }; - - createInfo.ppEnabledExtensionNames = extensions; - createInfo.enabledExtensionCount = ARRAYSIZE(extensions); - - VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT dynamicUnusedFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_FEATURES_EXT }; - dynamicUnusedFeatures.dynamicRenderingUnusedAttachments = true; - - VkPhysicalDeviceFeatures2 features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 }; - features.features.shaderInt16 = true; - features.features.shaderInt64 = true; - features.features.pipelineStatisticsQuery = true; - features.features.fillModeNonSolid = true; - features.features.samplerAnisotropy = true; - - VkPhysicalDeviceVulkan12Features features12 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES }; - features12.timelineSemaphore = true; - features12.runtimeDescriptorArray = true; - features12.descriptorIndexing = true; - features12.descriptorBindingVariableDescriptorCount = true; - features12.descriptorBindingPartiallyBound = true; - features12.descriptorBindingSampledImageUpdateAfterBind = true; - features12.descriptorBindingStorageBufferUpdateAfterBind = true; - features12.descriptorBindingStorageImageUpdateAfterBind = true; - features12.shaderSampledImageArrayNonUniformIndexing = true; - features12.separateDepthStencilLayouts = true; - features12.hostQueryReset = true; - features12.shaderInt8 = true; - features12.bufferDeviceAddress = true; - - if (VulkanDebugLayerEnabled) - { - features12.bufferDeviceAddressCaptureReplay = true; - } - - VkPhysicalDeviceVulkan13Features features13 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES }; - features13.maintenance4 = true; - features13.synchronization2 = true; - features13.dynamicRendering = true; - features13.shaderDemoteToHelperInvocation = true; - - VkPhysicalDeviceVulkan14Features features14 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES }; - features14.maintenance5 = true; - - VkPhysicalDeviceMeshShaderFeaturesEXT meshFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT }; - meshFeatures.meshShader = true; - meshFeatures.meshShaderQueries = true; - - VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT mutableDescriptorFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT }; - mutableDescriptorFeatures.mutableDescriptorType = true; - - VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR }; - presentIdFeatures.presentId = true; - - VkPhysicalDevicePresentWaitFeaturesKHR presentWaitFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR }; - presentWaitFeatures.presentWait = true; - - VkPhysicalDeviceRayQueryFeaturesKHR rayQueriesFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR }; - rayQueriesFeatures.rayQuery = true; - - VkPhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR }; - accelerationStructureFeatures.accelerationStructure = true; - - createInfo.pNext = &features; - features.pNext = &features12; - features12.pNext = &features13; - features13.pNext = &features14; - features14.pNext = &presentIdFeatures; - presentIdFeatures.pNext = &presentWaitFeatures; - presentWaitFeatures.pNext = &meshFeatures; - meshFeatures.pNext = &mutableDescriptorFeatures; - mutableDescriptorFeatures.pNext = &rayQueriesFeatures; - rayQueriesFeatures.pNext = &accelerationStructureFeatures; - accelerationStructureFeatures.pNext = &dynamicUnusedFeatures; - - VkDevice device = nullptr; - AssertIfFailedReturnNullHandle(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device)); - volkLoadDevice(device); - - auto memoryArena = SystemAllocateMemoryArena(); - - auto handle = SystemAddDataPoolItem(vulkanGraphicsDevicePool, { - .Device = device, - .MemoryArena = memoryArena - }); - - SystemAddDataPoolItemFull(vulkanGraphicsDevicePool, handle, { - .PhysicalDevice = physicalDevice, - .DeviceProperties = deviceProperties, - .DeviceMemoryProperties = deviceMemoryProperties, - .RenderCommandQueueIndex = renderCommandQueueIndex, - .ComputeCommandQueueIndex = computeCommandQueueIndex, - .CopyCommandQueueIndex = copyCommandQueueIndex, - .GpuMemoryTypeIndex = (uint32_t)gpuMemoryTypeIndex, - .GpuUploadMemoryTypeIndex = (uint32_t)gpuUploadMemoryTypeIndex, - .ReadBackMemoryTypeIndex = (uint32_t)readBackMemoryTypeIndex, - .UploadMemoryTypeIndex = (uint32_t)uploadMemoryTypeIndex - }); - - CreateVulkanPipelineLayout(handle); - - auto graphicsDeviceData = GetVulkanGraphicsDeviceData(handle); - auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(handle); - - graphicsDeviceData->ResourceDescriptorHeap = CreateVulkanDescriptorHeap(memoryArena, graphicsDeviceData->Device, graphicsDeviceDataFull->ResourceDescriptorSetLayout, VULKAN_MAX_RESOURCES); - graphicsDeviceData->SamplerDescriptorHeap = CreateVulkanDescriptorHeap(memoryArena, graphicsDeviceData->Device, graphicsDeviceDataFull->SamplerDescriptorSetLayout, VULKAN_MAX_SAMPLERS); - - // TODO: This need to be checked. We don't know how many max threads will use this. Maybe we can allocate for MAX_CONC_THREADS variable of param (that can be overriden) - graphicsDeviceData->UploadBufferPools = SystemPushArray*>(VulkanGraphicsMemoryArena, MAX_UPLOAD_BUFFERS); - graphicsDeviceData->QueryHeap = CreateVulkanQueryHeap(handle, memoryArena, VK_QUERY_TYPE_TIMESTAMP, VULKAN_MAX_QUERYHEAP_ITEMS); - - return handle; -} - -void VulkanFreeGraphicsDevice(ElemGraphicsDevice graphicsDevice) -{ - SystemAssert(graphicsDevice != ELEM_HANDLE_NULL); - - auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); - SystemAssert(graphicsDeviceData); - - auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(graphicsDevice); - SystemAssert(graphicsDeviceDataFull); - - for (uint32_t i = 0; i < graphicsDeviceData->UploadBufferPools.Length; i++) - { - auto bufferPool = graphicsDeviceData->UploadBufferPools[i]; - - if (bufferPool) - { - for (uint32_t j = 0; j < MAX_UPLOAD_BUFFERS; j++) - { - auto uploadBuffer = &bufferPool->UploadBuffers[j]; - - if (uploadBuffer->Buffer.Buffer) - { - vkDestroyBuffer(graphicsDeviceData->Device, uploadBuffer->Buffer.Buffer, nullptr); - vkFreeMemory(graphicsDeviceData->Device, uploadBuffer->Buffer.DeviceMemory, nullptr); - - uploadBuffer->Buffer = {}; - *uploadBuffer = {}; - } - } - - *bufferPool = {}; - } - } - - FreeVulkanDescriptorHeap(graphicsDeviceData->Device, graphicsDeviceData->ResourceDescriptorHeap); - FreeVulkanDescriptorHeap(graphicsDeviceData->Device, graphicsDeviceData->SamplerDescriptorHeap); - FreeVulkanQueryHeap(graphicsDeviceData->Device, graphicsDeviceData->QueryHeap); - - vkDestroyDescriptorSetLayout(graphicsDeviceData->Device, graphicsDeviceDataFull->ResourceDescriptorSetLayout, nullptr); - vkDestroyDescriptorSetLayout(graphicsDeviceData->Device, graphicsDeviceDataFull->SamplerDescriptorSetLayout, nullptr); - vkDestroyPipelineLayout(graphicsDeviceData->Device, graphicsDeviceData->PipelineLayout, nullptr); - vkDestroyDevice(graphicsDeviceData->Device, nullptr); - - SystemRemoveDataPoolItem(vulkanGraphicsDevicePool, graphicsDevice); - SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Releasing Vulkan"); -} - -ElemGraphicsDeviceInfo VulkanGetGraphicsDeviceInfo(ElemGraphicsDevice graphicsDevice) -{ - SystemAssert(graphicsDevice != ELEM_HANDLE_NULL); - - auto stackMemoryArena = SystemGetStackMemoryArena(); - auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(graphicsDevice); - SystemAssert(graphicsDeviceDataFull); - - return VulkanConstructGraphicsDeviceInfo(stackMemoryArena, graphicsDeviceDataFull->DeviceProperties, graphicsDeviceDataFull->DeviceMemoryProperties); -} From 5ffe9faba84f6bfebfd5b734cda35ede1b2b1cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 10:22:29 +0200 Subject: [PATCH 18/23] TEMP --- .../Graphics/Vulkan/VulkanGraphicsDevice.cpp | 170 +----------------- 1 file changed, 1 insertion(+), 169 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index 15448b7a..e3fbbe0e 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -1,169 +1 @@ -#include "VulkanGraphicsDevice.h" -#include "VulkanConfig.h" -#include "SystemDataPool.h" -#include "SystemFunctions.h" -#include "SystemLogging.h" -#include "SystemMemory.h" - -MemoryArena VulkanGraphicsMemoryArena; -SystemDataPool vulkanGraphicsDevicePool; - -bool VulkanDebugLayerEnabled = false; -bool vulkanDebugGpuValidationEnabled = false; -bool VulkanDebugBarrierInfoEnabled = false; -VkInstance VulkanInstance = nullptr; -VkDebugReportCallbackEXT vulkanDebugCallback = nullptr; - -VkBool32 VKAPI_CALL VulkanDebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t, const char*, const char* pMessage, void*) -{ - auto messageType = ElemLogMessageType_Debug; - - if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) - { - messageType = ElemLogMessageType_Error; - } - else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) - { - messageType = ElemLogMessageType_Warning; - } - - if (SystemFindSubString(pMessage, "VK_EXT_mutable_descriptor_type") != -1) - { - return VK_FALSE; - } - - if (SystemFindSubString(pMessage, "BestPractices-PushConstants") != -1) - { - return VK_FALSE; - } - - SystemLogMessage(messageType, ElemLogMessageCategory_Graphics, "%s", pMessage); - - return VK_FALSE; -} - -void InitVulkan() -{ - auto stackMemoryArena = SystemGetStackMemoryArena(); - - AssertIfFailed(volkInitialize()); - SystemAssert(volkGetInstanceVersion() >= VK_API_VERSION_1_4); - - VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; - appInfo.apiVersion = VK_API_VERSION_1_4; - - VkInstanceCreateInfo createInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; - createInfo.pApplicationInfo = &appInfo; - - auto isSdkInstalled = false; - auto instanceCreated = false; - - uint32_t instanceLayerCount; - vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr); - - auto instanceLayers = SystemPushArray(stackMemoryArena, instanceLayerCount); - vkEnumerateInstanceLayerProperties(&instanceLayerCount, instanceLayers.Pointer); - - for (uint32_t i = 0; i < instanceLayerCount; i++) - { - // TODO: Use a system function for that - if (strcmp(instanceLayers[i].layerName, "VK_LAYER_KHRONOS_validation") == 0) - { - isSdkInstalled = true; - break; - } - } - - // TODO: Enumerate instance extensions like in the cube sample to see if the system - // is compatible with the current surface extension - - if (VulkanDebugLayerEnabled) - { - if (isSdkInstalled) - { - SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Init Vulkan Debug Mode."); - - const char* layers[] = - { - "VK_LAYER_KHRONOS_validation" - }; - - createInfo.ppEnabledLayerNames = layers; - createInfo.enabledLayerCount = ARRAYSIZE(layers); - - const char* extensions[] = - { - VK_EXT_DEBUG_REPORT_EXTENSION_NAME, - VK_EXT_DEBUG_UTILS_EXTENSION_NAME, - VK_KHR_SURFACE_EXTENSION_NAME, - #ifdef _WIN32 - VK_KHR_WIN32_SURFACE_EXTENSION_NAME - #elif __linux__ - VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME - #endif - }; - - createInfo.ppEnabledExtensionNames = extensions; - createInfo.enabledExtensionCount = ARRAYSIZE(extensions); - - auto enabledValidationFeatures = SystemPushArray(stackMemoryArena, 3); - auto currentEnabledValidationFeaturesIndex = 0u; - - enabledValidationFeatures[currentEnabledValidationFeaturesIndex++] = VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT; - enabledValidationFeatures[currentEnabledValidationFeaturesIndex++] = VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT; - - if (vulkanDebugGpuValidationEnabled) - { - enabledValidationFeatures[currentEnabledValidationFeaturesIndex++] = VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT; - } - - VkValidationFeaturesEXT validationFeatures = { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT }; - validationFeatures.enabledValidationFeatureCount = currentEnabledValidationFeaturesIndex; - validationFeatures.pEnabledValidationFeatures = enabledValidationFeatures.Pointer; - - // Test B: keep VK_LAYER_KHRONOS_validation enabled without additional validation features. - // This isolates the base validation layer from Best Practices / Synchronization Validation. - createInfo.pNext = nullptr; - - AssertIfFailed(vkCreateInstance(&createInfo, nullptr, &VulkanInstance)); - instanceCreated = true; - - } - else - { - SystemLogWarningMessage(ElemLogMessageCategory_Graphics, "VkLayer_khronos_validation not found but EnableGraphicsDebugLayer() was called. Debug layer will not be enabled."); - VulkanDebugLayerEnabled = false; - } - } - - if (!instanceCreated) - { - SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Init Vulkan..."); - - const char* extensions[] = - { - VK_KHR_SURFACE_EXTENSION_NAME, - #ifdef WIN32 - VK_KHR_WIN32_SURFACE_EXTENSION_NAME - #elif __linux__ - VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME - #endif - }; - - createInfo.ppEnabledExtensionNames = extensions; - createInfo.enabledExtensionCount = ARRAYSIZE(extensions); - - AssertIfFailed(vkCreateInstance(&createInfo, nullptr, &VulkanInstance)); - } - - volkLoadInstanceOnly(VulkanInstance); - - if (VulkanDebugLayerEnabled && isSdkInstalled) - { - VkDebugReportCallbackCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; - debugCreateInfo.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; - debugCreateInfo.pfnCallback = VulkanDebugReportCallback; - - AssertIfFailed(vkCreateDebugReportCallbackEXT(VulkanInstance, &debugCreateInfo, 0, &vulkanDebugCallback)); - } -} +TEMP \ No newline at end of file From 7276a03bbc56f5b1508f54a8a625f67580aebc63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 10:25:40 +0200 Subject: [PATCH 19/23] Test Vulkan base validation layer --- .../Graphics/Vulkan/VulkanGraphicsDevice.cpp | 871 +++++++++++++++++- 1 file changed, 870 insertions(+), 1 deletion(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index e3fbbe0e..0c0d4fe4 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -1 +1,870 @@ -TEMP \ No newline at end of file +#include "VulkanGraphicsDevice.h" +#include "VulkanConfig.h" +#include "SystemDataPool.h" +#include "SystemFunctions.h" +#include "SystemLogging.h" +#include "SystemMemory.h" + +MemoryArena VulkanGraphicsMemoryArena; +SystemDataPool vulkanGraphicsDevicePool; + +bool VulkanDebugLayerEnabled = false; +bool vulkanDebugGpuValidationEnabled = false; +bool VulkanDebugBarrierInfoEnabled = false; +VkInstance VulkanInstance = nullptr; +VkDebugReportCallbackEXT vulkanDebugCallback = nullptr; + +VkBool32 VKAPI_CALL VulkanDebugReportCallback(VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT, uint64_t, size_t, int32_t, const char*, const char* pMessage, void*) +{ + auto messageType = ElemLogMessageType_Debug; + + if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) + { + messageType = ElemLogMessageType_Error; + } + else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) + { + messageType = ElemLogMessageType_Warning; + } + + if (SystemFindSubString(pMessage, "VK_EXT_mutable_descriptor_type") != -1) + { + return VK_FALSE; + } + + if (SystemFindSubString(pMessage, "BestPractices-PushConstants") != -1) + { + return VK_FALSE; + } + + SystemLogMessage(messageType, ElemLogMessageCategory_Graphics, "%s", pMessage); + + return VK_FALSE; +} + +void InitVulkan() +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + + AssertIfFailed(volkInitialize()); + SystemAssert(volkGetInstanceVersion() >= VK_API_VERSION_1_4); + + VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; + appInfo.apiVersion = VK_API_VERSION_1_4; + + VkInstanceCreateInfo createInfo = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO }; + createInfo.pApplicationInfo = &appInfo; + + auto isSdkInstalled = false; + auto instanceCreated = false; + + uint32_t instanceLayerCount; + vkEnumerateInstanceLayerProperties(&instanceLayerCount, nullptr); + + auto instanceLayers = SystemPushArray(stackMemoryArena, instanceLayerCount); + vkEnumerateInstanceLayerProperties(&instanceLayerCount, instanceLayers.Pointer); + + for (uint32_t i = 0; i < instanceLayerCount; i++) + { + // TODO: Use a system function for that + if (strcmp(instanceLayers[i].layerName, "VK_LAYER_KHRONOS_validation") == 0) + { + isSdkInstalled = true; + break; + } + } + + // TODO: Enumerate instance extensions like in the cube sample to see if the system + // is compatible with the current surface extension + + if (VulkanDebugLayerEnabled) + { + if (isSdkInstalled) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Init Vulkan Debug Mode."); + + const char* layers[] = + { + "VK_LAYER_KHRONOS_validation" + }; + + createInfo.ppEnabledLayerNames = layers; + createInfo.enabledLayerCount = ARRAYSIZE(layers); + + const char* extensions[] = + { + VK_EXT_DEBUG_REPORT_EXTENSION_NAME, + VK_EXT_DEBUG_UTILS_EXTENSION_NAME, + VK_KHR_SURFACE_EXTENSION_NAME, + #ifdef _WIN32 + VK_KHR_WIN32_SURFACE_EXTENSION_NAME + #elif __linux__ + VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME + #endif + }; + + createInfo.ppEnabledExtensionNames = extensions; + createInfo.enabledExtensionCount = ARRAYSIZE(extensions); + + auto enabledValidationFeatures = SystemPushArray(stackMemoryArena, 3); + auto currentEnabledValidationFeaturesIndex = 0u; + + enabledValidationFeatures[currentEnabledValidationFeaturesIndex++] = VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT; + enabledValidationFeatures[currentEnabledValidationFeaturesIndex++] = VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT; + + if (vulkanDebugGpuValidationEnabled) + { + enabledValidationFeatures[currentEnabledValidationFeaturesIndex++] = VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT; + } + + VkValidationFeaturesEXT validationFeatures = { VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT }; + validationFeatures.enabledValidationFeatureCount = currentEnabledValidationFeaturesIndex; + validationFeatures.pEnabledValidationFeatures = enabledValidationFeatures.Pointer; + + createInfo.pNext = nullptr; + + AssertIfFailed(vkCreateInstance(&createInfo, nullptr, &VulkanInstance)); + instanceCreated = true; + + } + else + { + SystemLogWarningMessage(ElemLogMessageCategory_Graphics, "VkLayer_khronos_validation not found but EnableGraphicsDebugLayer() was called. Debug layer will not be enabled."); + VulkanDebugLayerEnabled = false; + } + } + + if (!instanceCreated) + { + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Init Vulkan..."); + + const char* extensions[] = + { + VK_KHR_SURFACE_EXTENSION_NAME, + #ifdef WIN32 + VK_KHR_WIN32_SURFACE_EXTENSION_NAME + #elif __linux__ + VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME + #endif + }; + + createInfo.ppEnabledExtensionNames = extensions; + createInfo.enabledExtensionCount = ARRAYSIZE(extensions); + + AssertIfFailed(vkCreateInstance(&createInfo, nullptr, &VulkanInstance)); + } + + volkLoadInstanceOnly(VulkanInstance); + + if (VulkanDebugLayerEnabled && isSdkInstalled) + { + VkDebugReportCallbackCreateInfoEXT debugCreateInfo = { VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT }; + debugCreateInfo.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; + debugCreateInfo.pfnCallback = VulkanDebugReportCallback; + + AssertIfFailed(vkCreateDebugReportCallbackEXT(VulkanInstance, &debugCreateInfo, 0, &vulkanDebugCallback)); + } +} + +void InitVulkanGraphicsDeviceMemory() +{ + if (!VulkanGraphicsMemoryArena.Storage) + { + // TODO: To Review + VulkanGraphicsMemoryArena = SystemAllocateMemoryArena(256 * 1024 * 1024); + vulkanGraphicsDevicePool = SystemCreateDataPool(VulkanGraphicsMemoryArena, VULKAN_MAX_DEVICES); + + InitVulkan(); + } +} + +VkCompareOp ConvertToVulkanCompareFunction(ElemGraphicsCompareFunction compareFunction) +{ + switch (compareFunction) + { + case ElemGraphicsCompareFunction_Never: + return VK_COMPARE_OP_NEVER; + + case ElemGraphicsCompareFunction_Less: + return VK_COMPARE_OP_LESS; + + case ElemGraphicsCompareFunction_Equal: + return VK_COMPARE_OP_EQUAL; + + case ElemGraphicsCompareFunction_LessEqual: + return VK_COMPARE_OP_LESS_OR_EQUAL; + + case ElemGraphicsCompareFunction_Greater: + return VK_COMPARE_OP_GREATER; + + case ElemGraphicsCompareFunction_NotEqual: + return VK_COMPARE_OP_NOT_EQUAL; + + case ElemGraphicsCompareFunction_GreaterEqual: + return VK_COMPARE_OP_GREATER_OR_EQUAL; + + case ElemGraphicsCompareFunction_Always: + return VK_COMPARE_OP_ALWAYS; + } +} + +ElemGraphicsDeviceInfo VulkanConstructGraphicsDeviceInfo(MemoryArena memoryArena, VkPhysicalDeviceProperties deviceProperties, VkPhysicalDeviceMemoryProperties deviceMemoryProperties) +{ + auto deviceName = ReadOnlySpan(deviceProperties.deviceName); + auto destinationDeviceName = SystemPushArray(memoryArena, deviceName.Length); + SystemCopyBuffer(destinationDeviceName, deviceName); + + return + { + .DeviceName = destinationDeviceName.Pointer, + .GraphicsApi = ElemGraphicsApi_Vulkan, + .DeviceId = deviceProperties.deviceID, + .AvailableMemory = deviceMemoryProperties.memoryHeaps[0].size + }; +} + +VulkanDescriptorSet* CreateVulkanDescriptorSet(MemoryArena memoryArena, VkDevice graphicsDevice, VkDescriptorSetLayout descriptorSetLayout, uint32_t descriptorCount) +{ + VkDescriptorPoolSize poolSize + { + .type = VK_DESCRIPTOR_TYPE_MUTABLE_EXT, + .descriptorCount = descriptorCount + }; + + VkDescriptorPoolCreateInfo createInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; + createInfo.poolSizeCount = 1; + createInfo.pPoolSizes = &poolSize; + createInfo.flags = VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT; + createInfo.maxSets = 1; + + VkDescriptorPool descriptorPool; + AssertIfFailed(vkCreateDescriptorPool(graphicsDevice, &createInfo, nullptr, &descriptorPool)); + + VkDescriptorSetVariableDescriptorCountAllocateInfo descriptorSetLayoutCount = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_VARIABLE_DESCRIPTOR_COUNT_ALLOCATE_INFO }; + descriptorSetLayoutCount.descriptorSetCount = 1; + descriptorSetLayoutCount.pDescriptorCounts = &descriptorCount; + + VkDescriptorSetAllocateInfo allocateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; + allocateInfo.pSetLayouts = &descriptorSetLayout; + allocateInfo.descriptorSetCount = 1; + allocateInfo.descriptorPool = descriptorPool; + allocateInfo.pNext = &descriptorSetLayoutCount; + + VkDescriptorSet descriptorSet; + AssertIfFailed(vkAllocateDescriptorSets(graphicsDevice, &allocateInfo, &descriptorSet)); + + auto result = SystemPushStruct(memoryArena); + result->DescriptorPool = descriptorPool; + result->DescriptorSet = descriptorSet; + + return result; +} + +void FreeVulkanDescriptorSet(VkDevice device, const VulkanDescriptorSet* descriptorSet) +{ + SystemAssert(descriptorSet); + vkDestroyDescriptorPool(device, descriptorSet->DescriptorPool, nullptr); +} + +VulkanDescriptorHeap CreateVulkanDescriptorHeap(MemoryArena memoryArena, VkDevice graphicsDevice, VkDescriptorSetLayout descriptorSetLayout, uint32_t length) +{ + auto descriptorSet = CreateVulkanDescriptorSet(memoryArena, graphicsDevice, descriptorSetLayout, VULKAN_MAX_RESOURCES); + + auto descriptorStorage = SystemPushStruct(memoryArena); + descriptorStorage->DescriptorSet = descriptorSet; + descriptorStorage->Items = SystemPushArray(memoryArena, length); + descriptorStorage->CurrentIndex = 0; + descriptorStorage->FreeListIndex = UINT32_MAX; + + return + { + .Storage = descriptorStorage + }; +} + +void FreeVulkanDescriptorHeap(VkDevice device, const VulkanDescriptorHeap descriptorHeap) +{ + SystemAssert(descriptorHeap.Storage); + vkDestroyDescriptorPool(device, descriptorHeap.Storage->DescriptorSet->DescriptorPool, nullptr); +} + +uint32_t CreateVulkanDescriptorHandle(VulkanDescriptorHeap descriptorHeap) +{ + SystemAssert(descriptorHeap.Storage); + + auto storage = descriptorHeap.Storage; + auto descriptorIndex = UINT32_MAX; + + do + { + if (storage->FreeListIndex == UINT32_MAX) + { + descriptorIndex = UINT32_MAX; + break; + } + + descriptorIndex = storage->FreeListIndex; + } while (!SystemAtomicCompareExchange(storage->FreeListIndex, descriptorIndex, storage->Items[storage->FreeListIndex].Next)); + + if (descriptorIndex == UINT32_MAX) + { + descriptorIndex = SystemAtomicAdd(storage->CurrentIndex, 1); + } + + return descriptorIndex; +} + +void FreeVulkanDescriptorHandle(VulkanDescriptorHeap descriptorHeap, uint32_t handle) +{ + auto storage = descriptorHeap.Storage; + + do + { + storage->Items[handle].Next = storage->FreeListIndex; + } while (!SystemAtomicCompareExchange(storage->FreeListIndex, storage->FreeListIndex, handle)); +} + +VulkanQueryHeap CreateVulkanQueryHeap(ElemGraphicsDevice graphicsDevice, MemoryArena memoryArena, VkQueryType type, uint32_t length) +{ + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + VkQueryPoolCreateInfo createInfo = { VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO }; + createInfo.queryType = type; + createInfo.queryCount = length; + + VkQueryPool queryPool; + AssertIfFailed(vkCreateQueryPool(graphicsDeviceData->Device, &createInfo, nullptr, &queryPool)); + vkResetQueryPool(graphicsDeviceData->Device, queryPool, 0, length); + + auto queryHeapReadbackBuffer = CreateVulkanGraphicsBufferCpu(graphicsDevice, ElemGraphicsHeapType_Readback, sizeof(uint64_t) * VULKAN_MAX_QUERYHEAP_ITEMS, "QueryHeapReadbackBuffer"); + + uint8_t* cpuPointer = nullptr; + AssertIfFailed(vkMapMemory(graphicsDeviceData->Device, queryHeapReadbackBuffer.DeviceMemory, 0, VK_WHOLE_SIZE, 0, (void**)&cpuPointer)); + + auto descriptorStorage = SystemPushStruct(memoryArena); + descriptorStorage->QueryHeap = queryPool; + descriptorStorage->QueryHeapReadbackBuffer = queryHeapReadbackBuffer; + descriptorStorage->ReadbackCpuPointer = cpuPointer; + descriptorStorage->Type = type; + descriptorStorage->Items = SystemPushArray(memoryArena, length); + descriptorStorage->CurrentIndex = 0; + descriptorStorage->InitializationIndex = 0; + descriptorStorage->FreeListIndex = UINT32_MAX; + + return + { + .Storage = descriptorStorage + }; +} + +void FreeVulkanQueryHeap(VkDevice device, VulkanQueryHeap queryHeap) +{ + SystemAssert(queryHeap.Storage); + + vkDestroyQueryPool(device, queryHeap.Storage->QueryHeap, nullptr); + vkDestroyBuffer(device, queryHeap.Storage->QueryHeapReadbackBuffer.Buffer, nullptr); + vkFreeMemory(device, queryHeap.Storage->QueryHeapReadbackBuffer.DeviceMemory, nullptr); +} + +uint32_t CreateVulkanQueryHeapIndex(VulkanQueryHeap queryHeap) +{ + SystemAssert(queryHeap.Storage); + + auto storage = queryHeap.Storage; + auto index = UINT32_MAX; + + do + { + if (storage->FreeListIndex == UINT32_MAX) + { + index = UINT32_MAX; + break; + } + + index = storage->FreeListIndex; + } while (!SystemAtomicCompareExchange(storage->FreeListIndex, index, storage->Items[storage->FreeListIndex].Next)); + + if (index == UINT32_MAX) + { + index = SystemAtomicAdd(storage->CurrentIndex, 1); + } + + return index; +} + +void FreeVulkanQueryHeapIndex(VulkanQueryHeap queryHeap, uint32_t index) +{ + auto storage = queryHeap.Storage; + + do + { + storage->Items[index].Next = storage->FreeListIndex; + } while (!SystemAtomicCompareExchange(storage->FreeListIndex, storage->FreeListIndex, index)); +} + +bool VulkanCheckGraphicsDeviceCompatibility(VkPhysicalDevice device) +{ + VkPhysicalDeviceFeatures deviceFeatures; + vkGetPhysicalDeviceFeatures(device, &deviceFeatures); + + VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR }; + + VkPhysicalDeviceMeshShaderFeaturesEXT meshShaderFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT }; + meshShaderFeatures.pNext = &presentIdFeatures; + + VkPhysicalDeviceFeatures2 features2 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR }; + features2.pNext = &meshShaderFeatures; + + vkGetPhysicalDeviceFeatures2(device, &features2); + + if (meshShaderFeatures.meshShader && presentIdFeatures.presentId) + { + return true; + } + + return false; +} + +VulkanGraphicsDeviceData* GetVulkanGraphicsDeviceData(ElemGraphicsDevice graphicsDevice) +{ + return SystemGetDataPoolItem(vulkanGraphicsDevicePool, graphicsDevice); +} + +VulkanGraphicsDeviceDataFull* GetVulkanGraphicsDeviceDataFull(ElemGraphicsDevice graphicsDevice) +{ + return SystemGetDataPoolItemFull(vulkanGraphicsDevicePool, graphicsDevice); +} + +VkDescriptorSetLayout CreateVulkanDescriptorSetLayout(ElemGraphicsDevice graphicsDevice, VkDescriptorType* descriptorTypes, uint32_t descriptorTypeCount) +{ + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + VkMutableDescriptorTypeListEXT descriptorSetTypes = + { + .descriptorTypeCount = descriptorTypeCount, + .pDescriptorTypes = descriptorTypes + }; + + VkMutableDescriptorTypeCreateInfoEXT mutableDescriptorTypeCreateInfo = { VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_CREATE_INFO_EXT }; + mutableDescriptorTypeCreateInfo.pMutableDescriptorTypeLists = &descriptorSetTypes; + mutableDescriptorTypeCreateInfo.mutableDescriptorTypeListCount = 1; + + VkDescriptorBindingFlags descriptorBindingFlags[] = + { + VK_DESCRIPTOR_BINDING_VARIABLE_DESCRIPTOR_COUNT_BIT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT | VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT + }; + + VkDescriptorSetLayoutBindingFlagsCreateInfo descriptorBindingFlagsCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO }; + descriptorBindingFlagsCreateInfo.bindingCount = 1; + descriptorBindingFlagsCreateInfo.pBindingFlags = descriptorBindingFlags; + descriptorBindingFlagsCreateInfo.pNext = &mutableDescriptorTypeCreateInfo; + + VkDescriptorSetLayoutBinding descriptorBinding = + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_MUTABLE_EXT, + .descriptorCount = VULKAN_MAX_RESOURCES, + .stageFlags = VK_SHADER_STAGE_ALL + }; + + VkDescriptorSetLayoutCreateInfo descriptorSetCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + descriptorSetCreateInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT; + descriptorSetCreateInfo.bindingCount = 1; + descriptorSetCreateInfo.pBindings = &descriptorBinding; + descriptorSetCreateInfo.pNext = &descriptorBindingFlagsCreateInfo; + + VkDescriptorSetLayout result; + AssertIfFailed(vkCreateDescriptorSetLayout(graphicsDeviceData->Device, &descriptorSetCreateInfo, 0, &result)); + + return result; +} + +void CreateVulkanPipelineLayout(ElemGraphicsDevice graphicsDevice) +{ + auto stackMemoryArena = SystemGetStackMemoryArena(); + + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(graphicsDevice); + SystemAssert(graphicsDeviceDataFull); + + VkPipelineLayoutCreateInfo layoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; + + VkPushConstantRange push_constant; + push_constant.offset = 0; + push_constant.size = 24 * 4; + push_constant.stageFlags = VK_SHADER_STAGE_ALL; + + layoutCreateInfo.pPushConstantRanges = &push_constant; + layoutCreateInfo.pushConstantRangeCount = 1; + + // TODO: Recheck those + VkDescriptorType resourceDescriptorTypes[] = { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR }; + graphicsDeviceDataFull->ResourceDescriptorSetLayout = CreateVulkanDescriptorSetLayout(graphicsDevice, resourceDescriptorTypes, ARRAYSIZE(resourceDescriptorTypes)); + + VkDescriptorType samplerDescriptorTypes[] = { VK_DESCRIPTOR_TYPE_SAMPLER }; + graphicsDeviceDataFull->SamplerDescriptorSetLayout = CreateVulkanDescriptorSetLayout(graphicsDevice, samplerDescriptorTypes, ARRAYSIZE(samplerDescriptorTypes)); + + VkDescriptorSetLayout descriptorSetLayouts[] { graphicsDeviceDataFull->ResourceDescriptorSetLayout, graphicsDeviceDataFull->SamplerDescriptorSetLayout }; + layoutCreateInfo.pSetLayouts = descriptorSetLayouts; + layoutCreateInfo.setLayoutCount = ARRAYSIZE(descriptorSetLayouts); + + AssertIfFailed(vkCreatePipelineLayout(graphicsDeviceData->Device, &layoutCreateInfo, 0, &graphicsDeviceData->PipelineLayout)); +} + +void VulkanSetGraphicsOptions(const ElemGraphicsOptions* options) +{ + SystemAssert(options); + + if (options->EnableDebugLayer) + { + VulkanDebugLayerEnabled = options->EnableDebugLayer; + } + + if (options->EnableGpuValidation) + { + vulkanDebugGpuValidationEnabled = options->EnableGpuValidation; + } + + VulkanDebugBarrierInfoEnabled = options->EnableDebugBarrierInfo; +} + +ElemGraphicsDeviceInfoSpan VulkanGetAvailableGraphicsDevices() +{ + InitVulkanGraphicsDeviceMemory(); + + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto deviceInfos = SystemPushArray(stackMemoryArena, VULKAN_MAX_DEVICES); + auto currentDeviceInfoIndex = 0u; + + uint32_t deviceCount = VULKAN_MAX_DEVICES; + AssertIfFailed(vkEnumeratePhysicalDevices(VulkanInstance, &deviceCount, nullptr)); + + auto devices = SystemPushArray(stackMemoryArena, deviceCount); + AssertIfFailed(vkEnumeratePhysicalDevices(VulkanInstance, &deviceCount, devices.Pointer)); + + for (uint32_t i = 0; i < deviceCount; i++) + { + VkPhysicalDeviceProperties deviceProperties; + vkGetPhysicalDeviceProperties(devices[i], &deviceProperties); + + VkPhysicalDeviceMemoryProperties deviceMemoryProperties; + vkGetPhysicalDeviceMemoryProperties(devices[i], &deviceMemoryProperties); + + if (VulkanCheckGraphicsDeviceCompatibility(devices[i])) + { + deviceInfos[currentDeviceInfoIndex++] = VulkanConstructGraphicsDeviceInfo(stackMemoryArena, deviceProperties, deviceMemoryProperties); + } + } + + return + { + .Items = deviceInfos.Pointer, + .Length = currentDeviceInfoIndex + }; +} + +ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* options) +{ + // TODO: Review features selection + InitVulkanGraphicsDeviceMemory(); + + auto stackMemoryArena = SystemGetStackMemoryArena(); + + VkPhysicalDevice physicalDevice = {}; + VkPhysicalDeviceProperties deviceProperties = {}; + VkPhysicalDeviceMemoryProperties deviceMemoryProperties {}; + auto foundDevice = false; + + uint32_t deviceCount; + AssertIfFailed(vkEnumeratePhysicalDevices(VulkanInstance, &deviceCount, nullptr)); + + auto devices = SystemPushArray(stackMemoryArena, deviceCount); + AssertIfFailed(vkEnumeratePhysicalDevices(VulkanInstance, &deviceCount, devices.Pointer)); + + for (uint32_t i = 0; i < deviceCount; i++) + { + if (VulkanCheckGraphicsDeviceCompatibility(devices[i])) + { + vkGetPhysicalDeviceProperties(devices[i], &deviceProperties); + vkGetPhysicalDeviceMemoryProperties(devices[i], &deviceMemoryProperties); + + if ((options != nullptr && options->DeviceId == deviceProperties.deviceID) || options == nullptr || options->DeviceId == 0) + { + physicalDevice = devices[i]; + foundDevice = true; + break; + } + } + } + + SystemAssertReturnNullHandle(foundDevice); + + uint32_t queueFamilyCount = 0; + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, nullptr); + + auto queueFamilies = SystemPushArray(stackMemoryArena, queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &queueFamilyCount, queueFamilies.Pointer); + + VkDeviceQueueCreateInfo queueCreateInfos[3]; + uint32_t renderCommandQueueIndex = UINT32_MAX; + uint32_t computeCommandQueueIndex = UINT32_MAX; + uint32_t copyCommandQueueIndex = UINT32_MAX; + float queuePriority[3] = { 1.0f, 1.0f, 1.0f }; + + for (uint32_t i = 0; i < 3; i++) + { + uint32_t queueCount = 1; + + if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT && renderCommandQueueIndex == UINT32_MAX) + { + renderCommandQueueIndex = i; + queueCount = SystemMin(queueFamilies[i].queueCount, 3u); + } + else if (queueFamilies[i].queueFlags & VK_QUEUE_COMPUTE_BIT && computeCommandQueueIndex == UINT32_MAX) + { + computeCommandQueueIndex = i; + queueCount = SystemMin(queueFamilies[i].queueCount, 2u); + } + else if (queueFamilies[i].queueFlags & VK_QUEUE_TRANSFER_BIT && copyCommandQueueIndex == UINT32_MAX) + { + copyCommandQueueIndex = i; + queueCount = SystemMin(queueFamilies[i].queueCount, 2u); + } + else + { + SystemLogErrorMessage(ElemLogMessageCategory_Graphics, "Wrong queue type."); + } + + VkDeviceQueueCreateInfo queueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; + queueCreateInfo.pQueuePriorities = queuePriority; + queueCreateInfo.queueCount = queueCount; + queueCreateInfo.queueFamilyIndex = i; + + queueCreateInfos[i] = queueCreateInfo; + } + + int32_t gpuMemoryTypeIndex = -1; + int32_t gpuUploadMemoryTypeIndex = -1; + int32_t readBackMemoryTypeIndex = -1; + int32_t uploadMemoryTypeIndex = -1; + + for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++) + { + auto memoryPropertyFlags = deviceMemoryProperties.memoryTypes[i].propertyFlags; + + if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && + (memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + gpuMemoryTypeIndex = i; + } + if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) && + (memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)) + { + gpuUploadMemoryTypeIndex = i; + } + else if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) && + (memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && + (memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT)) + { + readBackMemoryTypeIndex = i; + } + + else if ((memoryPropertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) && + (memoryPropertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == 0) + { + uploadMemoryTypeIndex = i; + } + } + + SystemAssert(gpuMemoryTypeIndex != -1 && gpuUploadMemoryTypeIndex != -1 && readBackMemoryTypeIndex != -1); + + VkDeviceCreateInfo createInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; + createInfo.queueCreateInfoCount = 3; + createInfo.pQueueCreateInfos = queueCreateInfos; + + const char* extensions[] = + { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, // TODO: To review + VK_KHR_PRESENT_ID_EXTENSION_NAME, // TODO: To review + VK_KHR_PRESENT_WAIT_EXTENSION_NAME, // TODO: To review + VK_EXT_MESH_SHADER_EXTENSION_NAME, + VK_EXT_MUTABLE_DESCRIPTOR_TYPE_EXTENSION_NAME, + VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, + VK_KHR_RAY_QUERY_EXTENSION_NAME, + VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, + VK_EXT_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_EXTENSION_NAME + }; + + createInfo.ppEnabledExtensionNames = extensions; + createInfo.enabledExtensionCount = ARRAYSIZE(extensions); + + VkPhysicalDeviceDynamicRenderingUnusedAttachmentsFeaturesEXT dynamicUnusedFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_UNUSED_ATTACHMENTS_FEATURES_EXT }; + dynamicUnusedFeatures.dynamicRenderingUnusedAttachments = true; + + VkPhysicalDeviceFeatures2 features = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2 }; + features.features.shaderInt16 = true; + features.features.shaderInt64 = true; + features.features.pipelineStatisticsQuery = true; + features.features.fillModeNonSolid = true; + features.features.samplerAnisotropy = true; + + VkPhysicalDeviceVulkan12Features features12 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES }; + features12.timelineSemaphore = true; + features12.runtimeDescriptorArray = true; + features12.descriptorIndexing = true; + features12.descriptorBindingVariableDescriptorCount = true; + features12.descriptorBindingPartiallyBound = true; + features12.descriptorBindingSampledImageUpdateAfterBind = true; + features12.descriptorBindingStorageBufferUpdateAfterBind = true; + features12.descriptorBindingStorageImageUpdateAfterBind = true; + features12.shaderSampledImageArrayNonUniformIndexing = true; + features12.separateDepthStencilLayouts = true; + features12.hostQueryReset = true; + features12.shaderInt8 = true; + features12.bufferDeviceAddress = true; + + if (VulkanDebugLayerEnabled) + { + features12.bufferDeviceAddressCaptureReplay = true; + } + + VkPhysicalDeviceVulkan13Features features13 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES }; + features13.maintenance4 = true; + features13.synchronization2 = true; + features13.dynamicRendering = true; + features13.shaderDemoteToHelperInvocation = true; + + VkPhysicalDeviceVulkan14Features features14 = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES }; + features14.maintenance5 = true; + + VkPhysicalDeviceMeshShaderFeaturesEXT meshFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT }; + meshFeatures.meshShader = true; + meshFeatures.meshShaderQueries = true; + + VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT mutableDescriptorFeatures = { VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT }; + mutableDescriptorFeatures.mutableDescriptorType = true; + + VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR }; + presentIdFeatures.presentId = true; + + VkPhysicalDevicePresentWaitFeaturesKHR presentWaitFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_WAIT_FEATURES_KHR }; + presentWaitFeatures.presentWait = true; + + VkPhysicalDeviceRayQueryFeaturesKHR rayQueriesFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_QUERY_FEATURES_KHR }; + rayQueriesFeatures.rayQuery = true; + + VkPhysicalDeviceAccelerationStructureFeaturesKHR accelerationStructureFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR }; + accelerationStructureFeatures.accelerationStructure = true; + + createInfo.pNext = &features; + features.pNext = &features12; + features12.pNext = &features13; + features13.pNext = &features14; + features14.pNext = &presentIdFeatures; + presentIdFeatures.pNext = &presentWaitFeatures; + presentWaitFeatures.pNext = &meshFeatures; + meshFeatures.pNext = &mutableDescriptorFeatures; + mutableDescriptorFeatures.pNext = &rayQueriesFeatures; + rayQueriesFeatures.pNext = &accelerationStructureFeatures; + accelerationStructureFeatures.pNext = &dynamicUnusedFeatures; + + VkDevice device = nullptr; + AssertIfFailedReturnNullHandle(vkCreateDevice(physicalDevice, &createInfo, nullptr, &device)); + volkLoadDevice(device); + + auto memoryArena = SystemAllocateMemoryArena(); + + auto handle = SystemAddDataPoolItem(vulkanGraphicsDevicePool, { + .Device = device, + .MemoryArena = memoryArena + }); + + SystemAddDataPoolItemFull(vulkanGraphicsDevicePool, handle, { + .PhysicalDevice = physicalDevice, + .DeviceProperties = deviceProperties, + .DeviceMemoryProperties = deviceMemoryProperties, + .RenderCommandQueueIndex = renderCommandQueueIndex, + .ComputeCommandQueueIndex = computeCommandQueueIndex, + .CopyCommandQueueIndex = copyCommandQueueIndex, + .GpuMemoryTypeIndex = (uint32_t)gpuMemoryTypeIndex, + .GpuUploadMemoryTypeIndex = (uint32_t)gpuUploadMemoryTypeIndex, + .ReadBackMemoryTypeIndex = (uint32_t)readBackMemoryTypeIndex, + .UploadMemoryTypeIndex = (uint32_t)uploadMemoryTypeIndex + }); + + CreateVulkanPipelineLayout(handle); + + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(handle); + auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(handle); + + graphicsDeviceData->ResourceDescriptorHeap = CreateVulkanDescriptorHeap(memoryArena, graphicsDeviceData->Device, graphicsDeviceDataFull->ResourceDescriptorSetLayout, VULKAN_MAX_RESOURCES); + graphicsDeviceData->SamplerDescriptorHeap = CreateVulkanDescriptorHeap(memoryArena, graphicsDeviceData->Device, graphicsDeviceDataFull->SamplerDescriptorSetLayout, VULKAN_MAX_SAMPLERS); + + // TODO: This need to be checked. We don't know how many max threads will use this. Maybe we can allocate for MAX_CONC_THREADS variable of param (that can be overriden) + graphicsDeviceData->UploadBufferPools = SystemPushArray*>(VulkanGraphicsMemoryArena, MAX_UPLOAD_BUFFERS); + graphicsDeviceData->QueryHeap = CreateVulkanQueryHeap(handle, memoryArena, VK_QUERY_TYPE_TIMESTAMP, VULKAN_MAX_QUERYHEAP_ITEMS); + + return handle; +} + +void VulkanFreeGraphicsDevice(ElemGraphicsDevice graphicsDevice) +{ + SystemAssert(graphicsDevice != ELEM_HANDLE_NULL); + + auto graphicsDeviceData = GetVulkanGraphicsDeviceData(graphicsDevice); + SystemAssert(graphicsDeviceData); + + auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(graphicsDevice); + SystemAssert(graphicsDeviceDataFull); + + for (uint32_t i = 0; i < graphicsDeviceData->UploadBufferPools.Length; i++) + { + auto bufferPool = graphicsDeviceData->UploadBufferPools[i]; + + if (bufferPool) + { + for (uint32_t j = 0; j < MAX_UPLOAD_BUFFERS; j++) + { + auto uploadBuffer = &bufferPool->UploadBuffers[j]; + + if (uploadBuffer->Buffer.Buffer) + { + vkDestroyBuffer(graphicsDeviceData->Device, uploadBuffer->Buffer.Buffer, nullptr); + vkFreeMemory(graphicsDeviceData->Device, uploadBuffer->Buffer.DeviceMemory, nullptr); + + uploadBuffer->Buffer = {}; + *uploadBuffer = {}; + } + } + + *bufferPool = {}; + } + } + + FreeVulkanDescriptorHeap(graphicsDeviceData->Device, graphicsDeviceData->ResourceDescriptorHeap); + FreeVulkanDescriptorHeap(graphicsDeviceData->Device, graphicsDeviceData->SamplerDescriptorHeap); + FreeVulkanQueryHeap(graphicsDeviceData->Device, graphicsDeviceData->QueryHeap); + + vkDestroyDescriptorSetLayout(graphicsDeviceData->Device, graphicsDeviceDataFull->ResourceDescriptorSetLayout, nullptr); + vkDestroyDescriptorSetLayout(graphicsDeviceData->Device, graphicsDeviceDataFull->SamplerDescriptorSetLayout, nullptr); + vkDestroyPipelineLayout(graphicsDeviceData->Device, graphicsDeviceData->PipelineLayout, nullptr); + vkDestroyDevice(graphicsDeviceData->Device, nullptr); + + SystemRemoveDataPoolItem(vulkanGraphicsDevicePool, graphicsDevice); + SystemLogDebugMessage(ElemLogMessageCategory_Graphics, "Releasing Vulkan"); +} + +ElemGraphicsDeviceInfo VulkanGetGraphicsDeviceInfo(ElemGraphicsDevice graphicsDevice) +{ + SystemAssert(graphicsDevice != ELEM_HANDLE_NULL); + + auto stackMemoryArena = SystemGetStackMemoryArena(); + auto graphicsDeviceDataFull = GetVulkanGraphicsDeviceDataFull(graphicsDevice); + SystemAssert(graphicsDeviceDataFull); + + return VulkanConstructGraphicsDeviceInfo(stackMemoryArena, graphicsDeviceDataFull->DeviceProperties, graphicsDeviceDataFull->DeviceMemoryProperties); +} \ No newline at end of file From 9e18daf64bfb41e796b7c8c0a8712dd1f65e2a5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 10:28:08 +0200 Subject: [PATCH 20/23] Fix Vulkan validation test setup --- src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index 0c0d4fe4..1d47b39c 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -745,7 +745,7 @@ ElemGraphicsDevice VulkanCreateGraphicsDevice(const ElemGraphicsDeviceOptions* o meshFeatures.meshShader = true; meshFeatures.meshShaderQueries = true; - VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT mutableDescriptorFeatures = { VK_STRUCTURE_TYPE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT }; + VkPhysicalDeviceMutableDescriptorTypeFeaturesEXT mutableDescriptorFeatures = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MUTABLE_DESCRIPTOR_TYPE_FEATURES_EXT }; mutableDescriptorFeatures.mutableDescriptorType = true; VkPhysicalDevicePresentIdFeaturesKHR presentIdFeatures { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRESENT_ID_FEATURES_KHR }; @@ -867,4 +867,4 @@ ElemGraphicsDeviceInfo VulkanGetGraphicsDeviceInfo(ElemGraphicsDevice graphicsDe SystemAssert(graphicsDeviceDataFull); return VulkanConstructGraphicsDeviceInfo(stackMemoryArena, graphicsDeviceDataFull->DeviceProperties, graphicsDeviceDataFull->DeviceMemoryProperties); -} \ No newline at end of file +} From 5a76dc2469a2114d4f123735533bc1039fd1c8e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 10:57:08 +0200 Subject: [PATCH 21/23] Fix renderer debug UI buffer sizes --- samples/Demos/01-Renderer/ElementalArt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/samples/Demos/01-Renderer/ElementalArt.c b/samples/Demos/01-Renderer/ElementalArt.c index e6886f60..4343e853 100644 --- a/samples/Demos/01-Renderer/ElementalArt.c +++ b/samples/Demos/01-Renderer/ElementalArt.c @@ -21,8 +21,8 @@ void ElemArtInit(ElemGraphicsDevice graphicsDevice, ElemArtData* elemArtData) elemArtData->TextBufferCount = 0; elemArtData->MaxDraw2DCommandCount = 1024; - elemArtData->Draw2DCommands = (Draw2DCommand*)malloc(elemArtData->MaxDraw2DCommandCount); - elemArtData->Draw2DCommandsBuffer = SampleCreateGpuBuffer(&elemArtData->GpuMemory, elemArtData->MaxDraw2DCommandCount, ElemGraphicsResourceUsage_Read, "Draw2DCommandsBuffer"); + elemArtData->Draw2DCommands = (Draw2DCommand*)malloc(elemArtData->MaxDraw2DCommandCount * sizeof(Draw2DCommand)); + elemArtData->Draw2DCommandsBuffer = SampleCreateGpuBuffer(&elemArtData->GpuMemory, elemArtData->MaxDraw2DCommandCount * sizeof(Draw2DCommand), ElemGraphicsResourceUsage_Read, "Draw2DCommandsBuffer"); elemArtData->Draw2DCommandCount = 0; } @@ -66,7 +66,7 @@ void ElemArtRender(ElemCommandList commandList, ElemVector2 renderTargetSize, El .RenderTargetSize = renderTargetSize, }; - ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)¶meters, .Length = sizeof(RaytracingShaderParameters) }); + ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)¶meters, .Length = sizeof(DrawTextShaderParameters) }); ElemDispatchMesh(commandList, 1, 1, 1); elemArtData->TextBufferCount = 0; From 87b0ce0835ad3b4761ed554b6c8483cabf4b0b5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 10:58:00 +0200 Subject: [PATCH 22/23] Re-enable Vulkan validation features --- src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index 1d47b39c..a0afbe0f 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -121,7 +121,7 @@ void InitVulkan() validationFeatures.enabledValidationFeatureCount = currentEnabledValidationFeaturesIndex; validationFeatures.pEnabledValidationFeatures = enabledValidationFeatures.Pointer; - createInfo.pNext = nullptr; + createInfo.pNext = &validationFeatures; AssertIfFailed(vkCreateInstance(&createInfo, nullptr, &VulkanInstance)); instanceCreated = true; From 23e140e86846e18cb10c5b4e9a7e15f4ee4b397b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Decroy=C3=A8re?= Date: Fri, 28 Aug 2026 12:06:47 +0200 Subject: [PATCH 23/23] Temporarily disable Vulkan synchronization validation --- src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp index a0afbe0f..2aac53ed 100644 --- a/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp +++ b/src/Elemental/Common/Graphics/Vulkan/VulkanGraphicsDevice.cpp @@ -110,7 +110,8 @@ void InitVulkan() auto currentEnabledValidationFeaturesIndex = 0u; enabledValidationFeatures[currentEnabledValidationFeaturesIndex++] = VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT; - enabledValidationFeatures[currentEnabledValidationFeaturesIndex++] = VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT; + + // TODO: Re-enable synchronization validation once submit-time validation no longer causes intermittent device loss. if (vulkanDebugGpuValidationEnabled) {