Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
547d3d0
Fix data pool commit range
tdecroyere Aug 28, 2026
2e34aeb
Synchronize Vulkan acceleration structure builds
tdecroyere Aug 28, 2026
15f90e8
Clarify temporary Vulkan raytracing build barrier
tdecroyere Aug 28, 2026
6a6e333
No-op sync checkpoint
tdecroyere Aug 28, 2026
e485b06
Keep data-pool fix unchanged
tdecroyere Aug 28, 2026
7bf4201
Preserve data-pool fix
tdecroyere Aug 28, 2026
0a65a5b
Fix Vulkan swapchain semaphore ownership
tdecroyere Aug 28, 2026
52708e8
Track Vulkan swapchain frame semaphores
tdecroyere Aug 28, 2026
7abbb8f
Synchronize Vulkan swapchain acquire and submit
tdecroyere Aug 28, 2026
7dd0e3d
Fix Vulkan swapchain semaphore reuse
tdecroyere Aug 28, 2026
36750c6
Synchronize swapchain acquire before all commands
tdecroyere Aug 28, 2026
1f6c240
Simplify Vulkan CPU timeline waits
tdecroyere Aug 28, 2026
6bb7329
Test Vulkan command allocator reuse
tdecroyere Aug 28, 2026
734620e
Revert Vulkan command allocator diagnostic
tdecroyere Aug 28, 2026
1998498
Fix Vulkan swapchain composite alpha
tdecroyere Aug 28, 2026
d5ee074
Preserve swapchain alpha in renderer compositing
tdecroyere Aug 28, 2026
1f6f2ab
Test Vulkan base validation layer
tdecroyere Aug 28, 2026
5ffe9fa
TEMP
tdecroyere Aug 28, 2026
7276a03
Test Vulkan base validation layer
tdecroyere Aug 28, 2026
9e18daf
Fix Vulkan validation test setup
tdecroyere Aug 28, 2026
5a76dc2
Fix renderer debug UI buffer sizes
tdecroyere Aug 28, 2026
87b0ce0
Re-enable Vulkan validation features
tdecroyere Aug 28, 2026
23e140e
Temporarily disable Vulkan synchronization validation
tdecroyere Aug 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions samples/Demos/01-Renderer/ElementalArt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down Expand Up @@ -66,7 +66,7 @@ void ElemArtRender(ElemCommandList commandList, ElemVector2 renderTargetSize, El
.RenderTargetSize = renderTargetSize,
};

ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&parameters, .Length = sizeof(RaytracingShaderParameters) });
ElemPushPipelineStateConstants(commandList, 0, (ElemDataSpan) { .Items = (uint8_t*)&parameters, .Length = sizeof(DrawTextShaderParameters) });

ElemDispatchMesh(commandList, 1, 1, 1);
elemArtData->TextBufferCount = 0;
Expand Down
11 changes: 8 additions & 3 deletions samples/Demos/01-Renderer/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
Expand Down Expand Up @@ -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 },
});

Expand All @@ -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 },
});

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -687,4 +692,4 @@ int main(int argc, const char* argv[])
.Payload = &payload
});

}
}
2 changes: 1 addition & 1 deletion src/Elemental/Common/Graphics/CommandAllocatorPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,4 @@ template<typename TCommandList>
void ReleaseCommandListPoolItem(CommandListPoolItem<TCommandList>* commandListPoolItem);

template<typename TCommandAllocator, typename TCommandList>
void UpdateCommandAllocatorPoolItemFence(CommandAllocatorPoolItem<TCommandAllocator, TCommandList>* commandAllocatorPoolItem, ElemFence fence);
void UpdateCommandAllocatorPoolItemFence(CommandAllocatorPoolItem<TCommandAllocator, TCommandList>* commandAllocatorPoolItem, ElemFence fence);
70 changes: 38 additions & 32 deletions src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 };
Expand All @@ -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
});
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -369,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);
Expand Down Expand Up @@ -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<VkPipelineStageFlags> submitStageMasks = {};
Span<VkSemaphore> waitSemaphores = {};
Span<uint64_t> waitSemaphoreValues = {};

if (options && options->FencesToWait.Length > 0)
if (waitSemaphoreCount > 0)
{
submitStageMasks = SystemPushArray<VkPipelineStageFlags>(stackMemoryArena, options->FencesToWait.Length);
waitSemaphores = SystemPushArray<VkSemaphore>(stackMemoryArena, options->FencesToWait.Length);
waitSemaphoreValues = SystemPushArray<uint64_t>(stackMemoryArena, options->FencesToWait.Length);
submitStageMasks = SystemPushArray<VkPipelineStageFlags>(stackMemoryArena, waitSemaphoreCount);
waitSemaphores = SystemPushArray<VkSemaphore>(stackMemoryArena, waitSemaphoreCount);
waitSemaphoreValues = SystemPushArray<uint64_t>(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];

Expand All @@ -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_ALL_COMMANDS_BIT;
waitSemaphores[acquireSemaphoreIndex] = commandQueueData->AcquireSemaphore;
waitSemaphoreValues[acquireSemaphoreIndex] = 0;
}
}

bool hasError = false;
Expand All @@ -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;
Expand All @@ -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();
Expand Down Expand Up @@ -526,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;
}
}

Expand Down Expand Up @@ -644,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);
}
}
1 change: 1 addition & 0 deletions src/Elemental/Common/Graphics/Vulkan/VulkanCommandList.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ struct VulkanCommandQueueData
ElemGraphicsDevice GraphicsDevice;
VkSemaphore Fence;
uint64_t FenceValue;
VkSemaphore AcquireSemaphore;
VkSemaphore PresentSemaphore;
bool SignalPresentSemaphore;
uint64_t LastCompletedFenceValue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
22 changes: 20 additions & 2 deletions src/Elemental/Common/Graphics/Vulkan/VulkanResourceBarrier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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++)
{
Expand All @@ -137,6 +138,23 @@ void InsertVulkanResourceBarriersIfNeeded(ElemCommandList commandList, ElemGraph

VkMemoryBarrier2 accelerationStructureMemoryBarrier = { VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 };

if (needsRaytracingBuildBarrier)
{
// 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;
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;
Expand Down
Loading
Loading