
.. _program_listing_file_Src_GraphicsEngineVulkan_common_RenderPassHelper.hpp:

Program Listing for File RenderPassHelper.hpp
=============================================

|exhale_lsh| :ref:`Return to documentation for file <file_Src_GraphicsEngineVulkan_common_RenderPassHelper.hpp>` (``Src/GraphicsEngineVulkan/common/RenderPassHelper.hpp``)

.. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS

.. code-block:: cpp

   #pragma once
   
   #include <span>
   
   #include <vulkan/vulkan.hpp>
   
   namespace Kataglyphis {
   
   // Every render pass in this engine declared its attachments the same way, by
   // hand, in five files: Rasterizer, DeferredRasterizer, PostStage, SkyBox and
   // CascadedShadowMap each spelled out the same eight or nine field
   // assignments. Three of those fields are the same in EVERY pass and are the
   // ones a copy-paste silently drops:
   //
   //   samples         = e1        - nothing here is multisampled
   //   stencilLoadOp   = eDontCare - no pass reads or writes a stencil aspect,
   //   stencilStoreOp  = eDontCare   even when the chosen depth format has one
   //                                 (chooseDepthFormat may return eD32SfloatS8Uint)
   //
   // so they are baked in here rather than repeated. The five fields the passes
   // genuinely disagree on stay parameters, with the majority variant as the
   // default: clear on load, store the result, and start from eUndefined (i.e.
   // discard whatever the previous frame left). PostStage is the one pass that
   // overrides all three - it LOADS an already-rendered colour attachment and
   // hands it to the presentation engine.
   //
   // A pass that needs a multisampled or stencil-carrying attachment must build
   // the vk::AttachmentDescription inline and say why, rather than growing this
   // helper new parameters - the same rule ViewportHelper.hpp states, for the
   // same reason.
   constexpr vk::AttachmentDescription buildAttachmentDescription(vk::Format format,
     vk::ImageLayout final_layout,
     vk::AttachmentLoadOp load_op = vk::AttachmentLoadOp::eClear,
     vk::AttachmentStoreOp store_op = vk::AttachmentStoreOp::eStore,
     vk::ImageLayout initial_layout = vk::ImageLayout::eUndefined)
   {
       vk::AttachmentDescription description{};
       description.format = format;
       description.samples = vk::SampleCountFlagBits::e1;
       description.loadOp = load_op;
       description.storeOp = store_op;
       description.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
       description.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
       description.initialLayout = initial_layout;
       description.finalLayout = final_layout;
       return description;
   }
   
   // Every render pass begin in this engine spelled out the same five field
   // assignments by hand, in Rasterizer, PostStage, DeferredRasterizer, SkyBox
   // and CascadedShadowMap - two of the five hard-coded clearValueCount as a
   // literal rather than deriving it from the clear-value array actually
   // passed.
   //
   // renderArea.offset is always {0, 0} and the extent is always the target's
   // full extent - every call site follows the beginRenderPass with
   // setFullExtentViewportAndScissor on that same extent. clearValueCount is
   // deliberately DERIVED from clear_values.size() rather than taken as a
   // parameter, so it can never drift from the array actually passed.
   //
   // A pass that needs a partial render area must build the
   // vk::RenderPassBeginInfo inline and say why, rather than growing this
   // helper new parameters - the same rule FramebufferHelper.hpp and
   // ViewportHelper.hpp state, for the same reason.
   //
   // Built via the fully-explicit vk::RenderPassBeginInfo constructor rather
   // than value-init-then-assign, for the same constexpr reason
   // FramebufferHelper.hpp documents.
   constexpr vk::RenderPassBeginInfo buildRenderPassBeginInfo(vk::RenderPass render_pass,
     vk::Framebuffer framebuffer,
     vk::Extent2D extent,
     std::span<const vk::ClearValue> clear_values)
   {
       return vk::RenderPassBeginInfo{ render_pass, framebuffer, vk::Rect2D{ vk::Offset2D{ 0, 0 }, extent },
           static_cast<uint32_t>(clear_values.size()), clear_values.data() };
   }
   
   // Every render pass in this engine spelled out the same six field
   // assignments by hand, in Rasterizer, PostStage, DeferredRasterizer, SkyBox
   // and CascadedShadowMap - SkyBox hard-coded attachmentCount = 2 next to a
   // two-element std::array instead of deriving it, exactly as it did for
   // FramebufferHelper.hpp's attachmentCount.
   //
   // attachmentCount, subpassCount and dependencyCount are all deliberately
   // DERIVED from their span's .size() rather than taken as parameters, so none
   // of the three can drift from the array actually passed in.
   //
   // Lifetime note: the returned vk::RenderPassCreateInfo borrows all three
   // spans' .data() pointers - they must outlive the createRenderPass call that
   // consumes it.
   //
   // flags and pNext are deliberately left at their defaults so a pass that
   // needs either - CascadedShadowMap chains a
   // vk::RenderPassMultiviewCreateInfo through pNext - assigns it on the
   // returned value rather than this helper growing a parameter, the same rule
   // RenderPassHelper.hpp's other two helpers and FramebufferHelper.hpp state.
   //
   // Built via the fully-explicit vk::RenderPassCreateInfo constructor rather
   // than value-init-then-assign, for the same constexpr reason
   // FramebufferHelper.hpp documents.
   // Every render pass in this engine spelled out the same three or four field
   // assignments by hand, in Rasterizer, PostStage, DeferredRasterizer, SkyBox
   // and CascadedShadowMap - three of the five hard-coded colorAttachmentCount
   // as a literal (usually 1) next to a single-element reference instead of
   // deriving it, the same drift buildRenderPassCreateInfo's attachmentCount
   // fixed for the attachment array.
   //
   // colorAttachmentCount and inputAttachmentCount are both deliberately
   // DERIVED from their span's .size() rather than taken as parameters.
   // pipelineBindPoint is always eGraphics: a compute or ray-tracing subpass
   // does not go through a vk::RenderPass at all, so none of the five passes
   // this replaced ever set anything else.
   //
   // pResolveAttachments, preserveAttachmentCount and flags are deliberately
   // left at their defaults - nothing in this engine resolves a multisampled
   // attachment or preserves one across subpasses. A pass that needs either
   // must build the vk::SubpassDescription inline and say why, rather than
   // growing this helper new parameters - the same rule this file's other two
   // helpers and ViewportHelper.hpp state.
   //
   // Lifetime note: the returned vk::SubpassDescription borrows both spans'
   // .data() pointers and depth_attachment - they must all outlive the
   // createRenderPass call that consumes it (via buildRenderPassCreateInfo).
   constexpr vk::SubpassDescription buildSubpassDescription(
     std::span<const vk::AttachmentReference> color_attachments,
     const vk::AttachmentReference *depth_attachment,
     std::span<const vk::AttachmentReference> input_attachments = {})
   {
       vk::SubpassDescription subpass{};
       subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
       subpass.colorAttachmentCount = static_cast<uint32_t>(color_attachments.size());
       subpass.pColorAttachments = color_attachments.data();
       subpass.pDepthStencilAttachment = depth_attachment;
       subpass.inputAttachmentCount = static_cast<uint32_t>(input_attachments.size());
       subpass.pInputAttachments = input_attachments.data();
       return subpass;
   }
   
   constexpr vk::RenderPassCreateInfo buildRenderPassCreateInfo(
     std::span<const vk::AttachmentDescription> attachments,
     std::span<const vk::SubpassDescription> subpasses,
     std::span<const vk::SubpassDependency> dependencies)
   {
       return vk::RenderPassCreateInfo{ vk::RenderPassCreateFlags{}, static_cast<uint32_t>(attachments.size()),
           attachments.data(), static_cast<uint32_t>(subpasses.size()), subpasses.data(),
           static_cast<uint32_t>(dependencies.size()), dependencies.data() };
   }
   
   // Rasterizer, DeferredRasterizer and PostStage each spelled out this same
   // external dependency by hand for their single-subpass depth buffer: it is
   // written by one pass and cleared by the next, and the buffer is shared
   // across frames in flight, so the previous frame's write must be made
   // available before this frame's clear or draw touches it
   // (SYNC-HAZARD-WRITE-AFTER-WRITE otherwise - see Rasterizer.cpp's history,
   // which is where this was first found and fixed). dstSubpass is always 0:
   // none of the three passes this covers has more than one subpass.
   //
   // dependencyFlags is deliberately empty - eByRegion only makes sense for a
   // dependency local to the current frame's tile memory; a cross-frame
   // dependency like this one is not by-region.
   //
   // SkyBox and CascadedShadowMap keep their own inline dependency rather than
   // this helper: SkyBox's dstAccessMask carries a second, different member
   // (eColorAttachmentRead), and CascadedShadowMap's srcStageMask/srcAccessMask
   // cover a sampled shadow map (eFragmentShader/eShaderRead) - a genuinely
   // different edge, not a copy-paste. See
   // BuildIntegrity.NoRasterStageHandRollsItsExternalSubpassDependency in
   // buildIntegritySuite.cpp for the check that keeps it that way.
   constexpr vk::SubpassDependency buildExternalColorDepthDependency()
   {
       vk::SubpassDependency dependency{};
       dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
       dependency.dstSubpass = 0;
       dependency.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput
                                 | vk::PipelineStageFlagBits::eEarlyFragmentTests
                                 | vk::PipelineStageFlagBits::eLateFragmentTests;
       dependency.srcAccessMask =
         vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
       dependency.dstStageMask =
         vk::PipelineStageFlagBits::eColorAttachmentOutput | vk::PipelineStageFlagBits::eEarlyFragmentTests;
       dependency.dstAccessMask =
         vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eDepthStencilAttachmentWrite;
       dependency.dependencyFlags = vk::DependencyFlags{};
       return dependency;
   }
   
   // PostStage's colour-only twin of buildExternalColorDepthDependency: once
   // PostStage stopped owning a depth attachment (it never read the one it
   // cleared - see the removal that added this helper), its external dependency
   // no longer has a depth half to cover, and simply narrows to ordering this
   // frame's colour load/write against the previous colour write into the same
   // swapchain image (the SkyBox pass, which renders into it first).
   constexpr vk::SubpassDependency buildExternalColorDependency()
   {
       vk::SubpassDependency dependency{};
       dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
       dependency.dstSubpass = 0;
       dependency.srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
       dependency.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
       dependency.dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
       dependency.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
       dependency.dependencyFlags = vk::DependencyFlags{};
       return dependency;
   }
   
   // Destroys a render pass and nulls its handle - the same idempotence rule
   // FramebufferHelper.hpp's destroyFramebuffer/destroyFramebuffers and
   // PipelineLayoutHelper.hpp's destroyPipelineAndLayout follow: a device-less
   // call (already torn down, or never had a device) is a no-op rather than a
   // crash, so an explicit cleanUp followed by the destructor's safety net stays
   // safe. Rasterizer, DeferredRasterizer, PostStage, SkyBox and
   // CascadedShadowMap each hand-rolled this. The handle is taken by reference
   // for the same reason destroyPipelineAndLayout takes its handles by
   // reference; passing by value would destroy without nulling and leave the
   // caller holding a dangling handle.
   inline void destroyRenderPass(vk::Device device, vk::RenderPass &render_pass)
   {
       if (!device) { return; }
       if (render_pass) {
           device.destroyRenderPass(render_pass);
           render_pass = nullptr;
       }
   }
   
   }// namespace Kataglyphis
