bevyengine / bevy

A refreshingly simple data-driven game engine built in Rust
https://bevyengine.org
Apache License 2.0
36.26k stars 3.58k forks source link

2d custom vertex attribute not working #14077

Open jonathandw743 opened 4 months ago

jonathandw743 commented 4 months ago

Bevy version

0.14.0-rc.4

The following is a simple program which uses a custom vertex attribute to colour a shape.

use bevy::{
    math::{vec3, vec4, VectorSpace},
    prelude::*,
    render::{
        mesh::{MeshVertexAttribute, PrimitiveTopology},
        render_asset::RenderAssetUsages,
        render_resource::{AsBindGroup, VertexFormat},
    },
    sprite::{Material2d, Material2dPlugin, MaterialMesh2dBundle, Mesh2dHandle},
};

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, MaterialPlugin::<CustomMaterial>::default()))
        .add_systems(Startup, spawn)
        .run();
}

#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
struct CustomMaterial {
    #[uniform(0)]
    uniform_value: Vec4,
}

impl Material for CustomMaterial {
    fn fragment_shader() -> bevy::render::render_resource::ShaderRef {
        "custom_vertex_attr.wgsl".into()
    }
    fn vertex_shader() -> bevy::render::render_resource::ShaderRef {
        "custom_vertex_attr.wgsl".into()
    }
    fn specialize(
        _pipeline: &bevy::pbr::MaterialPipeline<Self>,
        descriptor: &mut bevy::render::render_resource::RenderPipelineDescriptor,
        layout: &bevy::render::mesh::MeshVertexBufferLayoutRef,
        _key: bevy::pbr::MaterialPipelineKey<Self>,
    ) -> Result<(), bevy::render::render_resource::SpecializedMeshPipelineError> {
        let vertex_layout = layout.0.get_layout(&[
            Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
            CUSTOM_ATTR.at_shader_location(1),
        ])?;
        descriptor.vertex.buffers = vec![vertex_layout];
        Ok(())
    }
}

const CUSTOM_ATTR: MeshVertexAttribute =
    MeshVertexAttribute::new("CustomAttr", 83457934, VertexFormat::Float32x4);

fn spawn(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<CustomMaterial>>,
) {
    let mut mesh = Mesh::from(Cuboid::default());
    mesh.insert_attribute(CUSTOM_ATTR, vec![vec4(0.0, 0.0, 0.5, 1.0); 24]);

    commands.spawn(MaterialMeshBundle {
        mesh: meshes.add(mesh),
        material: materials.add(CustomMaterial {
            uniform_value: vec4(0.0, 0.5, 0.0, 1.0),
        }),
        ..default()
    });
    commands.spawn(Camera3dBundle {
        transform: Transform::from_xyz(0.0, 0.0, -5.0).looking_at(Vec3::ZERO, Vec3::Y),
        ..default()
    });
}

However this equivalent in 2D does not work.

use bevy::{
    math::{vec3, vec4},
    prelude::*,
    render::{
        mesh::{MeshVertexAttribute, PrimitiveTopology},
        render_asset::RenderAssetUsages,
        render_resource::{AsBindGroup, VertexFormat},
    },
    sprite::{Material2d, Material2dPlugin, MaterialMesh2dBundle, Mesh2dHandle},
};

fn main() {
    App::new()
        .add_plugins((
            DefaultPlugins,
            Material2dPlugin::<CustomMaterial>::default(),
        ))
        .add_systems(Startup, spawn)
        .run();
}

#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
struct CustomMaterial {
    #[uniform(0)]
    uniform_value: Vec4,
}

impl Material2d for CustomMaterial {
    fn fragment_shader() -> bevy::render::render_resource::ShaderRef {
        "custom_vertex_attr.wgsl".into()
    }
    fn vertex_shader() -> bevy::render::render_resource::ShaderRef {
        "custom_vertex_attr.wgsl".into()
    }
    fn specialize(
        descriptor: &mut bevy::render::render_resource::RenderPipelineDescriptor,
        layout: &bevy::render::mesh::MeshVertexBufferLayoutRef,
        _key: bevy::sprite::Material2dKey<Self>,
    ) -> Result<(), bevy::render::render_resource::SpecializedMeshPipelineError> {
        let vertex_layout = layout.0.get_layout(&[
            Mesh::ATTRIBUTE_POSITION.at_shader_location(0),
            CUSTOM_ATTR.at_shader_location(1),
        ])?;
        descriptor.vertex.buffers = vec![vertex_layout];
        Ok(())
    }
}

const CUSTOM_ATTR: MeshVertexAttribute =
    MeshVertexAttribute::new("CustomAttr", 83457934, VertexFormat::Float32x4);

fn spawn(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<CustomMaterial>>,
) {
    let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::all());
    mesh.insert_attribute(
        Mesh::ATTRIBUTE_POSITION,
        vec![
            vec3(0.0, 0.0, 0.0),
            vec3(100.0, 0.0, 0.0),
            vec3(0.0, 100.0, 0.0),
        ],
    );
    mesh.insert_attribute(CUSTOM_ATTR, vec![vec4(0.5, 0.0, 0.0, 1.0); 3]);

    commands.spawn(MaterialMesh2dBundle {
        mesh: Mesh2dHandle::from(meshes.add(mesh)),
        material: materials.add(CustomMaterial {
            uniform_value: vec4(0.0, 0.5, 0.0, 1.0),
        }),
        ..default()
    });
    commands.spawn(Camera2dBundle { ..default() });
}

This error is shown, followed by a series of panics and the program crashing:

2024-06-30T15:15:49.531042Z ERROR wgpu_core::device::global: Device::create_render_pipeline error: Error matching ShaderStages(VERTEX) shader requirements against the pipeline    
2024-06-30T15:15:49.531192Z ERROR wgpu::backend::wgpu_core: Handling wgpu errors as fatal by default    
thread 'Async Compute Task Pool (0)' panicked at /home/.../.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.20.1/src/backend/wgpu_core.rs:2996:5:
wgpu error: Validation Error

Caused by:
    In Device::create_render_pipeline
      note: label = `transparent_mesh2d_pipeline`
    Error matching ShaderStages(VERTEX) shader requirements against the pipeline
    Shader global ResourceBinding { group: 1, binding: 0 } is not available in the pipeline layout
    Buffer structure size 144, added to one element of an unbound array, if it's the last field, ended up greater than the given `min_binding_size`

The following shader is used in both examples:

#import bevy_pbr::mesh_functions::{get_world_from_local, mesh_position_local_to_clip}

struct CustomMaterial {
    uniform_value: vec4<f32>,
}
@group(2) @binding(0)
var<uniform> material: CustomMaterial;

struct Vertex {
    @builtin(instance_index) instance_index: u32,
    @location(0) position: vec3<f32>,
    @location(1) custom_attr: vec4<f32>,
};

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) custom_attr: vec4<f32>,
};

@vertex
fn vertex(vertex: Vertex) -> VertexOutput {
    var out: VertexOutput;
    out.clip_position = mesh_position_local_to_clip(
        get_world_from_local(vertex.instance_index),
        vec4<f32>(vertex.position, 1.0),
    );
    out.custom_attr = vertex.custom_attr;
    return out;
}

struct FragmentInput {
    @location(0) custom_attr: vec4<f32>,
};

@fragment
fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
    return in.custom_attr;
}
janhohenheim commented 4 months ago

Do you know if this worked in 0.13?

jonathandw743 commented 4 months ago

Do you know if this worked in 0.13?

it did not. In 0.13.2, the same code with very minor changes gives the same output:

2024-07-01T21:18:02.604417Z ERROR log: Device::create_render_pipeline error: Error matching ShaderStages(VERTEX) shader requirements against the pipeline    
2024-07-01T21:18:02.604511Z ERROR log: Handling wgpu errors as fatal by default    
thread 'Async Compute Task Pool (0)' panicked at /home/.../.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.19.4/src/backend/wgpu_core.rs:3006:5:
wgpu error: Validation Error

Caused by:
    In Device::create_render_pipeline
      note: label = `transparent_mesh2d_pipeline`
    Error matching ShaderStages(VERTEX) shader requirements against the pipeline
    Shader global ResourceBinding { group: 1, binding: 0 } is not available in the pipeline layout
    Buffer structure size 160, added to one element of an unbound array, if it's the last field, ended up greater than the given `min_binding_size`

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 'Compute Task Pool (0)' panicked at /home/.../.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_render-0.13.2/src/pipelined_rendering.rs:49:67:
called `Result::unwrap()` on an `Err` value: RecvError

This is the same as the 0.14 error, just with a bit less spam.