pebble/graphics/pipeline/
binding.rs1use crate::graphics::{
2 render::Backend,
3 types::{StorageTextureAccess, TextureFormat, TextureSampleType, TextureViewDimension, flags::ShaderStages},
4};
5
6#[derive(Copy, Clone, PartialEq, Eq, Hash)]
11pub enum BindingKind {
12 Texture {
13 visibility: ShaderStages,
14 sample_type: TextureSampleType,
15 view_dimension: TextureViewDimension,
16 multisampled: bool,
17 },
18 StorageTexture {
19 visibility: ShaderStages,
20 format: TextureFormat,
21 access: StorageTextureAccess,
22 view_dimension: TextureViewDimension,
23 },
24 Sampler { visibility: ShaderStages },
25 ComparisonSampler { visibility: ShaderStages },
26 UniformBuffer {
27 visibility: ShaderStages,
28 has_dynamic_offset: bool,
29 min_binding_size: Option<u64>,
30 },
31 StorageBufferReadOnly {
32 visibility: ShaderStages,
33 has_dynamic_offset: bool,
34 min_binding_size: Option<u64>,
35 },
36 StorageBufferReadWrite {
37 visibility: ShaderStages,
38 has_dynamic_offset: bool,
39 min_binding_size: Option<u64>,
40 },
41}
42
43impl BindingKind {
44 pub fn texture_2d(visibility: ShaderStages) -> Self {
45 Self::Texture {
46 visibility,
47 sample_type: TextureSampleType::Float { filterable: true },
48 view_dimension: TextureViewDimension::D2,
49 multisampled: false,
50 }
51 }
52
53 pub fn texture_2d_array(visibility: ShaderStages) -> Self {
54 Self::Texture {
55 visibility,
56 sample_type: TextureSampleType::Float { filterable: true },
57 view_dimension: TextureViewDimension::D2Array,
58 multisampled: false,
59 }
60 }
61
62 pub fn texture_cubemap(visibility: ShaderStages) -> Self {
63 Self::Texture {
64 visibility,
65 sample_type: TextureSampleType::Float { filterable: true },
66 view_dimension: TextureViewDimension::Cube,
67 multisampled: false,
68 }
69 }
70
71 pub fn storage_texture(
72 visibility: ShaderStages,
73 format: TextureFormat,
74 access: StorageTextureAccess,
75 view_dimension: TextureViewDimension,
76 ) -> Self {
77 Self::StorageTexture { visibility, format, access, view_dimension }
78 }
79
80 pub fn sampler(visibility: ShaderStages) -> Self {
81 Self::Sampler { visibility }
82 }
83
84 pub fn comparison_sampler(visibility: ShaderStages) -> Self {
85 Self::ComparisonSampler { visibility }
86 }
87
88 pub fn uniform_buffer(visibility: ShaderStages) -> Self {
89 Self::UniformBuffer { visibility, has_dynamic_offset: false, min_binding_size: None }
90 }
91
92 pub fn dynamic_uniform_buffer(visibility: ShaderStages, element_size: u64) -> Self {
93 Self::UniformBuffer {
94 visibility,
95 has_dynamic_offset: true,
96 min_binding_size: Some(element_size),
97 }
98 }
99
100 pub fn storage_buffer_read_only(visibility: ShaderStages) -> Self {
101 Self::StorageBufferReadOnly { visibility, has_dynamic_offset: false, min_binding_size: None }
102 }
103
104 pub fn storage_buffer_read_write(visibility: ShaderStages) -> Self {
105 Self::StorageBufferReadWrite { visibility, has_dynamic_offset: false, min_binding_size: None }
106 }
107
108 pub fn dynamic_storage_buffer(visibility: ShaderStages, element_size: u64, read_only: bool) -> Self {
109 let has_dynamic_offset = true;
110 let min_binding_size = Some(element_size);
111 if read_only {
112 Self::StorageBufferReadOnly { visibility, has_dynamic_offset, min_binding_size }
113 } else {
114 Self::StorageBufferReadWrite { visibility, has_dynamic_offset, min_binding_size }
115 }
116 }
117
118 pub fn visibility(&self) -> ShaderStages {
119 match self {
120 Self::Texture { visibility, .. }
121 | Self::StorageTexture { visibility, .. }
122 | Self::Sampler { visibility }
123 | Self::ComparisonSampler { visibility }
124 | Self::UniformBuffer { visibility, .. }
125 | Self::StorageBufferReadOnly { visibility, .. }
126 | Self::StorageBufferReadWrite { visibility, .. } => *visibility,
127 }
128 }
129
130 pub(crate) fn layout_entry(&self, binding: u32) -> wgpu::BindGroupLayoutEntry {
131 match self {
132 Self::Texture { visibility, sample_type, view_dimension, multisampled } => wgpu::BindGroupLayoutEntry {
133 binding,
134 visibility: (*visibility).into(),
135 ty: wgpu::BindingType::Texture {
136 sample_type: (*sample_type).into(),
137 view_dimension: (*view_dimension).into(),
138 multisampled: *multisampled,
139 },
140 count: None,
141 },
142 Self::StorageTexture { visibility, format, access, view_dimension } => wgpu::BindGroupLayoutEntry {
143 binding,
144 visibility: (*visibility).into(),
145 ty: wgpu::BindingType::StorageTexture {
146 access: (*access).into(),
147 format: (*format).into(),
148 view_dimension: (*view_dimension).into(),
149 },
150 count: None,
151 },
152 Self::Sampler { visibility } => wgpu::BindGroupLayoutEntry {
153 binding,
154 visibility: (*visibility).into(),
155 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
156 count: None,
157 },
158 Self::ComparisonSampler { visibility } => wgpu::BindGroupLayoutEntry {
159 binding,
160 visibility: (*visibility).into(),
161 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
162 count: None,
163 },
164 Self::UniformBuffer { visibility, has_dynamic_offset, min_binding_size } => wgpu::BindGroupLayoutEntry {
165 binding,
166 visibility: (*visibility).into(),
167 ty: wgpu::BindingType::Buffer {
168 ty: wgpu::BufferBindingType::Uniform,
169 has_dynamic_offset: *has_dynamic_offset,
170 min_binding_size: min_binding_size.and_then(wgpu::BufferSize::new),
171 },
172 count: None,
173 },
174 Self::StorageBufferReadOnly { visibility, has_dynamic_offset, min_binding_size } => wgpu::BindGroupLayoutEntry {
175 binding,
176 visibility: (*visibility).into(),
177 ty: wgpu::BindingType::Buffer {
178 ty: wgpu::BufferBindingType::Storage { read_only: true },
179 has_dynamic_offset: *has_dynamic_offset,
180 min_binding_size: min_binding_size.and_then(wgpu::BufferSize::new),
181 },
182 count: None,
183 },
184 Self::StorageBufferReadWrite { visibility, has_dynamic_offset, min_binding_size } => wgpu::BindGroupLayoutEntry {
185 binding,
186 visibility: (*visibility).into(),
187 ty: wgpu::BindingType::Buffer {
188 ty: wgpu::BufferBindingType::Storage { read_only: false },
189 has_dynamic_offset: *has_dynamic_offset,
190 min_binding_size: min_binding_size.and_then(wgpu::BufferSize::new),
191 },
192 count: None,
193 },
194 }
195 }
196}
197
198#[derive(Clone)]
200pub struct BindGroupLayout(wgpu::BindGroupLayout);
201
202impl BindGroupLayout {
203 pub(crate) fn raw(&self) -> &wgpu::BindGroupLayout {
204 &self.0
205 }
206}
207
208#[derive(Clone, PartialEq, Eq, Hash)]
212pub struct BindingEntry {
213 pub name: &'static str,
214 pub binding: u32,
215 pub kind: BindingKind,
216}
217
218#[derive(Default)]
220pub struct BindGroupLayoutBuilder<'a> {
221 label: Option<&'a str>,
222 entries: Vec<BindingEntry>,
223}
224
225impl<'a> BindGroupLayoutBuilder<'a> {
226 pub fn new() -> Self {
227 Self::default()
228 }
229
230 pub fn with_label(mut self, label: impl Into<Option<&'a str>>) -> Self {
231 self.label = label.into();
232 self
233 }
234
235 pub fn with_entry(mut self, name: &'static str, binding: u32, kind: BindingKind) -> Self {
236 self.entries.push(BindingEntry { name, binding, kind });
237 self
238 }
239
240 pub fn with_entries(mut self, entries: impl IntoIterator<Item = BindingEntry>) -> Self {
241 self.entries.extend(entries);
242 self
243 }
244
245 pub fn build(self, backend: &Backend) -> BindGroupLayout {
246 let layout_entries: Vec<_> =
247 self.entries.iter().map(|e| e.kind.layout_entry(e.binding)).collect();
248
249 let mut seen = std::collections::HashSet::new();
250 for e in &self.entries {
251 if !seen.insert(e.binding) {
252 panic!(
253 "binding {} assigned more than once building bind group layout{} (entry '{}')",
254 e.binding,
255 self.label.map(|l| format!(" '{l}'")).unwrap_or_default(),
256 e.name
257 );
258 }
259 }
260
261 BindGroupLayout(backend.device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
262 label: self.label,
263 entries: &layout_entries,
264 }))
265 }
266}