I was experimenting with the Samsung Galaxy XR to see if my streaming pipeline would work as well as it does on the Quest 3. On the Galaxy XR there is a lot of frame stutter and lag when turning your head from left to right. If the performance can be tuned for the GalaxyXR that would be greatly appreciated. Also, exposing hand tracking would not only help the Galaxy XR but also Quest experiences that don’t have controllers.
I don’t think we have experienced this before. Could you follow Troubleshooting — NVIDIA CloudXR SDK to share logs?
# CloudXR.js: reprojection is lost on browsers without `XRFrame.predictedDisplayTime`
**Type:** Feature request / robustness improvement. **Not** a defect report — CloudXR
behaved correctly throughout our investigation, and all server-side components measured idle.
## Summary
CloudXR.js appears to depend on `XRFrame.predictedDisplayTime` to obtain the target pose for
its depth-mesh reprojection. On a browser that does not implement that attribute (Samsung
Internet on Android XR / Samsung Galaxy XR), reprojection stops entirely and the client
presents each decoded frame once, at the rate it arrives.
We would like CloudXR.js to fall back to an estimated display time when the attribute is
unavailable, so reprojection degrades gracefully rather than switching off.
## Environment
| | |
|—|—|
| CloudXR Runtime | 6.2.1 (Linux, containerized) |
| CloudXR.js | `@nvidia/cloudxr` 6.2.1 |
| Server GPU | NVIDIA A40 |
| Affected client | Samsung Galaxy XR (`SM-I610`), Samsung Internet 29.0 / Chromium 136 |
| Working client | Meta Quest 3, Meta Quest Browser |
| Session options | `perEyeWidth/Height` 2048×1792 and 1280×640, `deviceFrameRate: 72`, H.264 and H.265, ICE bypassed via explicit `mediaAddress`/`mediaPort` with `NV_CXR_STREAMSDK_ENABLE_ICE=0` |
## Observed behaviour
`XRFrame.predictedDisplayTime` is absent on the affected browser
(`‘predictedDisplayTime’ in XRFrame.prototype === false`). It is a required WebXR Device API
attribute ([immersive-web/webxr#1217](Add XRFrame.predictedDisplayTime by Squareys · Pull Request #1217 · immersive-web/webxr · GitHub)), and
we have raised its absence with the browser vendor separately.
Same title (ClassicSponza), same 4096×4032 H.265 stream, same server, same network. The only
variable is the headset:
| Metric | Galaxy XR (no `predictedDisplayTime`) | Quest 3 (has it) |
|—|—|—|
| `rafInterval` p50 | 13.889 ms | 13.903 ms |
| `rafInterval` p95 / mean | 250 ms / 100.4 ms | 15.4 ms / 14.1 ms |
| `streaming.framerate` p50 (received) | 4.23 | 36.50 |
| `render.framerate` p50 (displayed) | 4.24 | 71.94 |
| **Ratio (displayed ÷ received)** | **1.00** | **1.97** |
| `render.pose_to_render_time` p50 | 474.8 ms | 78.7 ms |
Both clients drive `requestAnimationFrame` at the same p50, so the render loop is not the
difference. The ratio is the tell: on Quest 3 CloudXR.js synthesizes roughly one frame per
frame received and holds 72 Hz. On Galaxy XR the ratio is exactly 1.00 — every decoded frame
is displayed once, at arrival rate, with no motion correction. Users report severe smearing
on head rotation across all content.
**A second effect we cannot yet attribute:** Galaxy XR also *receives* ~8.6× fewer frames
(4.23 vs 36.50 fps) with the server idle and the network healthy (see the table below). We do
not assume the missing attribute causes this, but it may share a root — see question 4.
## What we ruled out
Recorded so this is not mistaken for a performance or network problem — every one of these
was measured, not assumed:
| Component | Measured | Verdict |
|—|—|—|
| Server GPU | 3–22 % utilization, no throttling | idle |
| NVENC | 6.3–11.5 ms encode latency | fast |
| Server CPU (game) | 3.7–5 % of one core | idle |
| Server host | 92–93 % idle, 32 cores | idle |
| Node uplink | 538 Mb/s measured | ample |
| Client downlink | 113 Mb/s measured | ample |
| Network path | 0 % packet loss, ~20 ms RTT, `jitterBufferDelay` 5–8 ms | healthy |
| Client Wi-Fi | Wi-Fi 6, RSSI −22 dBm, 1080 Mb/s link | healthy |
| Client decoder | `mediaCapabilities` reports `smooth` + `powerEfficient` at 4096×4032, H.264 and H.265 | capable |
## Request
**When `XRFrame.predictedDisplayTime` is unavailable, estimate it** — for example from the
`requestAnimationFrame` timestamp plus a configurable offset — rather than disabling
reprojection. Even an approximate target pose would restore motion correction, and an
imperfect prediction is substantially better than none.
Useful additions alongside it:
1. **A `SessionOptions` field** for the fallback offset (or to force the fallback on), so
integrators can tune it per device.
2. **A warning on the CloudXR.js log channel** when the attribute is missing and reprojection
is consequently disabled. Nothing in the client surfaced this; we found it only by
instrumenting the render loop ourselves and comparing two headsets.
3. **Documentation** of which WebXR APIs CloudXR.js requires for reprojection, so integrators
can check device support before deployment.
## Questions
1. Is `XRFrame.predictedDisplayTime` in fact what drives the reprojection target pose, and is
its absence the reason the displayed/received ratio drops to 1.0?
2. Do `enablePoseSmoothing` and `posePredictionFactor` have any effect when the attribute is
unavailable, or are they inert in that state?
3. Is there a supported way to supply a display-time estimate from the application side today?
4. Can the absence of `predictedDisplayTime` also throttle the **delivered** frame rate — for
example if frame pacing or the frame-request cadence sent to the server derives from it?
We measured 4.23 fps delivered to Galaxy XR versus 36.50 fps to Quest 3 with the server
idle and the network clean, and have no explanation for the gap on the client side.