bevy icon indicating copy to clipboard operation
bevy copied to clipboard

Crash on macOS, related to ExtendedMaterial and custom vertex buffer layout

Open splashdust opened this issue 1 year ago • 1 comments

Bevy version

0.15

[Optional] Relevant system information

Rust version: 1.83

SystemInfo { os: "MacOS 15.1.1 ", kernel: "24.1.0", cpu: "Apple M1 Max", core_count: "10", memory: "64.0 GiB" }
AdapterInfo { name: "Apple M1 Max", vendor: 0, device: 0, device_type: IntegratedGpu, driver: "", driver_info: "", backend: Metal }

Problem

Since updating to Bevy 0.15, my plugin no longer runs on macOS. The plugin is using ExtendedMaterial with a custom shader and one extra vertex attribute.

The application crashes immediately on startup with the following error:

thread 'Compute Task Pool (0)' panicked at /Users/joacim/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-23.0.1/src/backend/wgpu_core.rs:1102:18:
wgpu error: Validation Error

Caused by:
  In Device::create_render_pipeline, label = 'pbr_prepass_pipeline'
    Internal error in ShaderStages(VERTEX) shader: Metal: program_source:252:14: error:  from vector 'metal::float3' (aka 'float3') to vector 'metal::float2' (aka 'float2') of different size
        uv = metal::float2(unpackFloat32x3_(vb_15_elem.data[12], vb_15_elem.data[13], vb_15_elem.data[14], vb_15_elem.data[15], vb_15_elem.data[16], vb_15_elem.data[17], vb_15_elem.data[18], vb_15_elem.data[19], vb_15_elem.data[20], vb_15_elem.data[21], vb_15_elem.data[22], vb_15_elem.data[23]));
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `bevy_render::render_resource::pipeline_cache::PipelineCache::process_pipeline_queue_system`!
thread '<unnamed>' panicked at /Users/joacim/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_render-0.15.0/src/render_resource/pipeline_cache.rs:546:28:
index out of bounds: the len is 0 but the index is 6
Encountered a panic in system `bevy_render::renderer::render_system`!

It looks like some UVs are being supplied to the shader as vec3 instead of vec2, and I can't figure out why. When the mesh is created, I'm only inserting [f32; 2] UV entries.

Other than that, I haven't been able to isolate the problem, but it goes away if I just remove the UV data from the mesh and buffer layout. Also, it seems to be related to ExtendedMaterial, because using a different material with the same meshes and layout works, so I think the issue is happening in the pbr pipeline.

It's possible that the error is somewhere in my code, but the fact that it only happens on macOS (with metal backend) causes me to suspect that it's an upstream issue. I was not able to reproduce the issue in Bevys extended_material example though, so it's likely being caused by some combination of things.

Additional information

The issue can be reproduced by running the noise_terrain example (or any example except custom_material) in https://github.com/splashdust/bevy_voxel_world

The custom_material example does not use ExtendedMaterial and does not cause the error to happen.

splashdust avatar Dec 01 '24 21:12 splashdust

when I use bevy_voxel_world plugin with Bevy's SSAO,it crash too.(Windows platform)

2024-12-02T16:31:44.652852Z ERROR wgpu_core::device::global: Device::create_render_pipeline error: Error matching ShaderStages(VERTEX) shader requirements against the pipeline
2024-12-02T16:31:44.653235Z ERROR wgpu::backend::wgpu_core: Handling wgpu errors as fatal by default
thread 'Async Compute Task Pool (0)' panicked at /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-23.0.1/src/backend/wgpu_core.rs:1102:18:
wgpu error: Validation Error

Caused by:
  In Device::create_render_pipeline, label = 'pbr_prepass_pipeline'
    Error matching ShaderStages(VERTEX) shader requirements against the pipeline
      Location[3] Float32x3 interpolated as Some(Perspective) with sampling Some(Center) is not provided by the previous stage outputs
        Input is not provided by the earlier stage in the pipeline


note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Encountered a panic in system `bevy_render::render_resource::pipeline_cache::PipelineCache::process_pipeline_queue_system`!
thread '<unnamed>' panicked at /root/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_render-0.15.0/src/render_resource/pipeline_cache.rs:580:28:
index out of bounds: the len is 0 but the index is 11
Encountered a panic in system `bevy_render::renderer::render_system`!

I want read the vertex shader in the pbr_prepass_pipeline,but I don't know how to find it,Does anybody can teach me?

Touma-Kazusa2 avatar Dec 02 '24 16:12 Touma-Kazusa2

I have the same issue on MacOS Intel. Works fine on Windows Intel. Worked fine on 0.14, it is a 0.15 regression. Also using ExtendedMaterial - when switching the same code to StandardMaterial, it works.

jurisk avatar Dec 08 '24 03:12 jurisk

@jurisk Do you have a reproducible example you could share? Perhaps that could help to further pinpoint the issue.

splashdust avatar Dec 08 '24 11:12 splashdust

@jurisk Do you have a reproducible example you could share? Perhaps that could help to further pinpoint the issue.

This isn't at all a small self-contained test example, but here is the project I'm getting the issue with - perhaps this is helpful to you:

jurisk avatar Dec 08 '24 15:12 jurisk

Digging some more into this, I managed to come up with this minimal reproducible example:

use bevy::{
    pbr::{ExtendedMaterial, MaterialExtension, MaterialExtensionKey, MaterialExtensionPipeline},
    prelude::*,
    render::render_resource::*,
};
use bevy_render::mesh::MeshVertexBufferLayoutRef;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_plugins(MaterialPlugin::<
            ExtendedMaterial<StandardMaterial, MyExtension>,
        >::default())
        .add_systems(Startup, setup)
        .run();
}

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, MyExtension>>>,
) {
    commands.spawn((
        PointLight {
            shadows_enabled: true, // this seem to trigger the wgpu validation error
            ..default()
        },
        Transform::from_xyz(4.0, 8.0, 4.0),
    ));

    // camera
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
    ));

    commands.spawn((
        Mesh3d(meshes.add(Mesh::from(Cuboid::default()))),
        MeshMaterial3d(materials.add(ExtendedMaterial {
            base: StandardMaterial {
                ..Default::default()
            },
            extension: MyExtension {},
        })),
        Transform::from_xyz(0.0, 0.5, 0.0),
    ));
}

#[derive(Asset, AsBindGroup, Reflect, Debug, Clone)]
struct MyExtension {}

impl MaterialExtension for MyExtension {
    fn specialize(
        _pipeline: &MaterialExtensionPipeline,
        descriptor: &mut RenderPipelineDescriptor,
        layout: &MeshVertexBufferLayoutRef,
        _key: MaterialExtensionKey<Self>,
    ) -> Result<(), SpecializedMeshPipelineError> {
        let vertex_layout = layout.0.get_layout(&[
            Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
            Mesh::ATTRIBUTE_NORMAL.at_shader_location(1),
            Mesh::ATTRIBUTE_UV_0.at_shader_location(2),
        ])?;
        descriptor.vertex.buffers = vec![vertex_layout];
        Ok(())
    }
}

It appears to happen only when shadows are enabled and when the pipeline is specialized.

splashdust avatar Dec 08 '24 15:12 splashdust

I'm not sure whether this is a good way to solve this, but since it seems like the issue happens because the prepass pipeline expects a different buffer layout (with UVs at location 1), we can skip specialization for the prepass:

if descriptor
    .vertex
    .shader_defs
    .contains(&ShaderDefVal::Bool("PREPASS_PIPELINE".into(), true))
{
    return Ok(());
}

let vertex_layout = layout.0.get_layout(&[
    // custom layout for main pass here
])?;
descriptor.vertex.buffers = vec![vertex_layout];
Ok(())

But maybe there is a better way to deal with custom layouts. Ideally, I'd like to just extend Bevys default pbr pipeline layout with my custom attribute, and leave the base unchanged.

Anyway, I don't know if this means that this is not actually a bug, but rather user error. In that case, I guess this issue can be closed. Then again, it still seems odd that it would work on Windows and not on macOS.

splashdust avatar Dec 08 '24 17:12 splashdust