1use bytemuck::{Pod, Zeroable};
2use std::num::{NonZeroU32, NonZeroU64};
3use wgpu::util::DeviceExt;
4
5#[repr(C)]
6#[derive(Clone, Copy, Pod, Zeroable)]
7struct Vertex {
8 _pos: [f32; 2],
9 _tex_coord: [f32; 2],
10 _index: u32,
11}
12
13fn vertex(pos: [i8; 2], tc: [i8; 2], index: i8) -> Vertex {
14 Vertex {
15 _pos: [pos[0] as f32, pos[1] as f32],
16 _tex_coord: [tc[0] as f32, tc[1] as f32],
17 _index: index as u32,
18 }
19}
20
21fn create_vertices() -> Vec<Vertex> {
22 vec![
23 vertex([-1, -1], [0, 1], 0),
25 vertex([-1, 1], [0, 0], 0),
26 vertex([0, 1], [1, 0], 0),
27 vertex([0, -1], [1, 1], 0),
28 vertex([0, -1], [0, 1], 1),
30 vertex([0, 1], [0, 0], 1),
31 vertex([1, 1], [1, 0], 1),
32 vertex([1, -1], [1, 1], 1),
33 ]
34}
35
36fn create_indices() -> Vec<u16> {
37 vec![
38 0, 1, 2, 2, 0, 3, 4, 5, 6, 6, 4, 7, ]
45}
46
47#[derive(Copy, Clone)]
48enum Color {
49 Red,
50 Green,
51 Blue,
52 White,
53}
54
55fn create_texture_data(color: Color) -> [u8; 4] {
56 match color {
57 Color::Red => [255, 0, 0, 255],
58 Color::Green => [0, 255, 0, 255],
59 Color::Blue => [0, 0, 255, 255],
60 Color::White => [255, 255, 255, 255],
61 }
62}
63
64struct Example {
65 pipeline: wgpu::RenderPipeline,
66 bind_group: wgpu::BindGroup,
67 uniform_bind_group: wgpu::BindGroup,
68 vertex_buffer: wgpu::Buffer,
69 index_buffer: wgpu::Buffer,
70 index_format: wgpu::IndexFormat,
71 uniform_workaround: bool,
72}
73
74impl crate::framework::Example for Example {
75 fn optional_features() -> wgpu::Features {
76 wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
77 }
78 fn required_features() -> wgpu::Features {
79 wgpu::Features::TEXTURE_BINDING_ARRAY
80 }
81 fn required_limits() -> wgpu::Limits {
82 wgpu::Limits {
83 max_binding_array_elements_per_shader_stage: 6,
84 max_binding_array_sampler_elements_per_shader_stage: 2,
85 ..wgpu::Limits::downlevel_defaults()
86 }
87 }
88 fn init(
89 config: &wgpu::SurfaceConfiguration,
90 _adapter: &wgpu::Adapter,
91 device: &wgpu::Device,
92 queue: &wgpu::Queue,
93 ) -> Self {
94 let mut uniform_workaround = false;
95 let base_shader_module = device.create_shader_module(wgpu::include_wgsl!("indexing.wgsl"));
96 let env_override = match std::env::var("WGPU_TEXTURE_ARRAY_STYLE") {
97 Ok(value) => match &*value.to_lowercase() {
98 "nonuniform" | "non_uniform" => Some(true),
99 "uniform" => Some(false),
100 _ => None,
101 },
102 Err(_) => None,
103 };
104 let fragment_entry_point = match (device.features(), env_override) {
105 (_, Some(false)) => {
106 uniform_workaround = true;
107 "uniform_main"
108 }
109 (_, Some(true)) => "non_uniform_main",
110 (f, _)
111 if f.contains(
112 wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
113 ) =>
114 {
115 "non_uniform_main"
116 }
117 _ => {
118 uniform_workaround = true;
119 "uniform_main"
120 }
121 };
122 let non_uniform_shader_module;
123 let fragment_shader_module = if !uniform_workaround {
126 non_uniform_shader_module =
127 device.create_shader_module(wgpu::include_wgsl!("non_uniform_indexing.wgsl"));
128 &non_uniform_shader_module
129 } else {
130 &base_shader_module
131 };
132
133 println!("Using fragment entry point '{fragment_entry_point}'");
134
135 let vertex_size = size_of::<Vertex>();
136 let vertex_data = create_vertices();
137 let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
138 label: Some("Vertex Buffer"),
139 contents: bytemuck::cast_slice(&vertex_data),
140 usage: wgpu::BufferUsages::VERTEX,
141 });
142
143 let index_data = create_indices();
144 let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
145 label: Some("Index Buffer"),
146 contents: bytemuck::cast_slice(&index_data),
147 usage: wgpu::BufferUsages::INDEX,
148 });
149
150 let mut texture_index_buffer_contents = vec![0u32; 128];
151 texture_index_buffer_contents[0] = 0;
152 texture_index_buffer_contents[64] = 1;
153 let texture_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
154 label: Some("Index Buffer"),
155 contents: bytemuck::cast_slice(&texture_index_buffer_contents),
156 usage: wgpu::BufferUsages::UNIFORM,
157 });
158
159 let red_texture_data = create_texture_data(Color::Red);
160 let green_texture_data = create_texture_data(Color::Green);
161 let blue_texture_data = create_texture_data(Color::Blue);
162 let white_texture_data = create_texture_data(Color::White);
163
164 let texture_descriptor = wgpu::TextureDescriptor {
165 size: wgpu::Extent3d::default(),
166 mip_level_count: 1,
167 sample_count: 1,
168 dimension: wgpu::TextureDimension::D2,
169 format: wgpu::TextureFormat::Rgba8UnormSrgb,
170 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
171 label: None,
172 view_formats: &[],
173 };
174 let red_texture = device.create_texture(&wgpu::TextureDescriptor {
175 label: Some("red"),
176 view_formats: &[],
177 ..texture_descriptor
178 });
179 let green_texture = device.create_texture(&wgpu::TextureDescriptor {
180 label: Some("green"),
181 view_formats: &[],
182 ..texture_descriptor
183 });
184 let blue_texture = device.create_texture(&wgpu::TextureDescriptor {
185 label: Some("blue"),
186 view_formats: &[],
187 ..texture_descriptor
188 });
189 let white_texture = device.create_texture(&wgpu::TextureDescriptor {
190 label: Some("white"),
191 view_formats: &[],
192 ..texture_descriptor
193 });
194
195 let red_texture_view = red_texture.create_view(&wgpu::TextureViewDescriptor::default());
196 let green_texture_view = green_texture.create_view(&wgpu::TextureViewDescriptor::default());
197 let blue_texture_view = blue_texture.create_view(&wgpu::TextureViewDescriptor::default());
198 let white_texture_view = white_texture.create_view(&wgpu::TextureViewDescriptor::default());
199
200 queue.write_texture(
201 red_texture.as_image_copy(),
202 &red_texture_data,
203 wgpu::TexelCopyBufferLayout {
204 offset: 0,
205 bytes_per_row: Some(4),
206 rows_per_image: None,
207 },
208 wgpu::Extent3d::default(),
209 );
210 queue.write_texture(
211 green_texture.as_image_copy(),
212 &green_texture_data,
213 wgpu::TexelCopyBufferLayout {
214 offset: 0,
215 bytes_per_row: Some(4),
216 rows_per_image: None,
217 },
218 wgpu::Extent3d::default(),
219 );
220 queue.write_texture(
221 blue_texture.as_image_copy(),
222 &blue_texture_data,
223 wgpu::TexelCopyBufferLayout {
224 offset: 0,
225 bytes_per_row: Some(4),
226 rows_per_image: None,
227 },
228 wgpu::Extent3d::default(),
229 );
230 queue.write_texture(
231 white_texture.as_image_copy(),
232 &white_texture_data,
233 wgpu::TexelCopyBufferLayout {
234 offset: 0,
235 bytes_per_row: Some(4),
236 rows_per_image: None,
237 },
238 wgpu::Extent3d::default(),
239 );
240
241 let sampler = device.create_sampler(&wgpu::SamplerDescriptor::default());
242
243 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
244 label: Some("bind group layout"),
245 entries: &[
246 wgpu::BindGroupLayoutEntry {
247 binding: 0,
248 visibility: wgpu::ShaderStages::FRAGMENT,
249 ty: wgpu::BindingType::Texture {
250 sample_type: wgpu::TextureSampleType::Float { filterable: true },
251 view_dimension: wgpu::TextureViewDimension::D2,
252 multisampled: false,
253 },
254 count: NonZeroU32::new(2),
255 },
256 wgpu::BindGroupLayoutEntry {
257 binding: 1,
258 visibility: wgpu::ShaderStages::FRAGMENT,
259 ty: wgpu::BindingType::Texture {
260 sample_type: wgpu::TextureSampleType::Float { filterable: true },
261 view_dimension: wgpu::TextureViewDimension::D2,
262 multisampled: false,
263 },
264 count: NonZeroU32::new(2),
265 },
266 wgpu::BindGroupLayoutEntry {
267 binding: 2,
268 visibility: wgpu::ShaderStages::FRAGMENT,
269 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
270 count: NonZeroU32::new(2),
271 },
272 ],
273 });
274
275 let uniform_bind_group_layout =
276 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
277 label: Some("uniform bind group layout"),
278 entries: &[wgpu::BindGroupLayoutEntry {
279 binding: 0,
280 visibility: wgpu::ShaderStages::FRAGMENT,
281 ty: wgpu::BindingType::Buffer {
282 ty: wgpu::BufferBindingType::Uniform,
283 has_dynamic_offset: true,
284 min_binding_size: Some(NonZeroU64::new(4).unwrap()),
285 },
286 count: None,
287 }],
288 });
289
290 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
291 entries: &[
292 wgpu::BindGroupEntry {
293 binding: 0,
294 resource: wgpu::BindingResource::TextureViewArray(&[
295 &red_texture_view,
296 &green_texture_view,
297 ]),
298 },
299 wgpu::BindGroupEntry {
300 binding: 1,
301 resource: wgpu::BindingResource::TextureViewArray(&[
302 &blue_texture_view,
303 &white_texture_view,
304 ]),
305 },
306 wgpu::BindGroupEntry {
307 binding: 2,
308 resource: wgpu::BindingResource::SamplerArray(&[&sampler, &sampler]),
309 },
310 ],
311 layout: &bind_group_layout,
312 label: Some("bind group"),
313 });
314
315 let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
316 entries: &[wgpu::BindGroupEntry {
317 binding: 0,
318 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
319 buffer: &texture_index_buffer,
320 offset: 0,
321 size: Some(NonZeroU64::new(4).unwrap()),
322 }),
323 }],
324 layout: &uniform_bind_group_layout,
325 label: Some("uniform bind group"),
326 });
327
328 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
329 label: Some("main"),
330 bind_group_layouts: &[&bind_group_layout, &uniform_bind_group_layout],
331 push_constant_ranges: &[],
332 });
333
334 let index_format = wgpu::IndexFormat::Uint16;
335
336 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
337 label: None,
338 layout: Some(&pipeline_layout),
339 vertex: wgpu::VertexState {
340 module: &base_shader_module,
341 entry_point: Some("vert_main"),
342 compilation_options: Default::default(),
343 buffers: &[wgpu::VertexBufferLayout {
344 array_stride: vertex_size as wgpu::BufferAddress,
345 step_mode: wgpu::VertexStepMode::Vertex,
346 attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Sint32],
347 }],
348 },
349 fragment: Some(wgpu::FragmentState {
350 module: fragment_shader_module,
351 entry_point: Some(fragment_entry_point),
352 compilation_options: Default::default(),
353 targets: &[Some(config.view_formats[0].into())],
354 }),
355 primitive: wgpu::PrimitiveState {
356 front_face: wgpu::FrontFace::Ccw,
357 ..Default::default()
358 },
359 depth_stencil: None,
360 multisample: wgpu::MultisampleState::default(),
361 multiview: None,
362 cache: None
363 });
364
365 Self {
366 pipeline,
367 bind_group,
368 uniform_bind_group,
369 vertex_buffer,
370 index_buffer,
371 index_format,
372 uniform_workaround,
373 }
374 }
375 fn resize(
376 &mut self,
377 _sc_desc: &wgpu::SurfaceConfiguration,
378 _device: &wgpu::Device,
379 _queue: &wgpu::Queue,
380 ) {
381 }
383 fn update(&mut self, _event: winit::event::WindowEvent) {
384 }
386 fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
387 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
388 label: Some("primary"),
389 });
390
391 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
392 label: None,
393 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
394 view,
395 depth_slice: None,
396 resolve_target: None,
397 ops: wgpu::Operations {
398 load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
399 store: wgpu::StoreOp::Store,
400 },
401 })],
402 depth_stencil_attachment: None,
403 timestamp_writes: None,
404 occlusion_query_set: None,
405 });
406
407 rpass.set_pipeline(&self.pipeline);
408 rpass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
409 rpass.set_index_buffer(self.index_buffer.slice(..), self.index_format);
410 if self.uniform_workaround {
411 rpass.set_bind_group(0, &self.bind_group, &[]);
412 rpass.set_bind_group(1, &self.uniform_bind_group, &[0]);
413 rpass.draw_indexed(0..6, 0, 0..1);
414 rpass.set_bind_group(1, &self.uniform_bind_group, &[256]);
415 rpass.draw_indexed(6..12, 0, 0..1);
416 } else {
417 rpass.set_bind_group(0, &self.bind_group, &[]);
418 rpass.set_bind_group(1, &self.uniform_bind_group, &[0]);
419 rpass.draw_indexed(0..12, 0, 0..1);
420 }
421
422 drop(rpass);
423
424 queue.submit(Some(encoder.finish()));
425 }
426}
427
428pub fn main() {
429 crate::framework::run::<Example>("texture-arrays");
430}
431
432#[cfg(test)]
433#[wgpu_test::gpu_test]
434pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
435 name: "texture-arrays",
436 image_path: "/examples/features/src/texture_arrays/screenshot.png",
437 width: 1024,
438 height: 768,
439 optional_features: wgpu::Features::empty(),
440 base_test_parameters: wgpu_test::TestParameters::default(),
441 comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
442 _phantom: std::marker::PhantomData::<Example>,
443};
444
445#[cfg(test)]
446#[wgpu_test::gpu_test]
447pub static TEST_UNIFORM: crate::framework::ExampleTestParams =
448 crate::framework::ExampleTestParams {
449 name: "texture-arrays-uniform",
450 image_path: "/examples/features/src/texture_arrays/screenshot.png",
451 width: 1024,
452 height: 768,
453 optional_features: wgpu::Features::empty(),
454 base_test_parameters: wgpu_test::TestParameters::default(),
455 comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
456 _phantom: std::marker::PhantomData::<Example>,
457 };
458
459#[cfg(test)]
460#[wgpu_test::gpu_test]
461pub static TEST_NON_UNIFORM: crate::framework::ExampleTestParams =
462 crate::framework::ExampleTestParams {
463 name: "texture-arrays-non-uniform",
464 image_path: "/examples/features/src/texture_arrays/screenshot.png",
465 width: 1024,
466 height: 768,
467 optional_features:
468 wgpu::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING,
469 base_test_parameters: wgpu_test::TestParameters::default(),
470 comparisons: &[wgpu_test::ComparisonType::Mean(0.0)],
471 _phantom: std::marker::PhantomData::<Example>,
472 };