"""RenderContext — setup-time GPU facade.
Passes receive this in setup() instead of raw engine references. Consolidates
GPU resource creation entry points and exposes concrete Vulkan handles as plain
attributes. Does NOT wrap per-frame command recording — passes call vk.* directly
inside record().
For a future Metal backend, a MetalRenderContext subclass would hold Metal
equivalents in the same-named attributes.
"""
from __future__ import annotations
import pathlib
from typing import Any
from .resource_registry import ResourceRegistry
__all__ = ["RenderContext"]
[docs]
class RenderContext:
"""Setup-time GPU facade. Created once by ForwardRenderer, passed to pass.setup()."""
__slots__ = (
"device", "physical_device", "graphics_queue", "command_pool",
"texture_descriptor_layout", "texture_descriptor_set",
"resources", "width", "height", "frames_in_flight",
"render_pass", "shader_dir", "extent",
# Engine back-reference (escape hatch for vendor-specific needs)
"_engine",
)
def __init__(
self,
engine: Any,
resources: ResourceRegistry,
) -> None:
self._engine = engine
self.device: Any = engine._device
self.physical_device: Any = engine._physical_device
self.graphics_queue: Any = engine._graphics_queue
self.command_pool: Any = engine._cmd_ctx.pool
self.texture_descriptor_layout: Any = engine.texture_descriptor_layout
self.texture_descriptor_set: Any = engine.texture_descriptor_set
self.resources = resources
w, h = engine.extent
self.width: int = w
self.height: int = h
self.extent: tuple[int, int] = (w, h)
self.render_pass: Any = engine.render_pass
self.shader_dir: pathlib.Path = engine.shader_dir
from .._types import FRAMES_IN_FLIGHT
self.frames_in_flight: int = FRAMES_IN_FLIGHT
# -- Resource creation helpers (thin delegates to existing gpu/* functions) --
[docs]
def create_buffer(
self, size: int, usage: int, properties: int,
) -> tuple[Any, Any]:
"""Create a Vulkan buffer + memory. Returns (buffer, memory)."""
from ..gpu.memory import create_buffer
return create_buffer(self.device, self.physical_device, size, usage, properties)
[docs]
def create_image(
self, width: int, height: int, fmt: int, usage: int,
) -> tuple[Any, Any]:
"""Create a Vulkan image + memory. Returns (image, memory)."""
from ..gpu.memory import create_image
return create_image(self.device, self.physical_device, width, height, fmt, usage)
[docs]
def create_sampler(self) -> Any:
"""Create a default linear sampler."""
from ..gpu.memory import create_sampler
return create_sampler(self.device)
[docs]
def load_shader(self, filename: str) -> Any:
"""Compile a shader from the engine's shader directory and create a module."""
from ..gpu.pipeline import create_shader_module
from ..materials.shader_compiler import compile_shader
spv = compile_shader(self.shader_dir / filename)
return create_shader_module(self.device, spv)
[docs]
def upload_image_data(
self, pixels: Any, width: int, height: int, fmt: int,
) -> tuple[Any, Any]:
"""Upload pixel data to a GPU image. Returns (image, memory)."""
from ..gpu.memory import upload_image_data
return upload_image_data(
self.device, self.physical_device,
self.graphics_queue, self.command_pool,
pixels, width, height, fmt,
)
[docs]
def transition_image_layout(
self, image: Any, old_layout: int, new_layout: int,
) -> None:
"""Transition an image between layouts (setup-time only)."""
from ..gpu.memory import transition_image_layout
transition_image_layout(
self.device, self.graphics_queue, self.command_pool,
image, old_layout, new_layout,
)
[docs]
def upload_numpy(self, memory: Any, data: Any) -> None:
"""Upload numpy array data to mapped GPU memory."""
from ..gpu.memory import upload_numpy
upload_numpy(self.device, memory, data)