0

I'm new to WGPU and I'm following the learn-wgpu site. However, I'm trying to make a library which I could use to create simple games. For example, to draw two different images on the screen I would use:

lib.clear();
lib.add_text_rect(0.0, 0.0, 0.5, 0.5, img1);
lib.add_text_rect(-0.5, 0.0, 0.5, 0.5, img2);
lib.update_buffers();

However, right now it is only drawing one of the images I've loaded before (done exactly as the tutorial suggests).

I'm pretty sure that the issue is either in the shader or in the render pass part of the render function, specifically when setting the bind groups, which looks like this right now: (take note that I'm storing the diffuse bind groups in a vec when loading the texture so I can use multiple textures at a time)

for i in 0..self.diffuse_bind_groups.len() {
     render_pass.set_bind_group(i as u32, &self.diffuse_bind_groups[i], &[]);
}

My logic says that what I'm doing in the code above is ok, as it is just setting multiple bind groups (meaning that img1 would be in group 0 and that img2 would be in group 1). However, in my shader I'm not sure how to define which group I should use for each textured set of vertices.

(Note: I tried changing the group(0) to group(1) just to try it out, and the whole thing crashes, which makes me think I'm looking in the wrong direction)

// ---------- Fragment shader -----------

@group(0) @binding(0)
var t_diffuse: texture_2d<f32>;
@group(0) @binding(1)
var s_diffuse: sampler;

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    return textureSample(t_diffuse, s_diffuse, in.text_coords);
}

I've tried what I mentioned above and searching for examples on similar stuff. However, the resources I've been able to find are either too complex for me to understand (like the valorant repo), or not functional.

If you want a deeper dive into what I'm trying to do, Here's the link to my repo.

1 Answers1

0

I am gonna provide an answer as to why your current code throws an error, but will only try to provide some basic pointers as to how you could solve the problems you have described in a sensible manner.

Currently your code leads to this validation error:

[2023-04-20T14:24:55Z ERROR wgpu::backend::direct] Handling wgpu errors as fatal by default
thread 'main' panicked at 'wgpu error: Validation Error

Caused by:
    In Device::create_render_pipeline
      note: label = `Render Pipeline`
    error matching FRAGMENT shader requirements against the pipeline
    shader global ResourceBinding { group: 1, binding: 1 } is not available in the layout pipeline layout
    binding is missing from the pipeline layout

The reason this happens is, because you try to access @group(1) in your wgsl shader which does not exist.

For just making it work for now with exactly two bind groups you could create a new BindGroupLayout

let texture_bind_group_layout2 =
    device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { 
        label: Some("texture_bind_group_layout_2"), 
        entries: &[
            wgpu::BindGroupLayoutEntry {
                binding: 0,
                visibility: wgpu::ShaderStages::FRAGMENT,
                ty: wgpu::BindingType::Texture { 
                    sample_type: wgpu::TextureSampleType::Float { filterable: true }, 
                    view_dimension: wgpu::TextureViewDimension::D2, 
                    multisampled: false 
                },
                count: None
            },
            wgpu::BindGroupLayoutEntry {
                binding: 1,
                visibility: wgpu::ShaderStages::FRAGMENT,
                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
                count: None
            }
        ]
    });

and add it to the PipelineLayout like this

let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
    label: Some("Render Pipeline Layout"),
    bind_group_layouts: &[&texture_bind_group_layout, &texture_bind_group_layout2],
    push_constant_ranges: &[]
});

Now you will get either the texture below with

@group(0) @binding(0)
var t_diffuse: texture_2d<f32>;
@group(0) @binding(1)
var s_diffuse: sampler;

First texture

or the other texture with this bind group

@group(1) @binding(0)
var t_diffuse: texture_2d<f32>;
@group(1) @binding(1)
var s_diffuse: sampler;

Second texture

However as you can probably imagine, this approach scales terribly which is why I would advise you too look into texture packing or similar techinques.

frankenapps
  • 5,800
  • 6
  • 28
  • 69
  • Thanks! I've thought about texture packing, however my intuition says that it would require some heavy abstraction to make it work in such way for it to be easy to load and use new textures. Isn't there a way to pass the group we want to use as an input to the shader? – Santy Arellano Apr 20 '23 at 17:32
  • Thinking a bit more about your response. It isn't clear to me why we must create a new bind group layout to use a new layout. As to what I understand the layout tells our system how to read the bind groups, so in theory if we have a second bind group which must be read in the same way, we then would need only one bind group layout, wouldn't we? – Santy Arellano Apr 20 '23 at 17:38
  • I do not know of a way how you can access the group dynamically. If you for some reason do not want to pack your textures you can create a [TextureArray](https://docs.rs/wgpu/latest/wgpu/enum.TextureViewDimension.html#variant.D2Array) instead which can be accessed from WGSL easily. Sure you do not create a new bind group layout, you can reuse the current one for the next bind group like so: `bind_group_layouts: &[&texture_bind_group_layout, &texture_bind_group_layout],` At this point however you should really reconsider your current approach... – frankenapps Apr 21 '23 at 04:08
  • You can really only have a [very limited number of bind groups](https://docs.rs/wgpu/latest/wgpu/struct.Limits.html#structfield.max_bind_groups) and [samplers on DX12](https://github.com/gfx-rs/wgpu/pull/3499) however which is the main reason why your bind groups approach won't scale. – frankenapps Apr 21 '23 at 04:12
  • Also just wanted to throw out this recommendation because you stated, that you think texture packing might require heavy abstraction: Have you looked into crates like [etagere](https://crates.io/crates/etagere) which basically does all the heavy lifting for you? – frankenapps Apr 21 '23 at 04:15
  • Thank you all for your help! In the end I realized I was just being dumb and I forgot that I could just draw all the indexed vertices corresponding to one texture, then swap the binding group for another texture, and so on... It works fine. However, I'm still curious about how (in)efficient this process is, specially when it comes to swap binding groups. – Santy Arellano Apr 24 '23 at 17:10