Source code for simvx.graphics.renderer.pass_helpers

"""Shared GPU resource creation helpers for render passes.

Pure factory functions — no base classes, no lifecycle management.
Each function creates a single Vulkan resource using common patterns
duplicated across multiple pass files.
"""


from __future__ import annotations

import logging
from pathlib import Path
from typing import Any

import vulkan as vk

from ..gpu.pipeline import create_shader_module
from ..materials.shader_compiler import compile_shader

log = logging.getLogger(__name__)

__all__ = [
    "load_shader_modules",
    "create_linear_sampler",
    "create_nearest_sampler",
    "create_sampler_descriptor_pool",
]


[docs] def load_shader_modules( device: Any, shader_dir: Path, vert_name: str, frag_name: str, ) -> tuple[Any, Any]: """Compile and load vertex/fragment shader modules from GLSL source files. Runs ``compile_shader`` (GLSL -> SPIR-V) then ``create_shader_module`` for each stage. Returns ``(vert_module, frag_module)``. """ vert_spv = compile_shader(shader_dir / vert_name) frag_spv = compile_shader(shader_dir / frag_name) return create_shader_module(device, vert_spv), create_shader_module(device, frag_spv)
[docs] def create_linear_sampler(device: Any) -> Any: """Create a linear-filtering sampler with CLAMP_TO_EDGE addressing. Used by text (MSDF atlas) and 2D overlay passes that need clamped UVs. """ return vk.vkCreateSampler(device, vk.VkSamplerCreateInfo( magFilter=vk.VK_FILTER_LINEAR, minFilter=vk.VK_FILTER_LINEAR, addressModeU=vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, addressModeV=vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, addressModeW=vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, anisotropyEnable=vk.VK_FALSE, unnormalizedCoordinates=vk.VK_FALSE, compareEnable=vk.VK_FALSE, mipmapMode=vk.VK_SAMPLER_MIPMAP_MODE_LINEAR, ), None)
[docs] def create_nearest_sampler(device: Any) -> Any: """Create a nearest-filtering sampler with CLAMP_TO_EDGE addressing. Used for pixel-art or integer-format textures where interpolation is unwanted. """ return vk.vkCreateSampler(device, vk.VkSamplerCreateInfo( magFilter=vk.VK_FILTER_NEAREST, minFilter=vk.VK_FILTER_NEAREST, addressModeU=vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, addressModeV=vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, addressModeW=vk.VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, anisotropyEnable=vk.VK_FALSE, unnormalizedCoordinates=vk.VK_FALSE, compareEnable=vk.VK_FALSE, mipmapMode=vk.VK_SAMPLER_MIPMAP_MODE_NEAREST, ), None)
[docs] def create_sampler_descriptor_pool( device: Any, layout: Any, *, max_sets: int = 1, descriptors_per_set: int = 1, ) -> tuple[Any, list[Any]]: """Create a descriptor pool and allocate sets for COMBINED_IMAGE_SAMPLER descriptors. Returns ``(pool, [descriptor_set, ...])``. """ total = max_sets * descriptors_per_set pool = vk.vkCreateDescriptorPool(device, vk.VkDescriptorPoolCreateInfo( maxSets=max_sets, poolSizeCount=1, pPoolSizes=[vk.VkDescriptorPoolSize( type=vk.VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, descriptorCount=total, )], ), None) sets = vk.vkAllocateDescriptorSets(device, vk.VkDescriptorSetAllocateInfo( descriptorPool=pool, descriptorSetCount=max_sets, pSetLayouts=[layout] * max_sets, )) return pool, list(sets) if max_sets > 1 else [sets[0] if sets else None]