1use std::{collections::HashMap, hash::Hash, num::NonZeroU64};
2
3use crate::{buffer::BufferBinding, context::Context, sampler::Sampler, texture::TextureBinding};
4
5#[derive(Hash, PartialEq, Clone, Eq, Debug)]
6pub(crate) struct Binding {
7 pub binding: usize,
9 pub visibility: wgpu::ShaderStages,
11 pub resource: BindingResource,
13}
14
15#[derive(Hash, PartialEq, Eq, Clone, Debug)]
16pub(crate) enum BindingResource {
17 Buffer(BufferBinding, Option<usize>),
18 Texture(TextureBinding),
19 Sampler(Sampler),
20}
21
22#[derive(Debug, Clone, Hash, PartialEq, Eq)]
23pub(crate) struct BindGroupLayout {
24 layout: Vec<wgpu::BindGroupLayoutEntry>,
25}
26
27impl BindGroupLayout {
28 pub(crate) fn get_or_build(&self, context: &Context) -> wgpu::BindGroupLayout {
29 let mut bind_group_layout_cache = context.caches.bind_group_layout_cache.borrow_mut();
30
31 bind_group_layout_cache
32 .get_or_insert_with(self.clone(), || {
33 context
34 .device()
35 .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
36 label: None,
37 entries: &self.layout,
38 })
39 })
40 .clone()
41 }
42}
43
44#[derive(Clone, Hash, PartialEq, Eq, Debug)]
51pub struct BindGroup {
52 bindings: Vec<Binding>,
53 name: Option<String>,
54}
55
56impl BindGroup {
57 pub(crate) fn build_layout(&self) -> BindGroupLayout {
58 let layout = self
59 .bindings
60 .iter()
61 .map(|b| match &b.resource {
62 BindingResource::Sampler(sampler) => wgpu::BindGroupLayoutEntry {
63 binding: b.binding as u32,
64 visibility: b.visibility,
65 ty: wgpu::BindingType::Sampler(sampler.sampler_type()),
66 count: None,
67 },
68 BindingResource::Texture(texture) => wgpu::BindGroupLayoutEntry {
69 binding: b.binding as u32,
70 visibility: b.visibility,
71 ty: texture.binding_type,
72 count: None,
73 },
74 BindingResource::Buffer(buffer, _) => wgpu::BindGroupLayoutEntry {
75 binding: b.binding as u32,
76 visibility: b.visibility,
77 ty: wgpu::BindingType::Buffer {
78 ty: buffer.binding_type,
79 has_dynamic_offset: buffer.has_dynamic_offset,
80 min_binding_size: buffer.min_binding_size,
81 },
82 count: None,
83 },
84 })
85 .collect();
86
87 BindGroupLayout { layout }
88 }
89
90 pub(crate) fn get_or_build(&self, context: &Context) -> wgpu::BindGroup {
91 let mut bind_group_cache = context.caches.bind_group_cache.borrow_mut();
92
93 bind_group_cache
94 .get_or_insert_with(self.clone(), || {
95 let gpu_layout = self.build_layout().get_or_build(context);
96
97 let mut texture_views = HashMap::new();
98 let mut samplers = HashMap::new();
99
100 for b in &self.bindings {
101 match &b.resource {
102 BindingResource::Texture(texture) => {
103 texture_views
104 .insert(&texture.texture, texture.texture.get_or_build(context));
105 }
106 BindingResource::Sampler(sampler) => {
107 samplers.insert(sampler, sampler.get_or_build(context));
108 }
109 _ => {}
110 }
111 }
112
113 let gpu_bindings = self
114 .bindings
115 .iter()
116 .map(|b| match &b.resource {
117 BindingResource::Buffer(buffer, size) => wgpu::BindGroupEntry {
118 binding: b.binding as u32,
119 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
120 buffer: buffer.buffer.buffer(),
121 offset: 0,
122 size: size.and_then(|s| NonZeroU64::new(s as u64)),
123 }),
124 },
125 BindingResource::Texture(texture) => wgpu::BindGroupEntry {
126 binding: b.binding as u32,
127 resource: wgpu::BindingResource::TextureView(
128 &texture_views.get(&texture.texture).unwrap(),
129 ),
130 },
131 BindingResource::Sampler(sampler) => wgpu::BindGroupEntry {
132 binding: b.binding as u32,
133 resource: wgpu::BindingResource::Sampler(
134 &samplers.get(sampler).unwrap(),
135 ),
136 },
137 })
138 .collect::<Vec<_>>();
139
140 context
141 .device()
142 .create_bind_group(&wgpu::BindGroupDescriptor {
143 label: self.name.as_deref(),
144 layout: &gpu_layout,
145 entries: &gpu_bindings,
146 })
147 })
148 .clone()
149 }
150}
151
152pub struct BindGroupBuilder {
154 bindings: Vec<Binding>,
155 name: Option<String>,
156}
157
158impl BindGroupBuilder {
159 pub fn new() -> Self {
161 Self {
162 bindings: vec![],
163 name: None,
164 }
165 }
166
167 pub fn name(mut self, name: &str) -> Self {
169 self.name = Some(name.to_string());
170 self
171 }
172
173 pub fn buffer(
175 mut self,
176 binding: usize,
177 visibility: wgpu::ShaderStages,
178 buffer: &BufferBinding,
179 size: Option<usize>,
180 ) -> Self {
181 self.bindings.push(Binding {
182 binding,
183 visibility,
184 resource: BindingResource::Buffer(buffer.clone(), size),
185 });
186 self
187 }
188
189 pub fn texture(
191 mut self,
192 binding: usize,
193 visibility: wgpu::ShaderStages,
194 texture: &TextureBinding,
195 ) -> Self {
196 self.bindings.push(Binding {
197 binding,
198 visibility,
199 resource: BindingResource::Texture(texture.clone()),
200 });
201 self
202 }
203
204 pub fn sampler(
206 mut self,
207 binding: usize,
208 visibility: wgpu::ShaderStages,
209 sampler: &Sampler,
210 ) -> Self {
211 self.bindings.push(Binding {
212 binding,
213 visibility,
214 resource: BindingResource::Sampler(sampler.clone()),
215 });
216 self
217 }
218
219 pub fn build(self) -> BindGroup {
221 BindGroup {
222 bindings: self.bindings,
223 name: self.name,
224 }
225 }
226}