Source code for simvx.graphics.renderer.passes

"""Shared render pass definitions."""

import logging
from typing import Any

import vulkan as vk

__all__ = [
    "create_render_pass",
    "create_offscreen_pass",
    "create_overlay_pass",
    "create_hdr_reload_pass",
    "create_pick_pass",
]

log = logging.getLogger(__name__)

[docs] def create_render_pass( device: Any, colour_format: int, depth_format: int = 0, ) -> Any: """Create a render pass with colour and optional depth attachment.""" attachments = [] colour_attachment = vk.VkAttachmentDescription( format=colour_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp=vk.VK_ATTACHMENT_STORE_OP_STORE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_UNDEFINED, finalLayout=vk.VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, ) attachments.append(colour_attachment) colour_ref = vk.VkAttachmentReference( attachment=0, layout=vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, ) depth_ref = None if depth_format: depth_attachment = vk.VkAttachmentDescription( format=depth_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_UNDEFINED, finalLayout=vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, ) attachments.append(depth_attachment) depth_ref = vk.VkAttachmentReference( attachment=1, layout=vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, ) subpass = vk.VkSubpassDescription( pipelineBindPoint=vk.VK_PIPELINE_BIND_POINT_GRAPHICS, colorAttachmentCount=1, pColorAttachments=[colour_ref], pDepthStencilAttachment=depth_ref, ) src_stages = vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT dst_stages = vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT dst_access = vk.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT if depth_format: src_stages |= vk.VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT dst_stages |= vk.VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT dst_access |= vk.VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT dependency = vk.VkSubpassDependency( srcSubpass=vk.VK_SUBPASS_EXTERNAL, dstSubpass=0, srcStageMask=src_stages, srcAccessMask=0, dstStageMask=dst_stages, dstAccessMask=dst_access, ) create_info = vk.VkRenderPassCreateInfo( attachmentCount=len(attachments), pAttachments=attachments, subpassCount=1, pSubpasses=[subpass], dependencyCount=1, pDependencies=[dependency], ) render_pass = vk.vkCreateRenderPass(device, create_info, None) log.debug("Render pass created (depth=%s)", bool(depth_format)) return render_pass
[docs] def create_offscreen_pass( device: Any, colour_format: int, depth_format: int = vk.VK_FORMAT_D32_SFLOAT, *, samplable_depth: bool = False, gbuffer_format: int = 0, ) -> Any: """Create a render pass for offscreen rendering (colour -> SHADER_READ_ONLY). When *samplable_depth* is True, the depth attachment transitions to SHADER_READ_ONLY_OPTIMAL so it can be sampled in a later pass (e.g. motion blur). When *gbuffer_format* is non-zero, a second colour attachment (the thin G-buffer) is added between the scene colour and depth. It clears and stores like the scene colour and ends in SHADER_READ_ONLY so a later compute/fragment pass can sample it. Attachment indices become colour=0, gbuffer=1, depth=2; with no G-buffer they stay colour=0, depth=1 (byte-identical to the historical pass). """ attachments = [ vk.VkAttachmentDescription( format=colour_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp=vk.VK_ATTACHMENT_STORE_OP_STORE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_UNDEFINED, finalLayout=vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, ), ] colour_refs = [vk.VkAttachmentReference( attachment=0, layout=vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, )] if gbuffer_format: attachments.append(vk.VkAttachmentDescription( format=gbuffer_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp=vk.VK_ATTACHMENT_STORE_OP_STORE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_UNDEFINED, finalLayout=vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, )) colour_refs.append(vk.VkAttachmentReference( attachment=1, layout=vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, )) depth_ref = None if depth_format: depth_final = ( vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL if samplable_depth else vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL ) depth_store = ( vk.VK_ATTACHMENT_STORE_OP_STORE if samplable_depth else vk.VK_ATTACHMENT_STORE_OP_DONT_CARE ) attachments.append(vk.VkAttachmentDescription( format=depth_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp=depth_store, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_UNDEFINED, finalLayout=depth_final, )) depth_ref = vk.VkAttachmentReference( attachment=len(colour_refs), layout=vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, ) subpass = vk.VkSubpassDescription( pipelineBindPoint=vk.VK_PIPELINE_BIND_POINT_GRAPHICS, colorAttachmentCount=len(colour_refs), pColorAttachments=colour_refs, pDepthStencilAttachment=depth_ref, ) src_stages = vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT dst_stages = vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT dst_access = vk.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT if depth_format: src_stages |= vk.VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT dst_stages |= vk.VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT dst_access |= vk.VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT dependencies = [ vk.VkSubpassDependency( srcSubpass=vk.VK_SUBPASS_EXTERNAL, dstSubpass=0, srcStageMask=src_stages, srcAccessMask=0, dstStageMask=dst_stages, dstAccessMask=dst_access, ), # Outgoing colour dependency: the colour write + the render pass's implicit # transition to SHADER_READ_ONLY must be made available to a later # fragment-shader sample in the SAME command buffer. Required by the # single-submit multi-scene model (SubViewport / reflection-probe SRUs are # written and then sampled by the main pass with no queue wait between). vk.VkSubpassDependency( srcSubpass=0, dstSubpass=vk.VK_SUBPASS_EXTERNAL, srcStageMask=vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, srcAccessMask=vk.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, dstStageMask=vk.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, dstAccessMask=vk.VK_ACCESS_SHADER_READ_BIT, ), ] # When depth is samplable, add a dependency so the fragment shader can read it if samplable_depth and depth_format: dependencies.append(vk.VkSubpassDependency( srcSubpass=0, dstSubpass=vk.VK_SUBPASS_EXTERNAL, srcStageMask=vk.VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, srcAccessMask=vk.VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, dstStageMask=vk.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, dstAccessMask=vk.VK_ACCESS_SHADER_READ_BIT, )) create_info = vk.VkRenderPassCreateInfo( attachmentCount=len(attachments), pAttachments=attachments, subpassCount=1, pSubpasses=[subpass], dependencyCount=len(dependencies), pDependencies=dependencies, ) render_pass = vk.vkCreateRenderPass(device, create_info, None) log.debug("Offscreen render pass created (samplable_depth=%s)", samplable_depth) return render_pass
[docs] def create_overlay_pass(device: Any, colour_format: int) -> Any: """Create a colour-only render pass that loads existing content (no clear). Used for drawing 2D overlays on top of a previously rendered target. Colour attachment uses LOAD_OP_LOAD and transitions to SHADER_READ_ONLY. No depth attachment. """ colour_attachment = vk.VkAttachmentDescription( format=colour_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_LOAD, storeOp=vk.VK_ATTACHMENT_STORE_OP_STORE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, finalLayout=vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, ) colour_ref = vk.VkAttachmentReference( attachment=0, layout=vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, ) subpass = vk.VkSubpassDescription( pipelineBindPoint=vk.VK_PIPELINE_BIND_POINT_GRAPHICS, colorAttachmentCount=1, pColorAttachments=[colour_ref], ) dependency = vk.VkSubpassDependency( srcSubpass=vk.VK_SUBPASS_EXTERNAL, dstSubpass=0, srcStageMask=vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, srcAccessMask=0, dstStageMask=vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, dstAccessMask=vk.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, ) render_pass = vk.vkCreateRenderPass(device, vk.VkRenderPassCreateInfo( attachmentCount=1, pAttachments=[colour_attachment], subpassCount=1, pSubpasses=[subpass], dependencyCount=1, pDependencies=[dependency], ), None) log.debug("Overlay render pass created") return render_pass
[docs] def create_hdr_reload_pass( device: Any, colour_format: int, depth_format: int = vk.VK_FORMAT_D32_SFLOAT, *, gbuffer_format: int = 0, ) -> Any: """Colour + depth LOAD render pass for the scene-read pass split. After the opaque family draws, the forward renderer ends the HDR pass, copies the HDR colour + depth into sampleable scene textures, then re-begins with THIS pass to draw the transparent phase. Both attachments load their existing content (opaque colour + depth) rather than clearing, so transparent geometry blends over the opaque scene and depth-tests against it. The final layouts match the offscreen HDR pass (colour -> SHADER_READ_ONLY for tonemap, depth -> DEPTH_STENCIL_READ_ONLY for the velocity / SSAO samplers), so downstream passes are byte-identical to the non-split path. When *gbuffer_format* is set (thin G-buffer), the second colour attachment is LOADED too (the opaque normal target is preserved; the transparent phase masks its writes to it), keeping this pass render-pass- compatible with the 2-attachment forward pipelines. """ colour_attachments = [vk.VkAttachmentDescription( format=colour_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_LOAD, storeOp=vk.VK_ATTACHMENT_STORE_OP_STORE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, finalLayout=vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, )] colour_refs = [vk.VkAttachmentReference( attachment=0, layout=vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, )] if gbuffer_format: colour_attachments.append(vk.VkAttachmentDescription( format=gbuffer_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_LOAD, storeOp=vk.VK_ATTACHMENT_STORE_OP_STORE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, finalLayout=vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, )) colour_refs.append(vk.VkAttachmentReference( attachment=1, layout=vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, )) depth_attachment = vk.VkAttachmentDescription( format=depth_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_LOAD, storeOp=vk.VK_ATTACHMENT_STORE_OP_STORE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, finalLayout=vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL, ) depth_ref = vk.VkAttachmentReference( attachment=len(colour_refs), layout=vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, ) subpass = vk.VkSubpassDescription( pipelineBindPoint=vk.VK_PIPELINE_BIND_POINT_GRAPHICS, colorAttachmentCount=len(colour_refs), pColorAttachments=colour_refs, pDepthStencilAttachment=depth_ref, ) # Subpass dependencies mirror the samplable-depth offscreen pass EXACTLY so # this pass is render-pass-compatible with the forward pipelines (which are # built against the offscreen HDR pass). The scene-read copy already inserts # explicit image barriers around the copy; these render-pass dependencies only # order the LOAD transition + the outgoing colour/depth samples. dependencies = [ vk.VkSubpassDependency( srcSubpass=vk.VK_SUBPASS_EXTERNAL, dstSubpass=0, srcStageMask=( vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | vk.VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT ), srcAccessMask=0, dstStageMask=( vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | vk.VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT ), dstAccessMask=( vk.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | vk.VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT ), ), # Outgoing colour: tonemap / bloom sample the colour after the pass. vk.VkSubpassDependency( srcSubpass=0, dstSubpass=vk.VK_SUBPASS_EXTERNAL, srcStageMask=vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, srcAccessMask=vk.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, dstStageMask=vk.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, dstAccessMask=vk.VK_ACCESS_SHADER_READ_BIT, ), # Outgoing depth: velocity / SSAO sample the depth after the pass. vk.VkSubpassDependency( srcSubpass=0, dstSubpass=vk.VK_SUBPASS_EXTERNAL, srcStageMask=vk.VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, srcAccessMask=vk.VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, dstStageMask=vk.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, dstAccessMask=vk.VK_ACCESS_SHADER_READ_BIT, ), ] all_attachments = [*colour_attachments, depth_attachment] render_pass = vk.vkCreateRenderPass(device, vk.VkRenderPassCreateInfo( attachmentCount=len(all_attachments), pAttachments=all_attachments, subpassCount=1, pSubpasses=[subpass], dependencyCount=len(dependencies), pDependencies=dependencies, ), None) log.debug("HDR reload render pass created (gbuffer=%s)", bool(gbuffer_format)) return render_pass
[docs] def create_shadow_pass(device: Any, depth_format: int = vk.VK_FORMAT_D32_SFLOAT) -> Any: """Create a depth-only render pass for shadow mapping.""" depth_attachment = vk.VkAttachmentDescription( format=depth_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp=vk.VK_ATTACHMENT_STORE_OP_STORE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_UNDEFINED, finalLayout=vk.VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, ) depth_ref = vk.VkAttachmentReference( attachment=0, layout=vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, ) subpass = vk.VkSubpassDescription( pipelineBindPoint=vk.VK_PIPELINE_BIND_POINT_GRAPHICS, colorAttachmentCount=0, pDepthStencilAttachment=depth_ref, ) # Two dependencies: write → read transition dependencies = [ vk.VkSubpassDependency( srcSubpass=vk.VK_SUBPASS_EXTERNAL, dstSubpass=0, srcStageMask=vk.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, srcAccessMask=vk.VK_ACCESS_SHADER_READ_BIT, dstStageMask=vk.VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, dstAccessMask=vk.VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, ), vk.VkSubpassDependency( srcSubpass=0, dstSubpass=vk.VK_SUBPASS_EXTERNAL, srcStageMask=vk.VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, srcAccessMask=vk.VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, dstStageMask=vk.VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, dstAccessMask=vk.VK_ACCESS_SHADER_READ_BIT, ), ] create_info = vk.VkRenderPassCreateInfo( attachmentCount=1, pAttachments=[depth_attachment], subpassCount=1, pSubpasses=[subpass], dependencyCount=2, pDependencies=dependencies, ) render_pass = vk.vkCreateRenderPass(device, create_info, None) log.debug("Shadow render pass created") return render_pass
[docs] def create_pick_pass(device: Any, depth_format: int = 0) -> Any: """Create the picking render pass (R32_UINT colour + optional depth).""" attachments = [ vk.VkAttachmentDescription( format=vk.VK_FORMAT_R32_UINT, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp=vk.VK_ATTACHMENT_STORE_OP_STORE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_UNDEFINED, finalLayout=vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, ), ] colour_ref = vk.VkAttachmentReference( attachment=0, layout=vk.VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, ) depth_ref = None if depth_format: attachments.append(vk.VkAttachmentDescription( format=depth_format, samples=vk.VK_SAMPLE_COUNT_1_BIT, loadOp=vk.VK_ATTACHMENT_LOAD_OP_CLEAR, storeOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, stencilLoadOp=vk.VK_ATTACHMENT_LOAD_OP_DONT_CARE, stencilStoreOp=vk.VK_ATTACHMENT_STORE_OP_DONT_CARE, initialLayout=vk.VK_IMAGE_LAYOUT_UNDEFINED, finalLayout=vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, )) depth_ref = vk.VkAttachmentReference( attachment=1, layout=vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, ) subpass = vk.VkSubpassDescription( pipelineBindPoint=vk.VK_PIPELINE_BIND_POINT_GRAPHICS, colorAttachmentCount=1, pColorAttachments=[colour_ref], pDepthStencilAttachment=depth_ref, ) src_stages = vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT dst_stages = vk.VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT dst_access = vk.VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT if depth_format: src_stages |= vk.VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT dst_stages |= vk.VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT dst_access |= vk.VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT dependency = vk.VkSubpassDependency( srcSubpass=vk.VK_SUBPASS_EXTERNAL, dstSubpass=0, srcStageMask=src_stages, srcAccessMask=0, dstStageMask=dst_stages, dstAccessMask=dst_access, ) create_info = vk.VkRenderPassCreateInfo( attachmentCount=len(attachments), pAttachments=attachments, subpassCount=1, pSubpasses=[subpass], dependencyCount=1, pDependencies=[dependency], ) render_pass = vk.vkCreateRenderPass(device, create_info, None) log.debug("Pick render pass created (depth=%s)", bool(depth_format)) return render_pass