Source code for simvx.core.graphics.shader

"""Shader — pure source code (no GPU dependencies).

Backends (SDL3, Vulkan) handle compilation to GPU code.
"""


from __future__ import annotations

import logging
from dataclasses import dataclass
from pathlib import Path

log = logging.getLogger(__name__)


[docs] @dataclass class ShaderResource: """Describes a shader resource binding (uniforms, samplers, etc.).""" name: str binding: int type: str # "uniform_buffer", "storage_buffer", "sampler", etc. size: int = 0
[docs] class Shader: """Shader source code. Backend-agnostic. Stores GLSL source and metadata. Backends compile to SPIR-V or native GPU code. Example: vert_src = "layout(location=0) in vec3 pos; void main() { ... }" frag_src = "uniform vec4 colour; void main() { ... }" shader = Shader(vert_src, "vertex") # Then backend compiles it when rendering """ def __init__( self, source: str, stage: str, # "vertex" or "fragment" entrypoint: str = "main", defines: dict[str, str | int | bool] | None = None, ): """Initialize shader with GLSL source. Args: source: GLSL source code (not a file path) stage: "vertex" or "fragment" entrypoint: Shader entry point function name defines: Preprocessor defines {"DEFINE": "value"} """ self.source = source self.stage = stage.lower() self.entrypoint = entrypoint self.defines = defines or {} if self.stage not in ("vertex", "fragment"): raise ValueError(f"stage must be 'vertex' or 'fragment', got {stage}") @property def is_vertex(self) -> bool: return self.stage == "vertex" @property def is_fragment(self) -> bool: return self.stage == "fragment"
[docs] def to_dict(self) -> dict: """Serialize to dict for storage.""" return { "__type__": "Shader", "source": self.source, "stage": self.stage, "entrypoint": self.entrypoint, "defines": self.defines, }
[docs] @classmethod def from_dict(cls, d: dict) -> Shader: """Deserialize from dict.""" return cls( source=d["source"], stage=d["stage"], entrypoint=d.get("entrypoint", "main"), defines=d.get("defines"), )
[docs] @classmethod def from_file(cls, path: str | Path, stage: str) -> Shader: """Load GLSL source from file.""" path = Path(path) source = path.read_text() return cls(source, stage)