/* Crafter®.Graphics Copyright (C) 2026 Catcrafts® catcrafts.net This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3.0 as published by the Free Software Foundation; This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; #ifndef CRAFTER_GRAPHICS_WINDOW_DOM #include "vulkan/vulkan.h" #endif // !CRAFTER_GRAPHICS_WINDOW_DOM export module Crafter.Graphics:RenderPass; import std; import :GraphicsTypes; export namespace Crafter { struct Window; #ifndef CRAFTER_GRAPHICS_WINDOW_DOM // Conservative union of every pipeline stage that can write the swapchain // storage image: a compute pass via COMPUTE_SHADER, a ray-tracing pass via // RAY_TRACING_SHADER, plus TRANSFER for any blit/copy writer. Used as the // RenderPass default (a pass that doesn't narrow its own stage) and as the // fallback when there are no passes to derive a real union from. Far tighter // than ALL_COMMANDS, but never under-synchronises a polymorphic pass. inline constexpr VkPipelineStageFlags kSwapchainWriterStages = VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR | VK_PIPELINE_STAGE_TRANSFER_BIT; #endif // !CRAFTER_GRAPHICS_WINDOW_DOM struct RenderPass { virtual void Record(GraphicsCommandBuffer cmd, std::uint32_t frameIdx, Window& window) = 0; virtual ~RenderPass() = default; #ifndef CRAFTER_GRAPHICS_WINDOW_DOM // Pipeline stage at which this pass reads and writes the swapchain // storage image. The frame loop uses this to scope the inter-pass and // frame-edge barriers to the stages that actually touch the image // instead of ALL_COMMANDS. A subclass MUST narrow this to its real // stage (RTPass -> RAY_TRACING_SHADER, a compute pass -> COMPUTE_SHADER): // the default is the conservative writer union so an un-overridden pass // can never be under-synchronised, only over-synchronised. virtual VkPipelineStageFlags SwapchainStage() const { return kSwapchainWriterStages; } #endif // !CRAFTER_GRAPHICS_WINDOW_DOM }; #ifndef CRAFTER_GRAPHICS_WINDOW_DOM // Union of the swapchain-access stages declared by `passes` — the "real" // per-pass stage union the frame-edge barriers (acquire dst / present src) // narrow to: an all-compute frame yields COMPUTE_SHADER only, a frame with // any RT pass folds in RAY_TRACING_SHADER, and so on. Falls back to the // conservative writer union when there are no passes (the image is still // transitioned, just never written), so the barrier is always well-formed. inline VkPipelineStageFlags SwapchainStageUnion(std::span passes) { if (passes.empty()) return kSwapchainWriterStages; VkPipelineStageFlags stages = 0; for (RenderPass* pass : passes) stages |= pass->SwapchainStage(); return stages; } // The access mask matching a swapchain-writer stage union, for the // frame-edge barriers (acquire dst / present src). A barrier's access mask // must only contain access flags supported by its accompanying stage mask // (VUID-vkCmdPipelineBarrier-pImageMemoryBarriers-02820): TRANSFER_WRITE is // not a valid access for COMPUTE/RAY_TRACING stages, so hardcoding both // SHADER_WRITE and TRANSFER_WRITE fires the VUID every frame on an // all-compute frame. The swapchain image is written as a storage image by // compute/RT passes (SHADER_WRITE) and only via TRANSFER_WRITE when a // transfer-stage pass is present, so derive the access bits from the same // per-pass stage union the barrier's stage mask uses instead of hardcoding. inline VkAccessFlags SwapchainWriterAccess(VkPipelineStageFlags stages) { VkAccessFlags access = 0; if (stages & (VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR)) access |= VK_ACCESS_SHADER_WRITE_BIT; if (stages & VK_PIPELINE_STAGE_TRANSFER_BIT) access |= VK_ACCESS_TRANSFER_WRITE_BIT; return access; } // The inter-pass swapchain barrier (replacing the old queue-wide // VkMemoryBarrier at the #115 location). Scoped to the swapchain image's // single colour subresource so only that image's shader caches round-trip — // unrelated buffers/images stay resident — exactly as the intra-pass UI // barrier already does. The image is bound as a storage image and stays in // VK_IMAGE_LAYOUT_GENERAL, so this is a pure memory dependency (no layout // transition). The execution scope (stage masks) is supplied per pass pair // by the caller via vkCmdPipelineBarrier. inline VkImageMemoryBarrier BuildSwapchainInterPassBarrier(VkImage image) { return { .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, .dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, .oldLayout = VK_IMAGE_LAYOUT_GENERAL, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, .subresourceRange = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1, }, }; } #endif // !CRAFTER_GRAPHICS_WINDOW_DOM }