From dfc4211574622f04031067289dcdaa837649ba76 Mon Sep 17 00:00:00 2001 From: yanziz-nv Date: Thu, 21 May 2026 14:02:57 -0700 Subject: [PATCH 1/2] Revert "Port web client perf updates from cloudxr-js 6.2.0 (#530)" This reverts commit e0d82508fdbf4ccb1170d2d7bea9224699bd794f. --- .../helpers/react/CloudXRComponent.tsx | 2 +- .../helpers/react/PerformanceCanvasImage.tsx | 211 ------------------ deps/cloudxr/webxr_client/package.json | 26 +-- deps/cloudxr/webxr_client/src/CloudXRUI.tsx | 108 +++++++-- 4 files changed, 107 insertions(+), 240 deletions(-) delete mode 100644 deps/cloudxr/webxr_client/helpers/react/PerformanceCanvasImage.tsx diff --git a/deps/cloudxr/webxr_client/helpers/react/CloudXRComponent.tsx b/deps/cloudxr/webxr_client/helpers/react/CloudXRComponent.tsx index 6b9eea500..1f1fdb22b 100644 --- a/deps/cloudxr/webxr_client/helpers/react/CloudXRComponent.tsx +++ b/deps/cloudxr/webxr_client/helpers/react/CloudXRComponent.tsx @@ -256,7 +256,7 @@ export default function CloudXRComponent({ telemetry: { enabled: true, appInfo: { - version: '6.2.0', + version: '6.1.0', product: applicationName, }, }, diff --git a/deps/cloudxr/webxr_client/helpers/react/PerformanceCanvasImage.tsx b/deps/cloudxr/webxr_client/helpers/react/PerformanceCanvasImage.tsx deleted file mode 100644 index 00068bcd4..000000000 --- a/deps/cloudxr/webxr_client/helpers/react/PerformanceCanvasImage.tsx +++ /dev/null @@ -1,211 +0,0 @@ -/* - * SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * PerformanceCanvasImage - Canvas-backed performance metrics display. - * - * Renders Render FPS, Streaming FPS, and Pose-to-Render in a single canvas texture - * with rounded rectangles per line. Used as a uikit Image for efficient per-frame - * updates without triggering layout. - * - * Why canvas + texture instead of uikit Text? - * - Updating uikit Text from signals can trigger layout recalculations every frame. - * - Drawing to a canvas and setting texture.needsUpdate = true updates the image - * without affecting the rest of the UI tree. - * - * The texture is assigned to the Image via ref (imageRef.current.texture.value), not - * the `src` prop, because the uikit bridge would stringify a Texture object and - * cause a 404 if passed as src. - */ - -import { ReadonlySignal } from '@preact/signals-react'; -import { useFrame } from '@react-three/fiber'; -import { Image } from '@react-three/uikit'; -import React, { useRef, useState, useEffect } from 'react'; -import { CanvasTexture } from 'three'; - -/** Canvas resolution (pixels). High values keep text sharp when the texture is scaled to the display size. */ -const CANVAS_WIDTH = 1024; -const CANVAS_HEIGHT = 500; - -// Layout constants (canvas space) — compact card style with label + value side-by-side. -const LAYOUT = { - fontSize: 56, - cardHeight: 130, - cardGap: 18, - numCards: 3, - margin: 32, - paddingLeft: 40, - radius: 18, - cardFillStyle: 'rgba(0, 0, 0, 0.5)', - labelColor: 'rgba(180, 180, 180, 1)', -} as const; - -const CARD_WIDTH = CANVAS_WIDTH - LAYOUT.margin * 2; - -/** Draw a rounded rectangle path; caller must ctx.fill() or ctx.stroke() after. */ -function drawRoundRect( - ctx: CanvasRenderingContext2D, - x: number, - y: number, - w: number, - h: number, - r: number -): void { - ctx.beginPath(); - ctx.moveTo(x + r, y); - ctx.lineTo(x + w - r, y); - ctx.quadraticCurveTo(x + w, y, x + w, y + r); - ctx.lineTo(x + w, y + h - r); - ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h); - ctx.lineTo(x + r, y + h); - ctx.quadraticCurveTo(x, y + h, x, y + h - r); - ctx.lineTo(x, y + r); - ctx.quadraticCurveTo(x, y, x + r, y); - ctx.closePath(); -} - -export interface PerformanceCanvasImageProps { - /** Display width of the metrics image (uikit units). */ - width?: number; - /** Display height of the metrics image (uikit units). */ - height?: number; - /** Signal for render FPS value (e.g. "72.0"). Label "Render FPS: " is drawn here. */ - renderFpsText?: ReadonlySignal; - /** Signal for streaming FPS value. Label "Streaming FPS: " is drawn here. */ - streamingFpsText?: ReadonlySignal; - /** Signal for pose-to-render latency (e.g. "12.3ms"). Label "Pose-to-Render: " is drawn here. */ - poseToRenderText?: ReadonlySignal; -} - -/** - * Renders three performance metric lines on an offscreen canvas, uploads it to a - * CanvasTexture, and displays it via a uikit Image. Redrawn every frame in useFrame - * so values stay in sync without React re-renders. - */ -export function PerformanceCanvasImage({ - width = 512, - height = 512, - renderFpsText, - streamingFpsText, - poseToRenderText, -}: PerformanceCanvasImageProps) { - /** Ref for the uikit Image; we set .texture.value on it to use our CanvasTexture. */ - const imageRef = useRef<{ texture: { value: CanvasTexture | undefined } } | null>(null); - /** Offscreen canvas we draw into each frame. */ - const canvasRef = useRef(null); - /** Cached 2D context for the canvas (avoids getContext('2d') every frame). */ - const ctxRef = useRef(null); - /** Three.js texture wrapping the canvas; needsUpdate = true each frame after drawing. */ - const textureRef = useRef(null); - const [textureReady, setTextureReady] = useState(false); - - /** Create the offscreen canvas and CanvasTexture once on mount; dispose on unmount. */ - useEffect(() => { - const canvas = document.createElement('canvas'); - canvas.width = CANVAS_WIDTH; - canvas.height = CANVAS_HEIGHT; - canvasRef.current = canvas; - ctxRef.current = canvas.getContext('2d'); - const tex = new CanvasTexture(canvas); - tex.matrixAutoUpdate = false; - textureRef.current = tex; - setTextureReady(true); - return () => { - tex.dispose(); - textureRef.current = null; - canvasRef.current = null; - ctxRef.current = null; - setTextureReady(false); - }; - }, []); - - /** Assign our texture to the uikit Image via ref (avoids src stringification). */ - useEffect(() => { - if (!textureReady || !textureRef.current || !imageRef.current) return; - const img = imageRef.current; - img.texture.value = textureRef.current; - return () => { - if (img) img.texture.value = undefined; - }; - }, [textureReady]); - - /** Every frame: clear canvas, draw three vertically-stacked metric cards. */ - useFrame(() => { - const canvas = canvasRef.current; - const texture = textureRef.current; - const ctx = ctxRef.current; - if (!canvas || !texture || !ctx) return; - ctx.clearRect(0, 0, canvas.width, canvas.height); - - const { - fontSize, - cardHeight, - cardGap, - numCards, - margin, - paddingLeft, - radius, - cardFillStyle, - labelColor, - } = LAYOUT; - // Vertically center the stack of cards within the canvas. - const totalHeight = numCards * cardHeight + (numCards - 1) * cardGap; - let cardY = (canvas.height - totalHeight) / 2; - - // Each tuple: [label, current signal value (or em-dash fallback), value color]. - const metrics: [string, string, string][] = [ - ['Render FPS', renderFpsText?.value ?? '—', 'rgba(100, 255, 100, 1)'], - ['Streaming FPS', streamingFpsText?.value ?? '—', 'rgba(100, 200, 255, 1)'], - ['Pose-to-Render', poseToRenderText?.value ?? '—', 'rgba(255, 200, 100, 1)'], - ]; - - ctx.font = `bold ${fontSize}px system-ui, sans-serif`; - ctx.textBaseline = 'middle'; - // Offset to vertically center text within each card. - const centerY = cardHeight / 2; - - for (const [label, value, valueColor] of metrics) { - // Draw the rounded card background. - ctx.fillStyle = cardFillStyle; - drawRoundRect(ctx, margin, cardY, CARD_WIDTH, cardHeight, radius); - ctx.fill(); - - const textY = cardY + centerY; - - // Draw the grey label (e.g. "Render FPS") on the left side of the card. - ctx.textAlign = 'left'; - ctx.fillStyle = labelColor; - ctx.fillText(label, margin + paddingLeft, textY); - - // Draw the colored value immediately after the label, with a small gap. - const labelWidth = ctx.measureText(label).width; - ctx.fillStyle = valueColor; - ctx.fillText(' ' + value, margin + paddingLeft + labelWidth, textY); - - // Advance to the next card position. - cardY += cardHeight + cardGap; - } - - texture.needsUpdate = true; - }); - - /** Single uikit Image; texture is set via ref, width/height from props. */ - return ( - - ); -} diff --git a/deps/cloudxr/webxr_client/package.json b/deps/cloudxr/webxr_client/package.json index 9d0f35a9e..ef4f7bf14 100644 --- a/deps/cloudxr/webxr_client/package.json +++ b/deps/cloudxr/webxr_client/package.json @@ -21,33 +21,33 @@ }, "dependencies": { "@nvidia/cloudxr": "file:../nvidia-cloudxr-6.2.0.tgz", - "@preact/signals-react": "^3.10.0", - "@react-three/drei": "^10.7.7", - "@react-three/fiber": "^9.6.0", - "@react-three/handle": "^6.6.29", - "@react-three/uikit": "^1.0.64", - "@react-three/uikit-default": "^1.0.64", - "@react-three/xr": "^6.6.29", + "@preact/signals-react": "^2.2.0", + "@react-three/drei": "^10.6.1", + "@react-three/fiber": "^9.3.0", + "@react-three/handle": "^6.6.28", + "@react-three/uikit": "^1.0.0", + "@react-three/uikit-default": "^1.0.0", + "@react-three/xr": "^6.6.22", "react": "^19.2.5", "react-dom": "^19.2.5", - "three": "^0.184.0", + "three": "^0.172.0", "uuid": "^14.0.0" }, "devDependencies": { "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "@types/three": "^0.184.0", - "@webxr-input-profiles/assets": "1.0.20", + "@types/three": "^0.172.0", + "@webxr-input-profiles/assets": "1.0.19", "copy-webpack-plugin": "^13.0.0", "cross-env": "^10.1.0", "css-loader": "^6.8.1", - "html-webpack-plugin": "^5.6.7", + "html-webpack-plugin": "^5.6.3", "iwer": "^2.2.1", "rimraf": "^5.0.5", "style-loader": "^3.3.3", - "ts-loader": "^9.5.7", + "ts-loader": "^9.5.1", "typescript": "^5.8.2", "webpack-cli": "^6.0.1", - "webpack-dev-server": "^5.2.3" + "webpack-dev-server": "^5.2.1" } } diff --git a/deps/cloudxr/webxr_client/src/CloudXRUI.tsx b/deps/cloudxr/webxr_client/src/CloudXRUI.tsx index 4ff470b7e..1579ca6ea 100644 --- a/deps/cloudxr/webxr_client/src/CloudXRUI.tsx +++ b/deps/cloudxr/webxr_client/src/CloudXRUI.tsx @@ -35,7 +35,6 @@ * back to the parent component through callback props. */ -import { PerformanceCanvasImage } from '@helpers/react/PerformanceCanvasImage'; import { useXRButton } from '@helpers/react/useXRButton'; import { ReadonlySignal } from '@preact/signals-react'; import { useFrame } from '@react-three/fiber'; @@ -49,10 +48,6 @@ import { damp } from 'three/src/math/MathUtils.js'; // Face-camera rotation constants const FACE_CAMERA_DAMPING = 10; // Higher = faster rotation toward camera -/** Display size for the Performance metrics slot (width and height passed to PerformanceCanvasImage and its container). */ -const METRIC_SLOT_WIDTH = 512; -const METRIC_SLOT_HEIGHT = 250; - interface CloudXRUIProps { onStartTeleop?: () => void; onDisconnect?: () => void; @@ -369,18 +364,101 @@ export default function CloudXR3DUI({ - + + + Render FPS + + + + {renderFpsText} + + + + + + + Streaming FPS + + + + {streamingFpsText} + + + + + + + Pose-to-Render + + + + {poseToRenderText} + + + From 8c64c858a4c53400d6ded0a4dd4f6bb2681ed4f9 Mon Sep 17 00:00:00 2001 From: yanziz-nv Date: Thu, 21 May 2026 14:03:00 -0700 Subject: [PATCH 2/2] Revert "deps: bump @nvidia/cloudxr to 6.2.0 (#528)" This reverts commit 8b5740637e2ee8e6fa9ba14a9fc2af3e5efab9a1. --- deps/cloudxr/.env.default | 2 +- deps/cloudxr/webxr_client/package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deps/cloudxr/.env.default b/deps/cloudxr/.env.default index b5725e392..0d747ef48 100644 --- a/deps/cloudxr/.env.default +++ b/deps/cloudxr/.env.default @@ -6,7 +6,7 @@ # CloudXR Docker Images and Configs ########################################################### CXR_RUNTIME_SDK_VERSION=6.1.0 -CXR_WEB_SDK_VERSION=6.2.0 +CXR_WEB_SDK_VERSION=6.1.0 CXR_HOST_VOLUME_PATH=$HOME/.cloudxr ########################################################### diff --git a/deps/cloudxr/webxr_client/package.json b/deps/cloudxr/webxr_client/package.json index ef4f7bf14..48df5c991 100644 --- a/deps/cloudxr/webxr_client/package.json +++ b/deps/cloudxr/webxr_client/package.json @@ -1,6 +1,6 @@ { "name": "cloudxr-isaac-lab-teleop", - "version": "6.2.0", + "version": "6.1.0", "private": true, "description": "NVIDIA Isaac Teleop Web Client", "author": "NVIDIA Corporation", @@ -20,7 +20,7 @@ "clean": "rimraf dist" }, "dependencies": { - "@nvidia/cloudxr": "file:../nvidia-cloudxr-6.2.0.tgz", + "@nvidia/cloudxr": "file:../nvidia-cloudxr-6.1.0.tgz", "@preact/signals-react": "^2.2.0", "@react-three/drei": "^10.6.1", "@react-three/fiber": "^9.3.0",