Source code for track_gen._src.generator_registry
"""Track-generator spec (canonical home of :class:`GeneratorSpec`) and the deprecated
module-level registry API.
A generator is a (name, alloc_scratch, generate) triple. ``config.generator`` selects
one by name. ``TrackGenerator`` resolves it once at construction through the public
registry, :data:`track_gen.plugin.registry` (kind ``"generator"``); the orchestrator
calls the resolved ``generate``. Built-in generators register explicitly from
``track_gen._src.builtin_plugins``; third parties use the ``"track_gen.plugins"`` entry
point (see :mod:`track_gen.plugin`). See docs/contributing/plugin-abis.rst for the
contract a ``GeneratorSpec`` generator implements.
Backward-compatibility shims (kept working, deprecated quietly): ``register(spec)``
(silent overwrite; a ``DeprecationWarning`` when it replaces an existing name),
``get(name)``, ``available()``, ``_ensure_loaded()`` and ``GENERATORS`` (a read-only
live view of the registry's generator kind) all delegate to
:data:`track_gen.plugin.registry`.
This module imports nothing from the package at load time (leaf): the registry is
imported lazily, so ``track_gen.plugin`` can re-export ``GeneratorSpec`` from here.
"""
from __future__ import annotations
import dataclasses
import types
from typing import Callable, ClassVar
[docs]
@dataclasses.dataclass(frozen=True)
class GeneratorSpec:
"""One registered generator.
name: the ``config.generator`` string that selects it. Third-party names
should be dotted (``"package.name"``).
alloc_scratch: ``(config) -> scratch`` — allocate this generator's PRIVATE working
buffers ONCE (fixed shapes from config, on config.device). The
generation OUTPUT buffers (centerline, valid) are owned by the
orchestrator and passed to ``generate``; they are NOT part of this.
generate: ``(seeds_wp, config, out_centerline, out_valid_wp, scratch) -> None`` —
write the closed centerline ([E*num_points] vec2f) into out_centerline
and per-env validity ([E] int32) into out_valid_wp, using scratch.
Pure Warp, in-place, zero-alloc, no host sync WHEN ``capturable=True``.
capturable: whether ``generate`` obeys the CUDA-graph-capture contract (pure Warp,
no host sync, no per-call allocation). Defaults ``True`` so existing
three-arg registrations are unchanged and stay on the capture-then-replay
CUDA path. A ``capturable=False`` generator may use host-side loop control
and per-call allocation; ``TrackGenerator`` runs it EAGERLY on CUDA every
call (the same code path as ``cpu``), never building a ``wp.Graph`` for it.
accepts_ctx: ``True`` when ``generate`` accepts a ``ctx=`` keyword (a
``track_gen.exec.LaunchContext``) and emits all of its device work through
it; the orchestrator then calls ``generate(..., ctx=ctx)``. Legacy plugins
leave it ``False`` and the orchestrator runs ``generate`` inside
``ctx.scope()``, so the ``warp_pipeline`` helpers they call (which default
``ctx=None`` to the ambient context) inherit the call's stream; their own
direct ``wp.launch`` calls still go to Warp's current stream.
params: optional :class:`track_gen.plugin.Params` subclass (STATIC fields
only) declaring this generator's own parameters. Users set them with
``TrackGenConfig(generator=name, generator_params=...)`` (an instance
or a mapping of its fields; ``None`` resolves
``params.from_legacy(config)``). ``generate`` / ``alloc_scratch`` read
the resolved instance as ``config.generator_params``, and
``registry.describe`` reports its JSON schema. ``None`` (the default):
the generator takes no ``generator_params``.
"""
#: Registry kind ``track_gen.plugin.registry.add(spec)`` infers for this spec type.
registry_kind: ClassVar[str] = "generator"
name: str
alloc_scratch: Callable
generate: Callable
capturable: bool = True
accepts_ctx: bool = False
params: "type | None" = None
def _registry():
from ..plugin._registry import registry
return registry
def register(spec: GeneratorSpec) -> None:
"""Deprecated: use ``track_gen.plugin.registry.add(spec)``.
Keeps the old semantics (an existing name is overwritten), implemented as
``add(spec, replace=True)`` with a ``DeprecationWarning`` when it replaces one.
"""
_registry()._add_legacy(spec, "generator")
def _ensure_loaded() -> None:
_registry()._ensure_loaded()
def get(name: str) -> "GeneratorSpec | type":
"""Deprecated alias of ``track_gen.plugin.registry.get("generator", name)``.
The value may be a :class:`~track_gen.plugin.Stage` CLASS (a Stage plugin), which
has no ``generate`` / ``alloc_scratch`` / ``capturable``: read capture support from
``registry.describe("generator", name).capturable``.
"""
return _registry().get("generator", name)
def available() -> list[str]:
"""Deprecated alias of ``track_gen.plugin.registry.available("generator")``."""
return _registry().available("generator")
def __getattr__(name: str):
# GENERATORS: read-only live view of the registry's generator kind (loads it). Its
# values may be Stage classes (no generate / alloc_scratch / capturable): enumerators
# read registry.describe("generator", name).capturable instead.
if name == "GENERATORS":
reg = _registry()
reg._ensure_loaded()
return types.MappingProxyType(reg._table("generator"))
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")