#25788·bevy

Pixel 11 Pro XL (PowerVR C-Series) aborts before the first frame: get_pixel10_driver_version matches one literal adapter name

Author: AceVikCreated Sep 14, 2026Updated Sep 14, 2026

Bevy version

0.19.1 (wgpu 29.0.4)

Relevant system information

Google Pixel 11 Pro XL (kodiak), Android 17.

Build fingerprint: google/kodiak/kodiak:17/CD1A.260905.001.B1/16238327:user/release-keys
AdapterInfo {
    name: "PowerVR C-Series CXTP-48-1536 MC1",
    vendor: 4112, device: 1879445570, device_type: IntegratedGpu,
    driver: "PowerVR C-Series Vulkan Driver",
    driver_info: "25.3@6908880",
    backend: Vulkan,
    subgroup_min_size: 32, subgroup_max_size: 128,
    transient_saves_memory: true,
}

Built with cargo-apk / #[bevy_main], aarch64-linux-android, dev profile, NativeActivity.

What you did

Started a DefaultPlugins app on the device. Nothing exotic: a 2D camera and a bevy_ui tree.

What went wrong

The process aborts before the first frame is presented, inside the PowerVR SPIR-V compiler, while IMG_vkCreateComputePipelines compiles a compute shader:

E spvcompiler: Unhandled sampler flag combo
F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 18432 (Async Compute T), pid 18390 (l.baylee.client)

Tombstone (top of a 56-frame backtrace):

#00 abort+160                                                            /apex/com.android.runtime/lib64/bionic/libc.so
#01 spvcompiler::getMangledImageTypeString(spvcompiler::SpvTypeImage const*)+716    /vendor/lib64/libufwriter.so
#02 spvcompiler::LLVMWriter::getMangledSampledImageOpaqueName(...)+480              /vendor/lib64/libufwriter.so
#03 spvcompiler::LLVMWriter::getLLVMSamplerOrImageType(...)+596                     /vendor/lib64/libufwriter.so
#04 spvcompiler::LLVMWriter::getLLVMType(...)+768                                   /vendor/lib64/libufwriter.so
#05 spvcompiler::LLVMWriter::generateLLVM(char const*, bool)+1516                   /vendor/lib64/libufwriter.so
#06 spvcompiler::VulkanSpvCompilerIF::compileToUniflex(...)+3360                    /vendor/lib64/libufwriter.so
#07 (anonymous namespace)::BILParseStreamsWithFlags(...)+96                         /vendor/lib64/libufwriter.so
#08 BILParseStream+96                                                               /vendor/lib64/libufwriter.so
#09 ComputeShaderCompileState::CompileUF()+100                        /vendor/lib64/hw/vulkan.powervr.so
#10 CompileShaders+480                                               /vendor/lib64/hw/vulkan.powervr.so
#11 IMG_vkCreateComputePipelines+476                                 /vendor/lib64/hw/vulkan.powervr.so
#12.. libbaylee_client_android.so                                    (wgpu / bevy)

getMangledImageTypeString + getMangledSampledImageOpaqueName inside vkCreateComputePipelines means the driver is choking on a sampled-image type in a compute shader. In 0.19.1 the compute shader that has one is mesh_preprocess.wgsl (depth_pyramid: texture_2d<f32>), and that shader only exists under GpuPreprocessingMode::Culling.

Why bevy's existing carve-out does not catch it

Bevy already knows this GPU family cannot do culling. GpuPreprocessingSupport::from_world (bevy_render/src/batching/gpu_preprocessing.rs:1336) has:

fn is_preprocessing_only_android_device(adapter_info: &RenderAdapterInfo) -> bool {
    crate::get_pixel10_driver_version(adapter_info).is_some()
}

and get_pixel10_driver_version (bevy_render/src/lib.rs:571) begins:

if adapter_info.name != "PowerVR D-Series DXT-48-1536 MC1" {
    return None;
}

That is exact string equality against one device's adapter name. The Pixel 11 Pro XL reports "PowerVR C-Series CXTP-48-1536 MC1" — same vendor (4112 = Imagination Technologies), one generation newer, different string. It falls through, is granted GpuPreprocessingMode::Culling, and aborts on the first frame.

The two sibling checks in the same function are deliberately not exact:

fn is_non_supported_android_device(adapter_info: &RenderAdapterInfo) -> bool {
    crate::get_adreno_model(adapter_info).is_some_and(|model| model != 720 && model <= 730)
        || crate::get_mali_driver_version(adapter_info).is_some_and(|version| version < 48)
}

get_adreno_model and get_mali_driver_version both parse a family out of the name (contains("Mali"), a number out of v1.rNNp…). The Pixel 10 check is the odd one out, and it is the one that aged out after a single hardware generation.

Suggested fix

Recognise the family, the way the Adreno and Mali checks do — for example

if !adapter_info.name.starts_with("PowerVR") {
    return None;
}

(or gate on vendor == 0x1010, Imagination Technologies). That still covers the D-Series it was written for, and it would have covered this device on the day it shipped. If a future PowerVR part is known good, an allow-list is cheaper to maintain than a deny-list that has to be extended for every new SoC before bevy will start on it at all.

Workaround, for anyone who lands here before that

Features::INDIRECT_FIRST_INSTANCE is read in exactly one place in bevy 0.19.1 — culling_feature_support in that same function:

bevy_render/src/batching/gpu_preprocessing.rs:1342:  .contains(Features::INDIRECT_FIRST_INSTANCE | Features::IMMEDIATES)

(the only other hits in the tree are doc comments in render_phase/draw_state.rs). So removing that one bit from the device asks for PreprocessingOnly through the public API and clamps nothing else:

app.add_plugins(DefaultPlugins.set(RenderPlugin {
    render_creation: WgpuSettings {
        disabled_features: Some(WgpuFeatures::INDIRECT_FIRST_INSTANCE),
        ..default()
    }
    .into(),
    ..default()
}));

Confirm it took effect by bevy's own log line rather than by "it did not crash":

INFO bevy_render::batching::gpu_preprocessing: Some GPU preprocessing are limited on this device.

With that, the app starts and runs on this device.

Additional information

A second, unrelated-looking problem shows up on the same device once it starts: bevy_ui renders a different random subset of its batch on every frame. That is almost certainly the already-open #14710, and I have put the details there rather than here.

One note in case it saves someone an hour: on this phone adb install -r streams the APK incrementally, and the dynamic linker then faults with SIGBUS / BUS_ADRERR in __dl_load_library on launch. It looks exactly like an engine crash and is not one — adb install -r --no-incremental fixes it.