Let run-time variations apply build-time effects#907
Conversation
- Add VariationBase.apply_build_time_effects (default no-op) and have the env builder call it on every enabled variation, so a run-time variation can also mutate asset configs before the scene is built. - Rename the BuildTimeVariationBase abstract apply() to apply_build_time_effects and update the existing build-time variations and tests accordingly. Signed-off-by: alex <amillane@nvidia.com>
| if not isinstance(variation, BuildTimeVariationBase): | ||
| continue | ||
| variation.apply() | ||
| variation.apply_build_time_effects() |
There was a problem hiding this comment.
🟡 New build-time hook for run-time variations has no test
The tests here still only exercise BuildTimeVariationBase, which was already applied at build time before this change. Nothing covers the actual new behavior: that a RunTimeVariationBase whose apply_build_time_effects is overridden gets called during compose_manager_cfg. Could we add a small test — a run-time variation that mutates a config in apply_build_time_effects, enable it, build, and assert the mutation took effect? That would guard the mechanism MR3 depends on and catch a future regression if this loop ever re-gains a type filter.
🤖 Isaac Lab-Arena Review BotSummaryThis PR turns the build-time Findings🟡 Warning: arena_env_builder.py:160 — The one genuinely new behavior (a run-time variation getting Test CoverageExisting build-time tests were correctly updated to the new method name and still pass. Missing: direct coverage of the new run-time → build-time path (see the warning above). Not a blocker given this is the mechanism-only MR of a three-part stack, but worth adding here rather than deferring. VerdictShip it (consider adding the run-time-path test). |
alexmillane
left a comment
There was a problem hiding this comment.
self review 2
| Calls ``apply_build_time_effects`` on each enabled variation regardless of type: a | ||
| :class:`BuildTimeVariationBase` realises its whole effect there (e.g. a dome light's | ||
| spawner texture), while a run-time variation uses it for build-time preconditions (e.g. | ||
| forcing its camera untiled). These mutate asset configs in place, so this must run before | ||
| the scene is materialised. |
There was a problem hiding this comment.
Restore the details section of the docstring to what it was.
| Called once per env build for every enabled variation, regardless of type. Default: | ||
| no-op. A :class:`BuildTimeVariationBase` realises its whole effect here; a run-time | ||
| variation overrides this only when it needs a build-time precondition (e.g. forcing a | ||
| camera untiled so per-env run-time edits take effect). |
There was a problem hiding this comment.
remove: regardless of type
remove :class: markings.
remove "(e.g. forcing a...)
| Use for properties that can't change in-flight: HDR maps, USD swaps, | ||
| spawner params baked into a config. Subclasses hold references to the | ||
| asset(s) they mutate. | ||
| asset(s) they mutate and realise the effect in :meth:`apply_build_time_effects`. |
There was a problem hiding this comment.
remove :meth: marker.
Greptile SummaryThis PR lifts the
Confidence Score: 5/5Safe to merge. The change is a clean upward refactor of a single hook method, with no logic mutations in existing build-time variations and a well-scoped no-op default for run-time variations. The rename from apply() to apply_build_time_effects() is complete — no remaining .apply() callers exist anywhere in the codebase. The isinstance guard removal is correct: RunTimeVariationBase subclasses inherit the no-op from VariationBase, and BuildTimeVariationBase re-declares the method as @AbstractMethod, preserving the enforcement on its subclasses. The BuildTimeVariationBase import removal from arena_env_builder.py is correct (still imported where needed in variations_printing.py). All three variation files and both test files are updated consistently. No files require special attention. All changed files are internally consistent and the codebase-wide grep confirms no stale .apply() callers remain. Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Builder as ArenaEnvBuilder
participant BTV as BuildTimeVariationBase subclass
participant RTV as RunTimeVariationBase subclass
participant VB as VariationBase
Builder->>Builder: _apply_build_time_variations()
loop every enabled variation
alt BuildTimeVariationBase subclass
Builder->>BTV: apply_build_time_effects()
Note right of BTV: @abstractmethod — samples and mutates asset config
else RunTimeVariationBase subclass (no override)
Builder->>RTV: apply_build_time_effects()
RTV->>VB: (inherits no-op from VariationBase)
Note right of VB: Returns None — nothing mutated
else RunTimeVariationBase subclass (with override)
Builder->>RTV: apply_build_time_effects()
Note right of RTV: Sets build-time precondition (e.g. untile camera)
end
end
Builder->>Builder: compose scene_cfg (materialise)
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Builder as ArenaEnvBuilder
participant BTV as BuildTimeVariationBase subclass
participant RTV as RunTimeVariationBase subclass
participant VB as VariationBase
Builder->>Builder: _apply_build_time_variations()
loop every enabled variation
alt BuildTimeVariationBase subclass
Builder->>BTV: apply_build_time_effects()
Note right of BTV: @abstractmethod — samples and mutates asset config
else RunTimeVariationBase subclass (no override)
Builder->>RTV: apply_build_time_effects()
RTV->>VB: (inherits no-op from VariationBase)
Note right of VB: Returns None — nothing mutated
else RunTimeVariationBase subclass (with override)
Builder->>RTV: apply_build_time_effects()
Note right of RTV: Sets build-time precondition (e.g. untile camera)
end
end
Builder->>Builder: compose scene_cfg (materialise)
Reviews (1): Last reviewed commit: "Merge branch 'main' into alex/feature/va..." | Re-trigger Greptile |
alexmillane
left a comment
There was a problem hiding this comment.
self review 3
| self._sampler.add_listener(listener) | ||
|
|
||
| def apply_build_time_effects(self) -> None: | ||
| """Sample and mutate the bound asset config(s) in place before the scene is materialised. |
There was a problem hiding this comment.
Update: Sample and mutate the bound assets in place in the ArenaEnvBuilder before the environment cfg is composed.
Signed-off-by: alex <amillane@nvidia.com>
The apply() method was not renamed to apply_build_time_effects() during the main merge, leaving LightColorVariation with an unimplemented abstract method so it could not be instantiated. This broke every test that builds a scene containing a light. Signed-off-by: alex <amillane@nvidia.com>
| if self._sampler is not None: | ||
| self._sampler.add_listener(listener) | ||
|
|
||
| def apply_build_time_effects(self) -> None: |
There was a problem hiding this comment.
Is "effect" a bit vaque here? I think we are doing build-time realization and preparation for later run-time realization here. So this function serves two different purposes: a BuildTimeVariationBase realizes its sampled variation, whereas a run-time variation only prepares configuration required by its later event.
Only a suggestion, could we make the distinction explicit while leaving the builder with one call with something like
class VariationBase(ABC):
def prepare_for_build(self) -> None:
"""Configure prerequisites required before environment construction."""
pass
def realize_at_build_time(self) -> None:
"""Realize this variation during environment construction."""
pass
def configure_at_build_time(self) -> None:
"""Run this variation's build-time preparation and realization."""
self.prepare_for_build()
self.realize_at_build_time()
BuildTimeVariationBase makes self.realize_at_build_time() mandatory:
class BuildTimeVariationBase(VariationBase):
@abstractmethod
def realize_at_build_time(self) -> None:
"""Sample and apply this variation to its target configuration."""
...
A run-time variation overrides self.prepare_for_build() when necessary:
class CameraIntrinsicsRunTimeVariation(RunTimeVariationBase):
def prepare_for_build(self) -> None:
"""Configure the camera for per-environment run-time updates."""
if self._camera_rig is not None:
self._camera_rig.set_use_tiled_camera(False)
and the arena_env_builder.py retains one call:
variation.configure_at_build_time()
Summary
Allow any variation (not just build-time ones) to perform build-time mutations.
Detail
EnvironmentCfgat build time.BuildTimeVariationBaseabstractapply()toapply_build_time_effects().Notes