Skip to content

Media player: video crashes on Quest (Adreno null-deref in vkCmdExecuteCommands from the Vulkan present path) #922

Description

@towneh

Describe the bug?

Media player video playback crashes the whole client on Quest within roughly a second of the first decoded frame reaching the present path — a SIGSEGV (null-pointer dereference) inside the Adreno driver's vkCmdExecuteCommands, raised on Unity's Vulkan submission thread. Audio decode is unaffected (5.1 audio was validated on the same hardware with video presentation disabled — see #921).

This makes all media player video unusable on Android/Quest today. As far as we can tell the Android Vulkan present path had never been exercised on device before this test session, so it isn't a regression — it's the first real run of that leg.

Steps to Reproduce

  1. Build the client for Android (arm64, Vulkan) and sideload it on a Quest headset.
  2. In-world, load any working video stream in the media player (reproduced with H.264 + AAC over rtspt://; the transport doesn't appear to matter — the crash follows the first decoded frames reaching the Vulkan present).
  3. The app hard-crashes within ~1 second of playback starting.

The expected behavior

The stream plays: video renders to the media player screen and the app keeps running.

Screenshots

N/A — the failure is a native crash; the tombstone is under Log Files.

Build Info

Local developer build (sideloaded, so no in-app "Copy Build Info" available):

Log Files

Plugin log up to the crash (adb logcat -s basis_media) — decode is healthy, then the process dies as frames reach the present:

I basis_media: audio output format: 48000 Hz, 6 ch, pcm-encoding 0
I basis_media: demux run ended: delta_aus=82 total_aus=82 attempt=1/6

Tombstone:

Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x15c in tid (Vulkan Submissi)
Cause: null pointer dereference
backtrace:
  #00 pc 000c6418  /vendor/lib64/hw/vulkan.adreno.so
      (qglinternal::vkCmdExecuteCommands(VkCommandBuffer_T*, unsigned int, VkCommandBuffer_T* const*)+1192)
  #01-#05        libunity.so

Note the crash is not in the plugin's frames — Unity's own primary command buffer blows up while executing its recorded secondary command buffers, after the plugin's render event has run.

Additional Context

Evidence chain from the device session:

  1. Reproduces with and without fix(mediaplayer): decode discrete multichannel AAC on Android #921's changes on the same content — that diff touches no video/Vulkan code.
  2. Declaring the render event with ConfigureEvent(kUnityVulkanRenderPass_EnsureOutside) does not help — identical tombstone (same PC offset, same fault address). The outside-render-pass precondition alone is insufficient.
  3. A diagnostic build that drops every decoded frame before any Vulkan work does not crash and plays audio indefinitely. That isolates the trigger to exactly the commands the present path records: a pipeline barrier plus its own render pass and draw, recorded into Unity's command buffer via IUnityGraphicsVulkan::CommandRecordingState.

Conclusion: recording a foreign render pass into Unity's command buffers corrupts the Adreno driver's command-buffer state on this stack (Unity 6000.5 + XR, where Unity records its passes into secondary command buffers), and the corruption detonates later at vkCmdExecuteCommands. Whether that's a driver bug or a violated implicit contract of the recording-state API is academic for us — recording into Unity's command buffer is the thing that has to go.

Planned fix (implementation underway on a branch stacked on #921; PR to follow once device-validated):

  • The YCbCr→RGBA resolve records into a plugin-owned command buffer (one per import-ring slot) and is submitted on the graphics queue directly from the render event, configured with kUnityVulkanGraphicsQueueAccess_Allow so Unity holds its own queue users off the queue during the callback.
  • A fence per slot replaces Unity's safeFrameNumber for reclaiming imported AHardwareBuffers.
  • The Unity RenderTexture's VkImage is obtained with AccessTexture(ObserveOnly) — no barriers recorded on our behalf. No layout coordination is needed: the resolve render pass already takes the attachment from UNDEFINED (full-frame overwrite every draw) and returns it in SHADER_READ_ONLY, the layout Unity samples it in.

Raising this ahead of that PR for visibility, and in case anyone has seen this driver behaviour elsewhere or knows of constraints on GraphicsQueueAccess_Allow under the Quest XR stack that would change the approach.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions