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

Open Beginner friendly
#25,788 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
android, rust

Research direction

Read get_pixel10_driver_version in bevy_render/src/lib.rs and is_preprocessing_only_android_device in bevy_render/src/batching/gpu_preprocessing.rs. Check how the existing Adreno and Mali family checks are structured, then verify the PowerVR C-Series and D-Series paths select preprocessing without culling and avoid the reported first-frame abort.

Written by the indexing model from the issue text.

Description

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.

Dominant language
Rust
Stars
48.3k
Forks
4.9k
Avg merge
3d 17h
Merged PRs (30d)
172

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from bevyengine/bevy

All issues in bevyengine/bevy

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.