Repository: Genesis-Embodied-AI/Genesis
File: genesis/engine/sensors/camera.py (Python)

Code before:
        manager: "gs.SensorManager",
    ):
        super().__init__(options, idx, data_cls, manager)
        self._options: RasterizerCameraOptions
        self._camera_node = None
        self._camera_target = None
        self._camera_wrapper = None
        self._camera_registered = False

    # ========================== Sensor Lifecycle ==========================

    def build(self):
        """Initialize the rasterizer and register this camera."""
        super().build()

        scene = self._manager._sim.scene

        if self._shared_metadata.sensors is None:
            self._shared_metadata.sensors = []
            self._shared_metadata.lights = gs.List()
            self._shared_metadata.image_cache = {}

            # Check if scene has a viewer - if so, use visualizer's rasterizer (like add_camera does)
            # This avoids OpenGL context conflicts since visualizer's rasterizer routes through viewer.render_offscreen()
            visualizer = getattr(scene, "visualizer", None)
            viewer = getattr(visualizer, "_viewer", None) if visualizer else None

            if viewer is not None:
                # Use visualizer's rasterizer - it will use viewer.render_offscreen() which handles context properly
                self._shared_metadata.context = visualizer.context
                self._shared_metadata.renderer = visualizer.rasterizer
            else:
                # No viewer - create standalone rasterizer with offscreen context
                self._shared_metadata.context = self._create_standalone_context(scene)
                self._shared_metadata.renderer = Rasterizer(viewer=None, context=self._shared_metadata.context)
                self._shared_metadata.renderer.build()

        self._shared_metadata.sensors.append(self)

        # Register camera now if standalone (offscreen), or defer to first render if using visualizer's rasterizer
        # (visualizer isn't built yet at sensor.build() time)
        if self._shared_metadata.renderer.offscreen:
            self._register_camera_with_renderer()

        _B = max(self._manager._sim.n_envs, 1)
        w, h = self._options.res
        self._shared_metadata.image_cache[self._idx] = torch.zeros((_B, h, w, 3), dtype=torch.uint8, device=gs.device)

    def _register_camera_with_renderer(self):
        """Register this camera with the renderer."""
        if self._camera_registered:

Review comment (suggestion):
`visualizer._viewer` is guaranteed to exist. No point to use `getattr` here. Moreover, no need to access it via visualizer, you could get it directly from the scene, which is the expected public API:
```python
viewer = scene.viewer
```

Code after:
        data_cls: Type[CameraData],
        manager: "gs.SensorManager",
    ):
        super().__init__(options, idx, data_cls, manager)
        self._options: RasterizerCameraOptions
        self._camera_node = None
        self._camera_target = None
        self._camera_wrapper = None
        self._is_camera_registered = False

    # ========================== Sensor Lifecycle ==========================

    def build(self):
        """Initialize the rasterizer and register this camera."""
        super().build()

        scene = self._manager._sim.scene

        if self._shared_metadata.sensors is None:
            self._shared_metadata.sensors = []
            self._shared_metadata.lights = gs.List()
            self._shared_metadata.image_cache = {}

            # If a viewer is active, reuse its windowed OpenGL context for both offscreen and onscreen
            # rendering, rather than creating a separate headless context which is fragile.
            if scene.viewer is not None:
                self._shared_metadata.context = scene.visualizer.context
                self._shared_metadata.renderer = scene.visualizer.rasterizer
            else:
                # No viewer - create standalone rasterizer with offscreen context
                self._shared_metadata.context = self._create_standalone_context(scene)
                self._shared_metadata.renderer = Rasterizer(viewer=None, context=self._shared_metadata.context)
                self._shared_metadata.renderer.build()

        self._shared_metadata.sensors.append(self)

        # Register camera now if standalone (offscreen), or defer to first render if using visualizer's rasterizer
        # (visualizer isn't built yet at sensor.build() time)
        if self._shared_metadata.renderer.offscreen:
            self._ensure_camera_registered()

        _B = max(self._manager._sim.n_envs, 1)
        w, h = self._options.res
        self._shared_metadata.image_cache[self._idx] = torch.zeros((_B, h, w, 3), dtype=torch.uint8, device=gs.device)

    def _ensure_camera_registered(self):
        """Register this camera with the renderer (no-op if already registered)."""
        if self._is_camera_registered:
            return

        # Add lights from options to the context