#Rendering BindGroups in Bevy

7 messages · Page 1 of 1 (latest)

gloomy flint
#


#[derive(Debug, Clone, Asset, TypePath)]
pub struct MapMaterial {
  base_texture: Handle<Image>,
  index_texture: Handle<Image>,
}

impl AsBindGroup for MapMaterial {
  type Data = ();

  fn as_bind_group(
    &self,
    layout: &BindGroupLayout,
    render_device: &RenderDevice,
    images: &RenderAssets<Image>,
    fallback_image: &FallbackImage
  ) -> Result<PreparedBindGroup<Self::Data>, AsBindGroupError> {
    let base_img = images.get(&self.base_texture)
      .ok_or(AsBindGroupError::RetryNextUpdate)?;
    let index_img = images.get(&self.index_texture)
      .ok_or(AsBindGroupError::RetryNextUpdate)?;

    let textures = vec![
      &*base_img.texture_view,
      &*index_img.texture_view
    ];

    let bind_group = render_device.create_bind_group(
      "bindless_material_bind_group",
      layout,
      &BindGroupEntries::sequential((textures.as_slice(), &fallback_image.d2.sampler)),
    );
    
    Ok(PreparedBindGroup {
      bindings: vec![],
      bind_group,
      data: (),
    })
  }

  fn unprepared_bind_group(
    &self,
    _: &BindGroupLayout,
    _: &RenderDevice,
    _: &RenderAssets<Image>,
    _: &FallbackImage,
  ) -> Result<UnpreparedBindGroup<Self::Data>, AsBindGroupError> {
    // we implement as_bind_group directly because
    panic!("bindless texture arrays can't be owned")
  }

  fn bind_group_layout_entries(_: &RenderDevice) -> Vec<BindGroupLayoutEntry>
    where
      Self: Sized,
  {
    vec![
      // @group(2) @binding(0) var textures: binding_array<texture_2d<f32>>;
      BindGroupLayoutEntry {
        binding: 0,
        visibility: ShaderStages::FRAGMENT,
        ty: BindingType::Texture {
          sample_type: TextureSampleType::Float { filterable: true },
          view_dimension: TextureViewDimension::D2,
          multisampled: false,
        },
        count: NonZeroU32::new(2),
      },
    ]
  }
}


#

// This is the struct that will be passed to your shader
#[derive(Asset, TypePath, AsBindGroup, Debug, Clone)]
struct StdMaterial {
  #[texture(1)]
  #[sampler(2)]
  color_texture: Handle<Image>,
}
impl Material2d for StdMaterial {
  fn fragment_shader() -> ShaderRef {
    "shaders/default.wgsl".into()
  }
}

impl Material2d for MapMaterial {
  fn fragment_shader() -> ShaderRef {
    "shaders/map_mat.wgsl".into()
  }
}

pub(crate) fn display_map(
  map: Res<IndexMap>,
  ind: Res<TileIndex>,
  mut commands: Commands,
  mut meshes: ResMut<Assets<Mesh>>,
  mut materials: ResMut<Assets<MapMaterial>>,
  mut kkr: ResMut<Assets<StdMaterial>>,
) {
  info!("Displaying map!");
  commands.spawn(MaterialMesh2dBundle {
    material: materials.add(MapMaterial {
      index_texture: ind.0.clone(),
      base_texture: map.0.clone(),
    }),
    mesh: meshes.add(Rectangle::default()).into(),
    transform: Transform::default().with_scale(Vec3::splat(128.)),
    ..Default::default()
  });

  // commands.spawn(MaterialMesh2dBundle {
  //   material: kkr.add(StdMaterial {
  //     color_texture: map.0.clone()
  //   }),
  //   mesh: meshes.add(Rectangle::default()).into(),
  //   transform: Transform::default().with_scale(Vec3::splat(128.)),
  //   ..Default::default()
  // });
}
#

The above is my code for making a bind group, for a 2d image that needs 2 separate textures, one being the primary, and the second one being just a regular image.

Unfortunately the code doesn't work, however the StdMaterial, works fine. So i believe it's due to the bindgroups? I am largely using the same code for shaders

gloomy flint
#

okay i found the particular issue.

let base_img = images.get(&self.base_texture)
      .ok_or(AsBindGroupError::RetryNextUpdate)?;

is None.
This is probably because i dynamically generate new images during runtime, and just insert them into regular Assets<Image>, and this seems to want RenderAssets<Image>. I do not know how to get them this way though, since changing the type (which is possible in my app) it removes the add function which is not present in RenderAssets.

let dynamic = DynamicImage::from(RgbaImage::from_vec(original_img.width(), original_img.height(), buf)?);
    let mut img = Image::from_dynamic(dynamic, true, RenderAssetUsages::all());
    img.sampler = ImageSampler::Descriptor(ImageSamplerDescriptor::nearest());

    Some(Self(render_images.add(img)))

This is the code generating the image for full clarity.
So, how do i add this image as an RenderAsset?

gloomy flint
#

update: i don't know what changed or happened, but the above code works now

#

i now get a fresh, brand new error at this part:

let bind_group = render_device.create_bind_group(
      "bindless_material_bind_group",
      layout,
      &BindGroupEntries::sequential((&textures[..], &fallback_image.d2.sampler)),
    );
 Handling wgpu errors as fatal by default    
thread 'Compute Task Pool (3)' panicked at /Users/pascaldachard/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wgpu-0.19.1/src/backend/wgpu_core.rs:3009:5:
wgpu error: Validation Error

Caused by:
    In Device::create_bind_group
      note: label = `bindless_material_bind_group`
    Number of bindings in bind group descriptor (2) does not match the number of bindings defined in the bind group layout (1)


stack backtrace:
libunwind: stepWithCompactEncoding - invalid compact unwind encoding

AFAIK i handled it as closely as possible as the example: https://github.com/bevyengine/bevy/blob/main/examples/shader/texture_binding_array.rs

So i'm stumped once again

GitHub

A refreshingly simple data-driven game engine built in Rust - bevyengine/bevy

gloomy flint
#

Genuinely no idea how i fixed it, but i did