Skip to content

Commit d4bbe47

Browse files
YLouWashUfacebook-github-bot
authored andcommitted
{CUDA Decoding} Restore SW decoder fallback in xprsDecoderMaker
Summary: The CUDA-enabled Linux wheel segfaults on any machine without an NVIDIA driver. The GitHub CI "Test wheel" job crashes with exit 139 after a burst of: Cannot load libcuda.so.1 [XPRS][WARNING]: Loading CUDA functions failed [DecoderFactory][WARNING]: Could not create a decoder for 'H.265'! Root cause: the SW-fallback fixes intended for `arvr/libraries/vrs/utils/xprs/XprsDecoder.cpp` never landed. The landed commit for D103253728 (`a4556645655d`) touched only the four `arvr/projects/compression/xprs/` files -- the `XprsDecoder.cpp` hunks were dropped somewhere during rebasing, even though the reviewed diff contained them. The same happened to the monochrome-skip hunks from D110227872. Internal master's copy of this file was last modified 2026-04-29, so ShipIt correctly exported a file that had never received the fixes. Consequence: with no `libcuda`, `getNvCodecContext()` throws, `enumDecoders()` catches it and returns non-OK -- while still having collected the SW `hevc`/`h264` decoders. The stale `xprsDecoderMaker` bails on the non-OK result (`if (xprs::enumDecoders(decoders) != XprsResult::OK) return nullptr;`) without ever looking at those SW decoders, so no decoder is created and the null decoder is later dereferenced. This re-applies the four lost changes to that file: - Tolerate a non-OK `enumDecoders()` result; fail only when the decoder list is genuinely empty. (Fixes the crash.) - On decoder `init()` failure, `continue` to the next decoder instead of returning `nullptr`, so HW->SW fallback works. - Downgrade the recoverable HW->SW decode-probe log from `XR_LOGE` to `XR_LOGW`. - Skip HW decoders up front for monochrome streams (`isMonochromePixelFormat`), which NVDEC cannot decode. No change to the GPU path: color streams still select `hevc_cuvid`. Reviewed By: kongchen1992 Differential Revision: D114806413
1 parent 2f4fc9e commit d4bbe47

1 file changed

Lines changed: 46 additions & 5 deletions

File tree

vrs/utils/xprs/XprsDecoder.cpp

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,22 @@ Ocean::Worker* getWorkers(uint32_t pixels) {
9393
return pixels >= 640 * 480 ? Ocean::WorkerPool::get().scopedWorker()() : nullptr;
9494
}
9595

96+
// NVDEC hardware cannot decode monochrome (4:0:0) H.265 — the format used by
97+
// Aria's grayscale SLAM and eye-tracking cameras. The decoded output pixel
98+
// format being grayscale is a reliable proxy for a monochrome bitstream, so the
99+
// decoder maker uses this to skip HW decoders for such streams up front.
100+
bool isMonochromePixelFormat(PixelFormat fmt) {
101+
switch (fmt) {
102+
case PixelFormat::GREY8:
103+
case PixelFormat::GREY10:
104+
case PixelFormat::GREY12:
105+
case PixelFormat::GREY16:
106+
return true;
107+
default:
108+
return false;
109+
}
110+
}
111+
96112
} // namespace
97113

98114
namespace vrs::vxprs {
@@ -337,7 +353,11 @@ class VXDecoder : public vrs::utils::DecoderI {
337353
compressedBuffer.data = const_cast<uint8_t*>(encodedFrame.data());
338354
XprsResult res = xprsDecoder_->decodeFrame(frame, compressedBuffer);
339355
if (res != XprsResult::OK) {
340-
XR_LOGE("Failed to decode xprs frame: {}", xprs::getErrorMessage(res));
356+
// Logged at WARNING, not ERROR: this path is hit during normal HW->SW
357+
// decoder probing in xprsDecoderMaker (e.g. NVDEC rejecting a monochrome
358+
// stream), where the failure is expected and recovered by falling back to
359+
// the next decoder. The caller turns this into a DecoderError it handles.
360+
XR_LOGW("Failed to decode xprs frame: {}", xprs::getErrorMessage(res));
341361
return domainError(utils::DecodeStatus::DecoderError);
342362
}
343363
return SUCCESS;
@@ -373,7 +393,13 @@ unique_ptr<utils::DecoderI> xprsDecoderMaker(
373393
const string& codecFormatName = outputImageSpec.getCodecName();
374394
unique_ptr<VXDecoder> decoder;
375395
xprs::CodecList decoders;
376-
if (xprs::enumDecoders(decoders) != XprsResult::OK) {
396+
// enumDecoders may return non-OK while still populating `decoders` with the
397+
// SW decoders that were collected before a HW decoder threw (e.g. CUDA init
398+
// failure on a non-GPU machine). Don't bail early — only fail if the list is
399+
// truly empty after enumeration.
400+
(void)xprs::enumDecoders(decoders);
401+
if (decoders.empty()) {
402+
XR_LOGE("xprs::enumDecoders returned no decoders");
377403
return nullptr;
378404
}
379405
xprs::VideoCodecFormat codecFormat{};
@@ -388,18 +414,33 @@ unique_ptr<utils::DecoderI> xprsDecoderMaker(
388414
if (!options.useHwCodecs && dec.hwAccel) {
389415
continue;
390416
}
417+
// NVDEC can't decode monochrome H.265 (Aria SLAM/eye cameras). Skip HW
418+
// decoders for grayscale streams here so we go straight to SW with one
419+
// informational log, instead of attempting NVDEC and emitting fallback
420+
// warnings for an expected, unsupported-by-hardware condition.
421+
if (dec.hwAccel && isMonochromePixelFormat(outputImageSpec.getPixelFormat())) {
422+
XR_LOGI(
423+
"Skipping HW decoder '{}' for monochrome {} stream (NVDEC decodes color only); using SW decoder",
424+
dec.implementationName,
425+
codecFormatName);
426+
continue;
427+
}
391428
unique_ptr<xprs::IVideoDecoder> xprsDecoder{createDecoder(dec)};
392429
if (!xprsDecoder) {
393430
XR_LOGE("Creating xprs decoder '{}' for {} failed!", dec.implementationName, codecFormatName);
394431
} else {
395432
XprsResult res = xprsDecoder->init({});
396433
if (res != XprsResult::OK) {
397-
XR_LOGE(
398-
"Failed to initialized xprs decoder '{}' for {}: {}",
434+
// Decoder init failure is recoverable — try the next decoder in the
435+
// preferred-order list (typically HW first, then SW). Returning here
436+
// would prevent SW fallback when a HW decoder fails to initialize on
437+
// a non-GPU machine.
438+
XR_LOGW(
439+
"Decoder '{}' for {} init failed ({}), trying next decoder",
399440
dec.implementationName,
400441
codecFormatName,
401442
xprs::getErrorMessage(res));
402-
return nullptr;
443+
continue;
403444
}
404445
decoder = make_unique<VXDecoder>(std::move(xprsDecoder));
405446
if (decoder->decode(encodedFrame, outDecodedFrame, outputImageSpec) == 0) {

0 commit comments

Comments
 (0)