From c9a1c8c2a7f5765dd694d4ae3ec2a0cb181f9b73 Mon Sep 17 00:00:00 2001 From: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> Date: Fri, 21 Aug 2026 14:01:17 +0100 Subject: [PATCH 1/2] Reset Hydra texture state when render product changes --- .../nodes/OgnIsaacAttachHydraTexture.py | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/source/extensions/isaacsim.core.nodes/python/nodes/OgnIsaacAttachHydraTexture.py b/source/extensions/isaacsim.core.nodes/python/nodes/OgnIsaacAttachHydraTexture.py index 590127c69c..37f3226011 100644 --- a/source/extensions/isaacsim.core.nodes/python/nodes/OgnIsaacAttachHydraTexture.py +++ b/source/extensions/isaacsim.core.nodes/python/nodes/OgnIsaacAttachHydraTexture.py @@ -35,6 +35,7 @@ class OgnIsaacAttachHydraTextureInternalState(BaseResetNode): def __init__(self) -> None: self.hydra_texture = None self.applied_render_vars = set() + self.render_product_path = None self.rp_sub_stop = None self.rp_sub_play = None self.drawable_changed_sub = None @@ -43,6 +44,22 @@ def __init__(self) -> None: self.is_async = settings.get("/app/asyncRendering") or False super().__init__(initialize=False) + def set_render_product(self, render_product_path: str) -> None: + """Reset cached attachment state when the render product target changes. + + Args: + render_product_path: Render product path used by the current evaluation. + """ + if self.render_product_path == render_product_path: + return + if self.hydra_texture is not None: + self.hydra_texture.set_updates_enabled(False) + self.hydra_texture = None + self.applied_render_vars.clear() + self.rp_sub_stop = None + self.rp_sub_play = None + self.render_product_path = render_product_path + def on_timeline_stop(self, event: carb.eventdispatcher.Event) -> None: """Disable hydra texture updates when the timeline stops. @@ -159,6 +176,8 @@ def compute(db: Any) -> bool: db.log_error(f'Invalid RenderProduct prim: "{render_product_path}"') return False + state.set_render_product(render_product_path) + with Usd.EditContext(stage, stage.GetSessionLayer()): # Apply render vars render_vars = db.inputs.renderVars @@ -252,6 +271,7 @@ def release_instance(node: Any, graph_instance_id: Any) -> None: if state is not None: # Clean up the hydra texture state.hydra_texture = None + state.render_product_path = None state.rp_sub_stop = None state.rp_sub_play = None state.drawable_changed_sub = None From b26d2ab5bb87e5066e8f4e97820763a9c1535cb5 Mon Sep 17 00:00:00 2001 From: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> Date: Fri, 21 Aug 2026 14:01:40 +0100 Subject: [PATCH 2/2] Add Hydra texture retarget regression tests --- .../test_attach_hydra_texture_retarget.py | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 source/extensions/isaacsim.core.nodes/python/tests/test_attach_hydra_texture_retarget.py diff --git a/source/extensions/isaacsim.core.nodes/python/tests/test_attach_hydra_texture_retarget.py b/source/extensions/isaacsim.core.nodes/python/tests/test_attach_hydra_texture_retarget.py new file mode 100644 index 0000000000..f185a09721 --- /dev/null +++ b/source/extensions/isaacsim.core.nodes/python/tests/test_attach_hydra_texture_retarget.py @@ -0,0 +1,55 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +"""Tests for render-product retargeting in IsaacAttachHydraTexture.""" + +import importlib.util +from pathlib import Path +from unittest.mock import MagicMock + +import omni.kit.test + +MODULE_PATH = Path(__file__).resolve().parents[1] / "nodes" / "OgnIsaacAttachHydraTexture.py" +SPEC = importlib.util.spec_from_file_location("_attach_hydra_texture_retarget", MODULE_PATH) +MODULE = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(MODULE) +State = MODULE.OgnIsaacAttachHydraTextureInternalState + + +class TestAttachHydraTextureRetarget(omni.kit.test.AsyncTestCase): + """Verify cached Hydra state follows the current render product target.""" + + async def test_new_render_product_clears_cached_attachment_state(self) -> None: + """Switching targets should force texture and render-var recreation.""" + state = State.__new__(State) + old_texture = MagicMock() + state.hydra_texture = old_texture + state.applied_render_vars = {"LdrColor", "Depth"} + state.render_product_path = "/Render/ProductA" + state.rp_sub_stop = object() + state.rp_sub_play = object() + + state.set_render_product("/Render/ProductB") + + old_texture.set_updates_enabled.assert_called_once_with(False) + self.assertIsNone(state.hydra_texture) + self.assertEqual(state.applied_render_vars, set()) + self.assertIsNone(state.rp_sub_stop) + self.assertIsNone(state.rp_sub_play) + self.assertEqual(state.render_product_path, "/Render/ProductB") + + async def test_same_render_product_preserves_cached_state(self) -> None: + """Repeated evaluation of the same target should keep the existing texture.""" + state = State.__new__(State) + texture = MagicMock() + state.hydra_texture = texture + state.applied_render_vars = {"LdrColor"} + state.render_product_path = "/Render/ProductA" + state.rp_sub_stop = object() + state.rp_sub_play = object() + + state.set_render_product("/Render/ProductA") + + texture.set_updates_enabled.assert_not_called() + self.assertIs(state.hydra_texture, texture) + self.assertEqual(state.applied_render_vars, {"LdrColor"})