Skip to content

Update from task a1ce759e-0b74-4564-9c2c-b6370e14c669#27

Open
tronpis wants to merge 1 commit into
rendergraph-bugs-15f39from
vulkan-rendergraph-barriers-bug-4c669
Open

Update from task a1ce759e-0b74-4564-9c2c-b6370e14c669#27
tronpis wants to merge 1 commit into
rendergraph-bugs-15f39from
vulkan-rendergraph-barriers-bug-4c669

Conversation

@tronpis
Copy link
Copy Markdown
Owner

@tronpis tronpis commented Apr 2, 2026

This PR was created by qwen-chat coder for task a1ce759e-0b74-4564-9c2c-b6370e14c669.

- Refactored RenderGraph::compile() to validate dependency indices and distinguish between pass-level and resource-specific dependencies, generating appropriate VkMemoryBarrier or VkImageMemoryBarrier objects
- Extended PassDependency struct and RenderGraphBuilder API to support resource-specific synchronization with layout transitions and aspect masks
- Added defensive guards in RenderGraph::execute() to prevent submission of invalid Vulkan commands when renderPass or framebuffer are null
- Updated all example usage files to call the new graph.build() method ensuring proper Vulkan object initialization
- Enhanced ResourceRegistry with additional bounds checking to prevent out-of-bounds access with stale indices
- Improved error reporting with descriptive messages for invalid dependencies and missing Vulkan objects
@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Fix Vulkan validation errors and enhance RenderGraph dependency system

🐞 Bug fix ✨ Enhancement

Grey Divider

Walkthroughs

Description
• Fix Vulkan validation errors with defensive guards and bounds checking
• Distinguish between pass-level and resource-specific dependencies with proper barriers
• Add resource-specific synchronization with layout transitions and aspect masks
• Require explicit build() call for Vulkan object initialization in all examples
• Improve error reporting with descriptive messages for invalid dependencies
Diagram
flowchart LR
  A["PassDependency struct"] -->|"extended with resource fields"| B["Resource-specific dependencies"]
  C["RenderGraphBuilder API"] -->|"new addResourceDependency method"| B
  B -->|"generates VkImageMemoryBarrier"| D["RenderGraph::compile"]
  E["Pass-level dependencies"] -->|"generates VkMemoryBarrier"| D
  D -->|"validates indices"| F["Error handling"]
  G["ResourceRegistry"] -->|"bounds checking"| H["Prevent out-of-bounds access"]
  I["RenderGraph::execute"] -->|"defensive guards"| J["Validate Vulkan objects"]
  K["Example usage files"] -->|"call graph.build"| L["Proper initialization"]
Loading

Grey Divider

File Changes

1. FarmEngine/src/rendering/graph/RenderGraph.cpp 🐞 Bug fix +77/-25

Vulkan validation fixes and dependency system improvements

• Added defensive bounds check in ResourceRegistry::updateResourceState() to prevent out-of-bounds
 access with stale indices
• Implemented new RenderGraphBuilder::addResourceDependency() method for resource-specific
 synchronization with layout transitions and aspect masks
• Enhanced RenderGraph::compile() to validate dependency indices and distinguish between
 pass-level dependencies (VkMemoryBarrier) and resource-specific dependencies (VkImageMemoryBarrier)
• Added defensive guards in RenderGraph::execute() to verify render pass and framebuffer validity
 before submission
• Improved barrier recording with proper pipeline stage flags and removed fallback extent
 calculation logic

FarmEngine/src/rendering/graph/RenderGraph.cpp


2. FarmEngine/src/rendering/graph/RenderGraph.h ✨ Enhancement +16/-0

Extended PassDependency struct and API

• Extended PassDependency struct with optional resource-specific fields: resourceName,
 oldLayout, newLayout, and aspectMask
• Added new addResourceDependency() method to RenderGraphBuilder API for defining
 resource-specific synchronization with layout transitions
• Documented distinction between pass-level and resource-specific dependencies

FarmEngine/src/rendering/graph/RenderGraph.h


3. FarmEngine/src/rendering/graph/RenderPipeline.cpp ✨ Enhancement +8/-2

Add explicit build() calls to renderers

• Updated ForwardRenderer::buildGraph() to accept VkDevice parameter and call graph.build()
 after compilation
• Updated DeferredRenderer::buildGraph() to accept VkDevice parameter and call graph.build()
 after compilation
• Both methods now ensure proper Vulkan object initialization before execution

FarmEngine/src/rendering/graph/RenderPipeline.cpp


View more (2)
4. FarmEngine/src/rendering/graph/RenderPipeline.h ✨ Enhancement +2/-2

Update renderer buildGraph signatures

• Updated ForwardRenderer::buildGraph() signature to include VkDevice parameter
• Updated DeferredRenderer::buildGraph() signature to include VkDevice parameter

FarmEngine/src/rendering/graph/RenderPipeline.h


5. FarmEngine/src/rendering/graph/ExampleUsage.h ✨ Enhancement +8/-4

Add build() calls to all example functions

• Updated all four example functions (ChunkRenderingExample, ShadowMappingExample,
 SSAOExample, MinimalExample) to accept VkDevice parameter
• Added graph.build(device, extent) call after graph.compile() in all examples to ensure proper
 Vulkan object initialization

FarmEngine/src/rendering/graph/ExampleUsage.h


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Apr 2, 2026

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📎 Requirement gaps (0)

Grey Divider


Action required

1. Wrong subpass dependency indices 🐞 Bug ≡ Correctness
Description
RenderGraph::compile() converts addDependency(from,to,...) into a VkSubpassDependency with
srcSubpass=from and dstSubpass=to, but each VkRenderPass created has subpassCount=1 so dstSubpass
must be 0 (or VK_SUBPASS_EXTERNAL). Any dependency with to!=0 (e.g., ShadowMappingExample’s 0->1)
can make vkCreateRenderPass fail or trigger validation errors.
Code

FarmEngine/src/rendering/graph/RenderGraph.cpp[R270-280]

+        } else {
+            // Pass-level execution dependency: store in CompiledPass for use with VkMemoryBarrier
+            compiledPasses[dep.to].dependencies.push_back({
+                dep.from,
+                dep.to,
+                dep.srcStageMask,
+                dep.dstStageMask,
+                dep.srcAccessMask,
+                dep.dstAccessMask,
+                VK_DEPENDENCY_BY_REGION_BIT
+            });
Evidence
CompiledPass.dependencies is a vector<VkSubpassDependency> and createRenderPasses() creates a render
pass with exactly one subpass (subpassCount=1), then passes compiled.dependencies directly into
vkCreateRenderPass. compile() now pushes {dep.from, dep.to, ...} into that vector for non-resource
dependencies, and the example code adds a dependency 0->1, which produces dstSubpass=1 for a render
pass that only has subpass 0.

FarmEngine/src/rendering/graph/RenderGraph.h[160-176]
FarmEngine/src/rendering/graph/RenderGraph.cpp[291-314]
FarmEngine/src/rendering/graph/RenderGraph.cpp[235-282]
FarmEngine/src/rendering/graph/ExampleUsage.h[94-116]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`RenderGraphBuilder::addDependency()` is a pass-to-pass dependency API, but `RenderGraph::compile()` currently encodes it into `VkSubpassDependency` fields (`srcSubpass`, `dstSubpass`) using *pass indices*. Each `VkRenderPass` created by `createRenderPasses()` has exactly one subpass (index 0), so `dstSubpass = to` becomes invalid when `to != 0`.

## Issue Context
This breaks real usage (e.g. `ShadowMappingExample` adds dependency 0->1). These dependencies must be enforced between *separate render passes*, not as subpass dependencies.

## Fix Focus Areas
- FarmEngine/src/rendering/graph/RenderGraph.cpp[235-282]
- FarmEngine/src/rendering/graph/RenderGraph.cpp[291-314]
- FarmEngine/src/rendering/graph/RenderGraph.h[160-176]

## Suggested fix
1. Change the representation for pass-level dependencies to something that can be emitted via `vkCmdPipelineBarrier` before executing pass `to` (e.g. store a list of `{srcStageMask,dstStageMask, VkMemoryBarrier}` on `CompiledPass`).
2. In `recordBarriers()`, emit those pass-level `VkMemoryBarrier`s using the stored stage masks.
3. Keep `VkSubpassDependency` only for within-renderpass subpass dependencies (if/when you have multiple subpasses).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Barriers capture VK_NULL images 🐞 Bug ≡ Correctness
Description
Resource-specific dependencies bake VkImageMemoryBarrier.image from compiledRegistry at compile
time, but addColorTarget/addDepthTarget never populate VkImage so this often becomes VK_NULL_HANDLE
and cannot be updated for per-frame external/swapchain images. recordBarriers() then submits
vkCmdPipelineBarrier with these baked (potentially null/wrong) images, so synchronization and layout
transitions can be invalid or ineffective.
Code

FarmEngine/src/rendering/graph/RenderGraph.cpp[R247-269]

+        if (!dep.resourceName.empty()) {
+            // Resource-specific dependency: create VkImageMemoryBarrier
+            const ResourceHandle* res = compiledRegistry.getResource(dep.resourceName);
+            if (!res) {
+                throw std::runtime_error("Resource dependency references unknown resource: " + dep.resourceName);
+            }
+            
            VkImageMemoryBarrier barrier{};
            barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
            barrier.srcAccessMask = dep.srcAccessMask;
            barrier.dstAccessMask = dep.dstAccessMask;
-            barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
-            barrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+            barrier.oldLayout = dep.oldLayout;
+            barrier.newLayout = dep.newLayout;
            barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
            barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-            barrier.image = VK_NULL_HANDLE; // Se establecerá durante la ejecución
-            barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
+            barrier.image = res->image;
+            barrier.subresourceRange = {
+                dep.aspectMask,
+                0, res->mipLevels,
+                0, res->arrayLayers
+            };
            
            compiledPasses[dep.to].prePassBarriers.push_back(barrier);
Evidence
ResourceHandle.image defaults to VK_NULL_HANDLE and addColorTarget/addDepthTarget only fill metadata
(format/extent/layout), not VkImage/VkImageView. compile() sets barrier.image = res->image from
compiledRegistry, and recordBarriers() submits those barriers as-is. execute() receives a separate
runtime ResourceRegistry& registry, but recordBarriers() does not resolve barrier.image from it,
so barriers cannot reflect actual per-frame images (especially swapchain/external).

FarmEngine/src/rendering/graph/RenderGraph.h[33-51]
FarmEngine/src/rendering/graph/RenderGraph.cpp[58-87]
FarmEngine/src/rendering/graph/RenderGraph.cpp[235-270]
FarmEngine/src/rendering/graph/RenderGraph.cpp[367-380]
FarmEngine/src/rendering/graph/RenderGraph.cpp[383-409]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Resource barriers are created during `compile()` with `barrier.image = res->image`, but for most resources built via `addColorTarget()` / `addDepthTarget()` the `VkImage` is never set (defaults to `VK_NULL_HANDLE`). Also, external/swapchain images can vary per-frame, so baking `VkImage` into the compiled barrier is not safe.

## Issue Context
`execute()` is passed a runtime `ResourceRegistry& registry`, but `recordBarriers()` currently ignores it and uses pre-baked `VkImageMemoryBarrier`s. This prevents correct per-frame image resolution and can submit invalid barriers.

## Fix Focus Areas
- FarmEngine/src/rendering/graph/RenderGraph.cpp[235-270]
- FarmEngine/src/rendering/graph/RenderGraph.cpp[367-380]
- FarmEngine/src/rendering/graph/RenderGraph.cpp[383-409]
- FarmEngine/src/rendering/graph/RenderGraph.h[33-51]

## Suggested fix
1. Don’t store `VkImageMemoryBarrier` with a concrete `image` in `CompiledPass`.
2. Store a small "pending barrier" struct that includes `resourceName`, `oldLayout/newLayout`, access masks, aspectMask, and stage masks.
3. In `recordBarriers(cmd, pass, registry)` resolve `VkImage image = registry.getImage(resourceName)` (or other runtime source) and build the `VkImageMemoryBarrier` just-in-time.
4. If `image == VK_NULL_HANDLE`, throw with a clear message (or skip execution) so the failure is explicit.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

3. Zero renderArea extent regression 🐞 Bug ☼ Reliability
Description
execute() now always uses compiled.extent for rpBegin.renderArea.extent after removing the dynamic
fallback, so if createFramebuffers computed {0,0} (e.g., swapchainExtent is {0,0})
vkCmdBeginRenderPass will be called with a zero render area. This can produce Vulkan validation
errors or undefined behavior.
Code

FarmEngine/src/rendering/graph/RenderGraph.cpp[R408-409]

+        rpBegin.renderArea.extent = compiled.extent;
        
Evidence
createFramebuffers initializes the per-pass extent from the swapchainExtent input and stores it into
compiled.extent; execute uses compiled.extent directly with no guard. The previous code had a
fallback extent computation when compiled.extent was {0,0}, but that block was removed in this PR.

FarmEngine/src/rendering/graph/RenderGraph.cpp[317-365]
FarmEngine/src/rendering/graph/RenderGraph.cpp[383-432]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`execute()` begins render passes using `rpBegin.renderArea.extent = compiled.extent;` with no validation. If `compiled.extent` is `{0,0}` (commonly when swapchain is minimized or not yet configured), `vkCmdBeginRenderPass` is invalid.

## Issue Context
The previous fallback logic for `{0,0}` was removed, but no replacement guard was added.

## Fix Focus Areas
- FarmEngine/src/rendering/graph/RenderGraph.cpp[317-365]
- FarmEngine/src/rendering/graph/RenderGraph.cpp[383-432]

## Suggested fix
Add an explicit guard either in `createFramebuffers()` (throw if extent is zero) or in `execute()` (early-return/skip passes for that frame when extent is zero). Prefer skipping rendering for minimized window scenarios rather than inventing a fake fallback extent.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. Dependency stage masks unused 🐞 Bug ➹ Performance
Description
recordBarriers() hardcodes VK_PIPELINE_STAGE_ALL_COMMANDS_BIT for src/dst stages, so the stage masks
provided via addDependency/addResourceDependency are effectively ignored and barriers
over-synchronize. This makes the dependency APIs misleading and can cause avoidable GPU stalls.
Code

FarmEngine/src/rendering/graph/RenderGraph.cpp[R372-374]

+            VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
            VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
            0,
Evidence
PassDependency stores srcStageMask/dstStageMask, and addResourceDependency populates them, but
recordBarriers uses ALL_COMMANDS for both stage masks and does not read any per-dependency stage
info when emitting vkCmdPipelineBarrier.

FarmEngine/src/rendering/graph/RenderGraph.h[53-68]
FarmEngine/src/rendering/graph/RenderGraph.cpp[131-153]
FarmEngine/src/rendering/graph/RenderGraph.cpp[367-380]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`vkCmdPipelineBarrier` is emitted with `VK_PIPELINE_STAGE_ALL_COMMANDS_BIT` for both src and dst, ignoring the stage masks captured in dependencies. This causes unnecessary synchronization and defeats the purpose of taking stage masks in the builder API.

## Issue Context
After fixing pass-level dependencies and resource barriers, you should be able to compute src/dst stage masks from the stored dependency/barrier metadata.

## Fix Focus Areas
- FarmEngine/src/rendering/graph/RenderGraph.h[53-68]
- FarmEngine/src/rendering/graph/RenderGraph.cpp[131-153]
- FarmEngine/src/rendering/graph/RenderGraph.cpp[367-380]

## Suggested fix
Track `srcStageMask`/`dstStageMask` alongside each emitted barrier (or compute a per-pass union), and pass those into `vkCmdPipelineBarrier` instead of `ALL_COMMANDS`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Copy link
Copy Markdown
Contributor

@amazon-q-developer amazon-q-developer Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changes successfully address Vulkan RenderGraph barrier handling and API consistency. All modifications are correctly implemented and improve the codebase:

Key Improvements:

  • Added missing graph.build() calls in example functions and renderer implementations
  • Implemented resource-specific dependency API with proper VkImageMemoryBarrier support
  • Added defensive bounds checking in ResourceRegistry methods
  • Enhanced barrier recording with proper pipeline stage synchronization

All changes follow Vulkan best practices and maintain API consistency across the codebase.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the project's .gitignore and significantly enhances the RenderGraph system by introducing resource-specific dependencies and more robust synchronization. Key changes include the implementation of addResourceDependency in the RenderGraphBuilder, the addition of defensive bounds checks in the ResourceRegistry, and the enforcement of a build step across various renderers and examples to ensure Vulkan objects are properly initialized before execution. Review feedback identifies critical issues regarding synchronization efficiency, specifically the lack of fine-grained stage masks in barriers, potential invalid image handles during the compilation phase, and the incorrect application of subpass dependency indices for inter-render-pass synchronization.

Comment on lines +247 to 269
if (!dep.resourceName.empty()) {
// Resource-specific dependency: create VkImageMemoryBarrier
const ResourceHandle* res = compiledRegistry.getResource(dep.resourceName);
if (!res) {
throw std::runtime_error("Resource dependency references unknown resource: " + dep.resourceName);
}

VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = dep.srcAccessMask;
barrier.dstAccessMask = dep.dstAccessMask;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.oldLayout = dep.oldLayout;
barrier.newLayout = dep.newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = VK_NULL_HANDLE; // Se establecerá durante la ejecución
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
barrier.image = res->image;
barrier.subresourceRange = {
dep.aspectMask,
0, res->mipLevels,
0, res->arrayLayers
};

compiledPasses[dep.to].prePassBarriers.push_back(barrier);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

When processing a resource-specific dependency, the srcStageMask and dstStageMask from the dep object are not stored. These masks are essential for the vkCmdPipelineBarrier call in recordBarriers. Currently, recordBarriers (lines 372-373) defaults to VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, which is inefficient and causes unnecessary GPU stalls. You should store these stage masks in the CompiledPass structure alongside the barriers to allow for fine-grained synchronization.

barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = VK_NULL_HANDLE; // Se establecerá durante la ejecución
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
barrier.image = res->image;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The barrier.image is assigned res->image during the compile phase. However, for internal resources (Frame/Persistent), the image handles are typically not created until the build phase or later. If res->image is VK_NULL_HANDLE during compilation, the recorded barrier will be invalid and won't function during execution. Consider resolving the image handle at execution time or ensuring allocation happens before the graph is compiled.

Comment on lines +272 to +280
compiledPasses[dep.to].dependencies.push_back({
dep.from,
dep.to,
dep.srcStageMask,
dep.dstStageMask,
dep.srcAccessMask,
dep.dstAccessMask,
VK_DEPENDENCY_BY_REGION_BIT
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The VkSubpassDependency is being populated with global pass indices (dep.from, dep.to). However, VkSubpassDependency is designed for synchronization between subpasses within the same VkRenderPass. Since each CompiledPass creates its own separate VkRenderPass with a single subpass (index 0), these indices are invalid and will likely cause Vulkan validation errors. For inter-render-pass synchronization, use vkCmdPipelineBarrier or set the subpass indices to VK_SUBPASS_EXTERNAL.

Comment on lines +270 to +280
} else {
// Pass-level execution dependency: store in CompiledPass for use with VkMemoryBarrier
compiledPasses[dep.to].dependencies.push_back({
dep.from,
dep.to,
dep.srcStageMask,
dep.dstStageMask,
dep.srcAccessMask,
dep.dstAccessMask,
VK_DEPENDENCY_BY_REGION_BIT
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Wrong subpass dependency indices 🐞 Bug ≡ Correctness

RenderGraph::compile() converts addDependency(from,to,...) into a VkSubpassDependency with
srcSubpass=from and dstSubpass=to, but each VkRenderPass created has subpassCount=1 so dstSubpass
must be 0 (or VK_SUBPASS_EXTERNAL). Any dependency with to!=0 (e.g., ShadowMappingExample’s 0->1)
can make vkCreateRenderPass fail or trigger validation errors.
Agent Prompt
## Issue description
`RenderGraphBuilder::addDependency()` is a pass-to-pass dependency API, but `RenderGraph::compile()` currently encodes it into `VkSubpassDependency` fields (`srcSubpass`, `dstSubpass`) using *pass indices*. Each `VkRenderPass` created by `createRenderPasses()` has exactly one subpass (index 0), so `dstSubpass = to` becomes invalid when `to != 0`.

## Issue Context
This breaks real usage (e.g. `ShadowMappingExample` adds dependency 0->1). These dependencies must be enforced between *separate render passes*, not as subpass dependencies.

## Fix Focus Areas
- FarmEngine/src/rendering/graph/RenderGraph.cpp[235-282]
- FarmEngine/src/rendering/graph/RenderGraph.cpp[291-314]
- FarmEngine/src/rendering/graph/RenderGraph.h[160-176]

## Suggested fix
1. Change the representation for pass-level dependencies to something that can be emitted via `vkCmdPipelineBarrier` before executing pass `to` (e.g. store a list of `{srcStageMask,dstStageMask, VkMemoryBarrier}` on `CompiledPass`).
2. In `recordBarriers()`, emit those pass-level `VkMemoryBarrier`s using the stored stage masks.
3. Keep `VkSubpassDependency` only for within-renderpass subpass dependencies (if/when you have multiple subpasses).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +247 to 269
if (!dep.resourceName.empty()) {
// Resource-specific dependency: create VkImageMemoryBarrier
const ResourceHandle* res = compiledRegistry.getResource(dep.resourceName);
if (!res) {
throw std::runtime_error("Resource dependency references unknown resource: " + dep.resourceName);
}

VkImageMemoryBarrier barrier{};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.srcAccessMask = dep.srcAccessMask;
barrier.dstAccessMask = dep.dstAccessMask;
barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
barrier.oldLayout = dep.oldLayout;
barrier.newLayout = dep.newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = VK_NULL_HANDLE; // Se establecerá durante la ejecución
barrier.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1};
barrier.image = res->image;
barrier.subresourceRange = {
dep.aspectMask,
0, res->mipLevels,
0, res->arrayLayers
};

compiledPasses[dep.to].prePassBarriers.push_back(barrier);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Barriers capture vk_null images 🐞 Bug ≡ Correctness

Resource-specific dependencies bake VkImageMemoryBarrier.image from compiledRegistry at compile
time, but addColorTarget/addDepthTarget never populate VkImage so this often becomes VK_NULL_HANDLE
and cannot be updated for per-frame external/swapchain images. recordBarriers() then submits
vkCmdPipelineBarrier with these baked (potentially null/wrong) images, so synchronization and layout
transitions can be invalid or ineffective.
Agent Prompt
## Issue description
Resource barriers are created during `compile()` with `barrier.image = res->image`, but for most resources built via `addColorTarget()` / `addDepthTarget()` the `VkImage` is never set (defaults to `VK_NULL_HANDLE`). Also, external/swapchain images can vary per-frame, so baking `VkImage` into the compiled barrier is not safe.

## Issue Context
`execute()` is passed a runtime `ResourceRegistry& registry`, but `recordBarriers()` currently ignores it and uses pre-baked `VkImageMemoryBarrier`s. This prevents correct per-frame image resolution and can submit invalid barriers.

## Fix Focus Areas
- FarmEngine/src/rendering/graph/RenderGraph.cpp[235-270]
- FarmEngine/src/rendering/graph/RenderGraph.cpp[367-380]
- FarmEngine/src/rendering/graph/RenderGraph.cpp[383-409]
- FarmEngine/src/rendering/graph/RenderGraph.h[33-51]

## Suggested fix
1. Don’t store `VkImageMemoryBarrier` with a concrete `image` in `CompiledPass`.
2. Store a small "pending barrier" struct that includes `resourceName`, `oldLayout/newLayout`, access masks, aspectMask, and stage masks.
3. In `recordBarriers(cmd, pass, registry)` resolve `VkImage image = registry.getImage(resourceName)` (or other runtime source) and build the `VkImageMemoryBarrier` just-in-time.
4. If `image == VK_NULL_HANDLE`, throw with a clear message (or skip execution) so the failure is explicit.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants