From 0807c5c92e1bb9ffa57aa990d6c8e93b34f44e94 Mon Sep 17 00:00:00 2001 From: "qwen.ai[bot]" Date: Thu, 2 Apr 2026 19:50:04 +0000 Subject: [PATCH 1/2] Fix VK_DEPENDENCY_BY_REGION_BIT overuse in RenderGraph barriers - Extended RenderGraphBuilder::addDependency and addResourceDependency with optional VkDependencyFlags parameter - Modified RenderGraph::compile to default PassLevelDependency::dependencyFlags to 0 instead of VK_DEPENDENCY_BY_REGION_BIT - Updated RenderGraph::recordBarriers to emit separate vkCmdPipelineBarrier calls when memory and image barriers have different dependency flags - Added dependencyFlags field to PassDependency, PendingResourceBarrier and PassLevelDependency structs - Fixed barrier emission logic to prevent BY_REGION flag from incorrectly applying to all barrier types in same call This change provides explicit control over dependency flags while maintaining safe defaults and ensuring correct synchronization behavior for different resource types. --- .gitignore | 18 +++--- .../src/rendering/graph/RenderGraph.cpp | 62 +++++++++++++++---- FarmEngine/src/rendering/graph/RenderGraph.h | 8 ++- 3 files changed, 64 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index bdb6a0c..b7995ca 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ -``` -# Compiled and binary files +```cpp +# Compiled and build artifacts *.o *.obj *.exe @@ -7,27 +7,24 @@ *.so *.a *.out +build/ +dist/ # Dependencies -node_modules/ venv/ .venv/ +node_modules/ __pycache__/ .mypy_cache/ .pytest_cache/ target/ .gradle/ -# Build artifacts -build/ -dist/ - -# Editor/IDE files +# Editors .vscode/ .idea/ *.swp *.swo -*.tmp # System files .DS_Store @@ -36,8 +33,9 @@ Thumbs.db .env.local *.env.* -# Logs +# Logs and temp files *.log +*.tmp # Coverage coverage/ diff --git a/FarmEngine/src/rendering/graph/RenderGraph.cpp b/FarmEngine/src/rendering/graph/RenderGraph.cpp index f8412e2..d8b26a8 100644 --- a/FarmEngine/src/rendering/graph/RenderGraph.cpp +++ b/FarmEngine/src/rendering/graph/RenderGraph.cpp @@ -116,7 +116,8 @@ RenderGraphBuilder& RenderGraphBuilder::addDependency(uint32_t from, uint32_t to VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, VkAccessFlags srcAccess, - VkAccessFlags dstAccess) { + VkAccessFlags dstAccess, + VkDependencyFlags dependencyFlags) { PassDependency dep; dep.from = from; dep.to = to; @@ -124,6 +125,7 @@ RenderGraphBuilder& RenderGraphBuilder::addDependency(uint32_t from, uint32_t to dep.dstStageMask = dstStage; dep.srcAccessMask = srcAccess; dep.dstAccessMask = dstAccess; + dep.dependencyFlags = dependencyFlags; explicitDependencies.push_back(dep); return *this; } @@ -136,7 +138,8 @@ RenderGraphBuilder& RenderGraphBuilder::addResourceDependency(uint32_t from, uin VkAccessFlags dstAccess, VkImageLayout oldLayout, VkImageLayout newLayout, - VkImageAspectFlags aspectMask) { + VkImageAspectFlags aspectMask, + VkDependencyFlags dependencyFlags) { PassDependency dep; dep.from = from; dep.to = to; @@ -148,6 +151,7 @@ RenderGraphBuilder& RenderGraphBuilder::addResourceDependency(uint32_t from, uin dep.oldLayout = oldLayout; dep.newLayout = newLayout; dep.aspectMask = aspectMask; + dep.dependencyFlags = dependencyFlags; explicitDependencies.push_back(dep); return *this; } @@ -268,6 +272,7 @@ void RenderGraph::compile(RenderGraphBuilder&& builder) { pendingBarrier.oldLayout = dep.oldLayout; pendingBarrier.newLayout = dep.newLayout; pendingBarrier.aspectMask = dep.aspectMask; + pendingBarrier.dependencyFlags = dep.dependencyFlags; compiledPasses[dep.to].pendingResourceBarriers.push_back(pendingBarrier); } else { @@ -279,7 +284,7 @@ void RenderGraph::compile(RenderGraphBuilder&& builder) { passDep.dstStageMask = dep.dstStageMask; passDep.srcAccessMask = dep.srcAccessMask; passDep.dstAccessMask = dep.dstAccessMask; - passDep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + passDep.dependencyFlags = dep.dependencyFlags; compiledPasses[dep.to].passLevelDependencies.push_back(passDep); } @@ -374,8 +379,8 @@ void RenderGraph::recordBarriers(VkCommandBuffer cmd, const CompiledPass& pass, VkPipelineStageFlags combinedSrcStage = 0; VkPipelineStageFlags combinedDstStage = 0; - // Combine dependency flags from all pass-level dependencies - VkDependencyFlags combinedDependencyFlags = 0; + // Combine dependency flags from all pass-level dependencies (for memory barriers only) + VkDependencyFlags combinedMemoryDependencyFlags = 0; // Process pass-level dependencies (emit as VkMemoryBarrier) std::vector memoryBarriers; @@ -388,11 +393,13 @@ void RenderGraph::recordBarriers(VkCommandBuffer cmd, const CompiledPass& pass, combinedSrcStage |= dep.srcStageMask; combinedDstStage |= dep.dstStageMask; - combinedDependencyFlags |= dep.dependencyFlags; + combinedMemoryDependencyFlags |= dep.dependencyFlags; } // Process resource-specific barriers (resolve image at runtime) + // Collect dependency flags from resource barriers separately std::vector imageBarriers; + VkDependencyFlags combinedImageDependencyFlags = 0; for (const auto& pending : pass.pendingResourceBarriers) { VkImage image = registry.getImage(pending.resourceName); const ResourceHandle* res = registry.getResource(pending.resourceName); @@ -421,19 +428,50 @@ void RenderGraph::recordBarriers(VkCommandBuffer cmd, const CompiledPass& pass, combinedSrcStage |= pending.srcStageMask; combinedDstStage |= pending.dstStageMask; + combinedImageDependencyFlags |= pending.dependencyFlags; } - // Emit barriers if any exist - if (!memoryBarriers.empty() || !imageBarriers.empty()) { - // Use combined stage masks, or fallback to ALL_COMMANDS if no stages were specified - VkPipelineStageFlags srcStage = combinedSrcStage != 0 ? combinedSrcStage : VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; - VkPipelineStageFlags dstStage = combinedDstStage != 0 ? combinedDstStage : VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + // Emit barriers - use separate calls when dependency flags differ to avoid + // unintentionally applying flags (like VK_DEPENDENCY_BY_REGION_BIT) to all barrier types + const bool hasMemoryBarriers = !memoryBarriers.empty(); + const bool hasImageBarriers = !imageBarriers.empty(); + const bool flagsDiffer = combinedMemoryDependencyFlags != combinedImageDependencyFlags; + + // Use combined stage masks, or fallback to ALL_COMMANDS if no stages were specified + VkPipelineStageFlags srcStage = combinedSrcStage != 0 ? combinedSrcStage : VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + VkPipelineStageFlags dstStage = combinedDstStage != 0 ? combinedDstStage : VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + + if (hasMemoryBarriers && hasImageBarriers && flagsDiffer) { + // Emit separate barrier calls to keep dependency flags isolated + vkCmdPipelineBarrier( + cmd, + srcStage, + dstStage, + combinedMemoryDependencyFlags, + static_cast(memoryBarriers.size()), + memoryBarriers.data(), + 0, nullptr, + 0, nullptr + ); vkCmdPipelineBarrier( cmd, srcStage, dstStage, - combinedDependencyFlags, + combinedImageDependencyFlags, + 0, nullptr, + 0, nullptr, + static_cast(imageBarriers.size()), + imageBarriers.data() + ); + } else if (hasMemoryBarriers || hasImageBarriers) { + // Same flags or only one type of barrier - can combine in single call + VkDependencyFlags finalFlags = hasMemoryBarriers ? combinedMemoryDependencyFlags : combinedImageDependencyFlags; + vkCmdPipelineBarrier( + cmd, + srcStage, + dstStage, + finalFlags, static_cast(memoryBarriers.size()), memoryBarriers.data(), 0, nullptr, diff --git a/FarmEngine/src/rendering/graph/RenderGraph.h b/FarmEngine/src/rendering/graph/RenderGraph.h index c16423f..ba19d36 100644 --- a/FarmEngine/src/rendering/graph/RenderGraph.h +++ b/FarmEngine/src/rendering/graph/RenderGraph.h @@ -65,6 +65,7 @@ struct PassDependency { VkImageLayout oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkImageLayout newLayout = VK_IMAGE_LAYOUT_UNDEFINED; VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + VkDependencyFlags dependencyFlags = 0; }; class RenderPass { @@ -109,7 +110,8 @@ class RenderGraphBuilder { // in sequential order. Attempting to add a dependency where from >= to will throw an error. RenderGraphBuilder& addDependency(uint32_t from, uint32_t to, VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, - VkAccessFlags srcAccess, VkAccessFlags dstAccess); + VkAccessFlags srcAccess, VkAccessFlags dstAccess, + VkDependencyFlags dependencyFlags = 0); // Definir dependencias específicas de recursos (para barreras de imagen) // Note: The 'from' pass index must be less than the 'to' pass index, as passes execute @@ -119,7 +121,8 @@ class RenderGraphBuilder { VkPipelineStageFlags srcStage, VkPipelineStageFlags dstStage, VkAccessFlags srcAccess, VkAccessFlags dstAccess, VkImageLayout oldLayout, VkImageLayout newLayout, - VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT); + VkImageAspectFlags aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + VkDependencyFlags dependencyFlags = 0); private: friend class RenderGraph; @@ -172,6 +175,7 @@ class RenderGraph { VkImageLayout oldLayout; VkImageLayout newLayout; VkImageAspectFlags aspectMask; + VkDependencyFlags dependencyFlags = 0; }; // Pass-level execution dependency info - emitted as VkMemoryBarrier at execution time From 3ecc1166c31323799dd64ee6517a66e145bea680 Mon Sep 17 00:00:00 2001 From: tronpis <252255532+tronpis@users.noreply.github.com> Date: Thu, 2 Apr 2026 16:53:11 -0300 Subject: [PATCH 2/2] Update FarmEngine/src/rendering/graph/RenderGraph.cpp Co-authored-by: amazon-q-developer[bot] <208079219+amazon-q-developer[bot]@users.noreply.github.com> --- FarmEngine/src/rendering/graph/RenderGraph.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FarmEngine/src/rendering/graph/RenderGraph.cpp b/FarmEngine/src/rendering/graph/RenderGraph.cpp index d8b26a8..f34175e 100644 --- a/FarmEngine/src/rendering/graph/RenderGraph.cpp +++ b/FarmEngine/src/rendering/graph/RenderGraph.cpp @@ -466,7 +466,7 @@ void RenderGraph::recordBarriers(VkCommandBuffer cmd, const CompiledPass& pass, ); } else if (hasMemoryBarriers || hasImageBarriers) { // Same flags or only one type of barrier - can combine in single call - VkDependencyFlags finalFlags = hasMemoryBarriers ? combinedMemoryDependencyFlags : combinedImageDependencyFlags; + VkDependencyFlags finalFlags = !memoryBarriers.empty() ? combinedMemoryDependencyFlags : combinedImageDependencyFlags; vkCmdPipelineBarrier( cmd, srcStage,