"""Shared render pass definitions."""
from __future__ import annotations
import logging
from typing import Any
import vulkan as vk
__all__ = ["create_render_pass", "create_offscreen_pass", "create_pick_pass"]
log = logging.getLogger(__name__)
[docs]
def create_render_pass(
device: Any, color_format: int, depth_format: int = 0,
) -> Any:
"""Create a render pass with colour and optional depth attachment."""
attachments = []
color_attachment = vk.VkAttachmentDescription(
format=color_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(color_attachment)
color_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=[color_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, color_format: int, depth_format: int = vk.VK_FORMAT_D32_SFLOAT,
*, samplable_depth: bool = False,
) -> 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).
"""
attachments = [
vk.VkAttachmentDescription(
format=color_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,
),
]
color_ref = vk.VkAttachmentReference(
attachment=0,
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=1,
layout=vk.VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
)
subpass = vk.VkSubpassDescription(
pipelineBindPoint=vk.VK_PIPELINE_BIND_POINT_GRAPHICS,
colorAttachmentCount=1,
pColorAttachments=[color_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
dependencies = [
vk.VkSubpassDependency(
srcSubpass=vk.VK_SUBPASS_EXTERNAL,
dstSubpass=0,
srcStageMask=src_stages,
srcAccessMask=0,
dstStageMask=dst_stages,
dstAccessMask=dst_access,
),
]
# 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_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,
),
]
color_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=[color_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