"""PipelineManager — owns the forward renderer's 3D graphics pipelines.
Extracted from Renderer so pipeline creation, rebuild-on-HDR-switch,
resize, and cleanup live in one place. Renderer re-exports the
pipeline handles as attributes so SceneContentRenderer keeps accessing
``renderer._pipeline`` etc. without change.
"""
from typing import Any
import vulkan as vk
from ..gpu.pipeline import (
create_forward_pipeline,
create_shader_module,
create_skinned_pipeline,
create_transparent_pipeline,
)
from ..materials.shader_compiler import compile_shader
__all__ = ["PipelineManager"]
[docs]
class PipelineManager:
"""Owns the 3D forward-render pipelines and their shader modules."""
def __init__(self, engine: Any) -> None:
self._engine = engine
self.vert_module: Any = None
self.frag_module: Any = None
self.skinned_vert_module: Any = None
self.opaque: Any = None
self.opaque_layout: Any = None
self.nocull: Any = None
self.nocull_layout: Any = None
self.transparent: Any = None
self.transparent_layout: Any = None
self.skinned: Any = None
self.skinned_layout: Any = None
self._ssbo_layout: Any = None
self._joint_layout: Any = None
[docs]
def setup(self, ssbo_layout: Any, joint_layout: Any) -> None:
"""Compile shaders and build the four 3D pipelines against the engine render pass."""
self._ssbo_layout = ssbo_layout
self._joint_layout = joint_layout
e = self._engine
device = e.ctx.device
shader_dir = e.shader_dir
vert_spv = compile_shader(shader_dir / "cube.vert")
frag_spv = compile_shader(shader_dir / "cube_textured.frag")
self.vert_module = create_shader_module(device, vert_spv)
self.frag_module = create_shader_module(device, frag_spv)
skinned_vert_spv = compile_shader(shader_dir / "skinned.vert")
self.skinned_vert_module = create_shader_module(device, skinned_vert_spv)
self._build_pipelines(e.render_pass, e.extent)
[docs]
def rebuild_for_render_pass(self, render_pass: Any) -> None:
"""Rebuild all four pipelines against ``render_pass`` (e.g. HDR target)."""
self._destroy_pipelines()
self._build_pipelines(render_pass, self._engine.extent)
[docs]
def rebuild_for_resize(self, width: int, height: int, render_pass: Any) -> None:
"""Rebuild all four pipelines for a new framebuffer size."""
self._destroy_pipelines()
self._build_pipelines(render_pass, (width, height))
[docs]
def cleanup(self) -> None:
"""Destroy pipelines, layouts, and shader modules."""
self._destroy_pipelines()
device = self._engine.ctx.device
for mod in (self.skinned_vert_module, self.vert_module, self.frag_module):
if mod:
vk.vkDestroyShaderModule(device, mod, None)
self.skinned_vert_module = self.vert_module = self.frag_module = None
def _build_pipelines(self, render_pass: Any, extent: Any) -> None:
e = self._engine
device = e.ctx.device
tex_layout = e.texture_descriptor_layout
self.opaque, self.opaque_layout = create_forward_pipeline(
device, self.vert_module, self.frag_module, render_pass, extent,
self._ssbo_layout, texture_layout=tex_layout,
)
self.nocull, self.nocull_layout = create_forward_pipeline(
device, self.vert_module, self.frag_module, render_pass, extent,
self._ssbo_layout, texture_layout=tex_layout, double_sided=True,
)
self.transparent, self.transparent_layout = create_transparent_pipeline(
device, self.vert_module, self.frag_module, render_pass, extent,
self._ssbo_layout, texture_layout=tex_layout,
)
self.skinned, self.skinned_layout = create_skinned_pipeline(
device, self.skinned_vert_module, self.frag_module, render_pass, extent,
self._ssbo_layout, texture_layout=tex_layout, joint_layout=self._joint_layout,
)
def _destroy_pipelines(self) -> None:
device = self._engine.ctx.device
for pipeline, layout in (
(self.opaque, self.opaque_layout),
(self.nocull, self.nocull_layout),
(self.transparent, self.transparent_layout),
(self.skinned, self.skinned_layout),
):
if pipeline:
vk.vkDestroyPipeline(device, pipeline, None)
if layout:
vk.vkDestroyPipelineLayout(device, layout, None)
self.opaque = self.opaque_layout = None
self.nocull = self.nocull_layout = None
self.transparent = self.transparent_layout = None
self.skinned = self.skinned_layout = None