Files
mindbot/deps/cloudxr/helpers/WebGlUtils.ts
yt lee 623e05f250 Add CloudXR VR streaming support for PICO 4 Ultra (Early Access)
Replaces manual H264/TCP stereo streaming with NVIDIA CloudXR for
higher-quality stereoscopic rendering and lower latency.

Changes:
- teleop_xr_agent.py: add --cloudxr flag (enables Isaac Sim XR mode,
  disables manual StreamingManager)
- deps/cloudxr/: NVIDIA CloudXR.js SDK (Early Access) with Isaac Lab
  teleop React web client
- deps/cloudxr/Dockerfile.wss.proxy: HAProxy WSS proxy for PICO 4 Ultra
  HTTPS mode (routes wss://48322 → ws://49100)
- deps/cloudxr/isaac/webpack.dev.js: disable file watching to avoid
  EMFILE errors with large node_modules
- deps/cloudxr/INSTALL.md: full setup guide

Usage:
  # Start CloudXR Runtime + Isaac Lab
  cd ~/IsaacLab && ./docker/container.py start \
      --files docker-compose.cloudxr-runtime.patch.yaml \
      --env-file .env.cloudxr-runtime

  # Run teleop with CloudXR
  ~/IsaacLab/isaaclab.sh -p teleop_xr_agent.py \
      --task Isaac-MindRobot-2i-DualArm-IK-Abs-v0 --cloudxr

  # Serve web client
  cd deps/cloudxr/isaac && npm run dev-server:https
2026-03-26 14:29:03 +08:00

55 lines
1.5 KiB
TypeScript

export function getOrCreateCanvas(
id: string,
resolution?: { width: number; height: number }
): HTMLCanvasElement {
let canvas = document.getElementById(id) as HTMLCanvasElement | null;
if (!canvas) {
canvas = document.createElement('canvas') as HTMLCanvasElement;
canvas.id = id;
// canvas.style.display = "none";
document.body.appendChild(canvas);
}
if (!canvas) {
throw new Error('Failed to create canvas');
}
if (resolution) {
canvas.width = resolution.width;
canvas.height = resolution.height;
}
return canvas;
}
export function logOrThrow(tagString: string, gl: WebGL2RenderingContext) {
const err = gl.getError();
if (err !== gl.NO_ERROR) {
let errorString;
switch (err) {
case gl.INVALID_ENUM:
errorString = 'INVALID_ENUM';
break;
case gl.INVALID_VALUE:
errorString = 'INVALID_VALUE';
break;
case gl.INVALID_OPERATION:
errorString = 'INVALID_OPERATION';
break;
case gl.INVALID_FRAMEBUFFER_OPERATION:
errorString = 'INVALID_FRAMEBUFFER_OPERATION';
break;
case gl.OUT_OF_MEMORY:
errorString = 'OUT_OF_MEMORY';
break;
case gl.CONTEXT_LOST_WEBGL:
errorString = 'CONTEXT_LOST_WEBGL';
break;
default:
errorString = 'UNKNOWN_ERROR';
break;
}
throw new Error('WebGL error: ' + tagString + ': ' + errorString + ' (' + err + ')');
} else {
console.debug('WebGL no-error: ' + tagString);
}
}