diff --git a/.gitignore b/.gitignore index 46c1c67..edef6ed 100644 --- a/.gitignore +++ b/.gitignore @@ -18,20 +18,11 @@ __pycache__/ target/ .gradle/ -# Build directories -build/ -dist/ - # Logs and temp files *.log *.tmp *.swp -# Environment -.env -.env.local -*.env.* - # Editors .vscode/ .idea/ @@ -39,8 +30,11 @@ dist/ # System files .DS_Store Thumbs.db +.env +.env.local +*.env.* -# Coverage +# Coverage reports coverage/ htmlcov/ .coverage @@ -64,4 +58,8 @@ htmlcov/ *.Z *.lz *.lzo +*.tar.gz +*.tar.bz2 +*.tar.xz +*.tar.zst ``` \ No newline at end of file diff --git a/FarmEngine/src/rendering/graph/RenderGraph.cpp b/FarmEngine/src/rendering/graph/RenderGraph.cpp index b74b912..856c47b 100644 --- a/FarmEngine/src/rendering/graph/RenderGraph.cpp +++ b/FarmEngine/src/rendering/graph/RenderGraph.cpp @@ -232,7 +232,7 @@ void RenderGraph::compile(RenderGraphBuilder&& builder) { // TODO: Implementar análisis de dependencias del grafo } - // Procesar dependencias explícitas y convertirlas en barreras por-pass + // Process explicit dependencies and convert them to per-pass barriers for (const auto& dep : builder.explicitDependencies) { // Validate dependency indices if (dep.from >= compiledPasses.size()) { @@ -245,39 +245,35 @@ void RenderGraph::compile(RenderGraphBuilder&& builder) { } if (!dep.resourceName.empty()) { - // Resource-specific dependency: create VkImageMemoryBarrier + // Resource-specific dependency: store as pending barrier for resolution at execution time 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 = dep.oldLayout; - barrier.newLayout = dep.newLayout; - barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - barrier.image = res->image; - barrier.subresourceRange = { - dep.aspectMask, - 0, res->mipLevels, - 0, res->arrayLayers - }; + PendingResourceBarrier pendingBarrier{}; + pendingBarrier.resourceName = dep.resourceName; + pendingBarrier.srcStageMask = dep.srcStageMask; + pendingBarrier.dstStageMask = dep.dstStageMask; + pendingBarrier.srcAccessMask = dep.srcAccessMask; + pendingBarrier.dstAccessMask = dep.dstAccessMask; + pendingBarrier.oldLayout = dep.oldLayout; + pendingBarrier.newLayout = dep.newLayout; + pendingBarrier.aspectMask = dep.aspectMask; - compiledPasses[dep.to].prePassBarriers.push_back(barrier); + compiledPasses[dep.to].pendingResourceBarriers.push_back(pendingBarrier); } 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 - }); + // Pass-level execution dependency: store for emission as VkMemoryBarrier at execution time + PassLevelDependency passDep{}; + passDep.fromPass = dep.from; + passDep.toPass = dep.to; + passDep.srcStageMask = dep.srcStageMask; + passDep.dstStageMask = dep.dstStageMask; + passDep.srcAccessMask = dep.srcAccessMask; + passDep.dstAccessMask = dep.dstAccessMask; + passDep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + compiledPasses[dep.to].passLevelDependencies.push_back(passDep); } } } @@ -305,8 +301,9 @@ void RenderGraph::createRenderPasses(VkDevice dev) { subpass.pDepthStencilAttachment = compiled.depthRef.attachment != VK_ATTACHMENT_UNUSED ? &compiled.depthRef : nullptr; rpInfo.pSubpasses = &subpass; - rpInfo.dependencyCount = static_cast(compiled.dependencies.size()); - rpInfo.pDependencies = compiled.dependencies.data(); + // No subpass dependencies - all synchronization is done via vkCmdPipelineBarrier + rpInfo.dependencyCount = 0; + rpInfo.pDependencies = nullptr; if (vkCreateRenderPass(device, &rpInfo, nullptr, &compiled.vkRenderPass) != VK_SUCCESS) { throw std::runtime_error("Failed to create render pass: " + compiled.definition.name); @@ -364,18 +361,72 @@ void RenderGraph::createFramebuffers(VkDevice dev, VkExtent2D swapchainExtent) { } } -void RenderGraph::recordBarriers(VkCommandBuffer cmd, const CompiledPass& pass) { - // Ejecutar barreras de imagen pre-pass si existen - if (!pass.prePassBarriers.empty()) { +void RenderGraph::recordBarriers(VkCommandBuffer cmd, const CompiledPass& pass, const ResourceRegistry& registry) { + // Compute combined stage masks from all barriers for vkCmdPipelineBarrier + VkPipelineStageFlags combinedSrcStage = 0; + VkPipelineStageFlags combinedDstStage = 0; + + // Process pass-level dependencies (emit as VkMemoryBarrier) + std::vector memoryBarriers; + for (const auto& dep : pass.passLevelDependencies) { + VkMemoryBarrier memBarrier{}; + memBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER; + memBarrier.srcAccessMask = dep.srcAccessMask; + memBarrier.dstAccessMask = dep.dstAccessMask; + memoryBarriers.push_back(memBarrier); + + combinedSrcStage |= dep.srcStageMask; + combinedDstStage |= dep.dstStageMask; + } + + // Process resource-specific barriers (resolve image at runtime) + std::vector imageBarriers; + for (const auto& pending : pass.pendingResourceBarriers) { + VkImage image = registry.getImage(pending.resourceName); + const ResourceHandle* res = registry.getResource(pending.resourceName); + if (!res) { + throw std::runtime_error("Resource barrier references unknown resource: " + pending.resourceName); + } + if (image == VK_NULL_HANDLE) { + throw std::runtime_error("Resource barrier references resource with VK_NULL_HANDLE: " + pending.resourceName); + } + + VkImageMemoryBarrier barrier{}; + barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + barrier.srcAccessMask = pending.srcAccessMask; + barrier.dstAccessMask = pending.dstAccessMask; + barrier.oldLayout = pending.oldLayout; + barrier.newLayout = pending.newLayout; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.image = image; + barrier.subresourceRange = { + pending.aspectMask, + 0, res->mipLevels, + 0, res->arrayLayers + }; + imageBarriers.push_back(barrier); + + combinedSrcStage |= pending.srcStageMask; + combinedDstStage |= pending.dstStageMask; + } + + // 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; + vkCmdPipelineBarrier( cmd, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + srcStage, + dstStage, 0, + static_cast(memoryBarriers.size()), + memoryBarriers.data(), 0, nullptr, - 0, nullptr, - static_cast(pass.prePassBarriers.size()), - pass.prePassBarriers.data() + static_cast(imageBarriers.size()), + imageBarriers.data() ); } } @@ -396,8 +447,13 @@ void RenderGraph::execute(VkCommandBuffer cmd, ResourceRegistry& registry, uint3 compiled.definition.name + "'. Call build() before execute()."); } + // Skip rendering if extent is zero (e.g., minimized window) + if (compiled.extent.width == 0 || compiled.extent.height == 0) { + continue; + } + // 1. Record barreras de transición - recordBarriers(cmd, compiled); + recordBarriers(cmd, compiled, registry); // 2. Begin render pass VkRenderPassBeginInfo rpBegin{}; diff --git a/FarmEngine/src/rendering/graph/RenderGraph.h b/FarmEngine/src/rendering/graph/RenderGraph.h index a7a4432..b52525d 100644 --- a/FarmEngine/src/rendering/graph/RenderGraph.h +++ b/FarmEngine/src/rendering/graph/RenderGraph.h @@ -158,20 +158,45 @@ class RenderGraph { void cleanup(VkDevice device); private: + // Pending barrier info for resource-specific dependencies - resolved at execution time + struct PendingResourceBarrier { + std::string resourceName; + VkPipelineStageFlags srcStageMask; + VkPipelineStageFlags dstStageMask; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkImageLayout oldLayout; + VkImageLayout newLayout; + VkImageAspectFlags aspectMask; + }; + + // Pass-level execution dependency info - emitted as VkMemoryBarrier at execution time + struct PassLevelDependency { + uint32_t fromPass; + uint32_t toPass; + VkPipelineStageFlags srcStageMask; + VkPipelineStageFlags dstStageMask; + VkAccessFlags srcAccessMask; + VkAccessFlags dstAccessMask; + VkDependencyFlags dependencyFlags; + }; + struct CompiledPass { RenderPass definition; std::vector attachments; std::vector colorRefs; VkAttachmentReference depthRef = {VK_ATTACHMENT_UNUSED, VK_IMAGE_LAYOUT_UNDEFINED}; - std::vector dependencies; VkRenderPass vkRenderPass = VK_NULL_HANDLE; VkFramebuffer framebuffer = VK_NULL_HANDLE; // Extent calculado durante la creación del framebuffer para usar en execute() VkExtent2D extent = {0, 0}; - // Barreras de transición antes del pass - std::vector prePassBarriers; + // Resource-specific barriers (resolved at execution time) + std::vector pendingResourceBarriers; + + // Pass-level dependencies (emitted as memory barriers at execution time) + std::vector passLevelDependencies; }; std::vector compiledPasses; @@ -182,7 +207,7 @@ class RenderGraph { void createRenderPasses(VkDevice dev); void createFramebuffers(VkDevice dev, VkExtent2D swapchainExtent); - void recordBarriers(VkCommandBuffer cmd, const CompiledPass& pass); + void recordBarriers(VkCommandBuffer cmd, const CompiledPass& pass, const ResourceRegistry& registry); }; } // namespace FarmEngine::Render