1use bytemuck::{Pod, Zeroable};
2use std::f32::consts;
3use wgpu::util::DeviceExt;
4
5#[repr(C)]
6#[derive(Clone, Copy, Pod, Zeroable)]
7struct Vertex {
8 _pos: [f32; 4],
9 _tex_coord: [f32; 2],
10}
11
12fn vertex(pos: [i8; 3], tc: [i8; 2]) -> Vertex {
13 Vertex {
14 _pos: [pos[0] as f32, pos[1] as f32, pos[2] as f32, 1.0],
15 _tex_coord: [tc[0] as f32, tc[1] as f32],
16 }
17}
18
19fn create_vertices() -> (Vec<Vertex>, Vec<u16>) {
20 let vertex_data = [
21 vertex([-1, -1, 1], [0, 0]),
23 vertex([1, -1, 1], [1, 0]),
24 vertex([1, 1, 1], [1, 1]),
25 vertex([-1, 1, 1], [0, 1]),
26 vertex([-1, 1, -1], [1, 0]),
28 vertex([1, 1, -1], [0, 0]),
29 vertex([1, -1, -1], [0, 1]),
30 vertex([-1, -1, -1], [1, 1]),
31 vertex([1, -1, -1], [0, 0]),
33 vertex([1, 1, -1], [1, 0]),
34 vertex([1, 1, 1], [1, 1]),
35 vertex([1, -1, 1], [0, 1]),
36 vertex([-1, -1, 1], [1, 0]),
38 vertex([-1, 1, 1], [0, 0]),
39 vertex([-1, 1, -1], [0, 1]),
40 vertex([-1, -1, -1], [1, 1]),
41 vertex([1, 1, -1], [1, 0]),
43 vertex([-1, 1, -1], [0, 0]),
44 vertex([-1, 1, 1], [0, 1]),
45 vertex([1, 1, 1], [1, 1]),
46 vertex([1, -1, 1], [0, 0]),
48 vertex([-1, -1, 1], [1, 0]),
49 vertex([-1, -1, -1], [1, 1]),
50 vertex([1, -1, -1], [0, 1]),
51 ];
52
53 let index_data: &[u16] = &[
54 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 8, 9, 10, 10, 11, 8, 12, 13, 14, 14, 15, 12, 16, 17, 18, 18, 19, 16, 20, 21, 22, 22, 23, 20, ];
61
62 (vertex_data.to_vec(), index_data.to_vec())
63}
64
65fn create_texels(size: usize) -> Vec<u8> {
66 (0..size * size)
67 .map(|id| {
68 let cx = 3.0 * (id % size) as f32 / (size - 1) as f32 - 2.0;
70 let cy = 2.0 * (id / size) as f32 / (size - 1) as f32 - 1.0;
71 let (mut x, mut y, mut count) = (cx, cy, 0);
72 while count < 0xFF && x * x + y * y < 4.0 {
73 let old_x = x;
74 x = x * x - y * y + cx;
75 y = 2.0 * old_x * y + cy;
76 count += 1;
77 }
78 count
79 })
80 .collect()
81}
82
83struct Example {
84 vertex_buf: wgpu::Buffer,
85 index_buf: wgpu::Buffer,
86 index_count: usize,
87 bind_group: wgpu::BindGroup,
88 uniform_buf: wgpu::Buffer,
89 pipeline: wgpu::RenderPipeline,
90 pipeline_wire: Option<wgpu::RenderPipeline>,
91}
92
93impl Example {
94 fn generate_matrix(aspect_ratio: f32) -> glam::Mat4 {
95 let projection = glam::Mat4::perspective_rh(consts::FRAC_PI_4, aspect_ratio, 1.0, 10.0);
96 let view = glam::Mat4::look_at_rh(
97 glam::Vec3::new(1.5f32, -5.0, 3.0),
98 glam::Vec3::ZERO,
99 glam::Vec3::Z,
100 );
101 projection * view
102 }
103}
104
105impl crate::framework::Example for Example {
106 fn optional_features() -> wgpu::Features {
107 wgpu::Features::POLYGON_MODE_LINE
108 }
109
110 fn init(
111 config: &wgpu::SurfaceConfiguration,
112 _adapter: &wgpu::Adapter,
113 device: &wgpu::Device,
114 queue: &wgpu::Queue,
115 ) -> Self {
116 let vertex_size = size_of::<Vertex>();
118 let (vertex_data, index_data) = create_vertices();
119
120 let vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
121 label: Some("Vertex Buffer"),
122 contents: bytemuck::cast_slice(&vertex_data),
123 usage: wgpu::BufferUsages::VERTEX,
124 });
125
126 let index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
127 label: Some("Index Buffer"),
128 contents: bytemuck::cast_slice(&index_data),
129 usage: wgpu::BufferUsages::INDEX,
130 });
131
132 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
134 label: None,
135 entries: &[
136 wgpu::BindGroupLayoutEntry {
137 binding: 0,
138 visibility: wgpu::ShaderStages::VERTEX,
139 ty: wgpu::BindingType::Buffer {
140 ty: wgpu::BufferBindingType::Uniform,
141 has_dynamic_offset: false,
142 min_binding_size: wgpu::BufferSize::new(64),
143 },
144 count: None,
145 },
146 wgpu::BindGroupLayoutEntry {
147 binding: 1,
148 visibility: wgpu::ShaderStages::FRAGMENT,
149 ty: wgpu::BindingType::Texture {
150 multisampled: false,
151 sample_type: wgpu::TextureSampleType::Uint,
152 view_dimension: wgpu::TextureViewDimension::D2,
153 },
154 count: None,
155 },
156 ],
157 });
158 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
159 label: None,
160 bind_group_layouts: &[&bind_group_layout],
161 push_constant_ranges: &[],
162 });
163
164 let size = 256u32;
166 let texels = create_texels(size as usize);
167 let texture_extent = wgpu::Extent3d {
168 width: size,
169 height: size,
170 depth_or_array_layers: 1,
171 };
172 let texture = device.create_texture(&wgpu::TextureDescriptor {
173 label: None,
174 size: texture_extent,
175 mip_level_count: 1,
176 sample_count: 1,
177 dimension: wgpu::TextureDimension::D2,
178 format: wgpu::TextureFormat::R8Uint,
179 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
180 view_formats: &[],
181 });
182 let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
183 queue.write_texture(
184 texture.as_image_copy(),
185 &texels,
186 wgpu::TexelCopyBufferLayout {
187 offset: 0,
188 bytes_per_row: Some(size),
189 rows_per_image: None,
190 },
191 texture_extent,
192 );
193
194 let mx_total = Self::generate_matrix(config.width as f32 / config.height as f32);
196 let mx_ref: &[f32; 16] = mx_total.as_ref();
197 let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
198 label: Some("Uniform Buffer"),
199 contents: bytemuck::cast_slice(mx_ref),
200 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
201 });
202
203 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
205 layout: &bind_group_layout,
206 entries: &[
207 wgpu::BindGroupEntry {
208 binding: 0,
209 resource: uniform_buf.as_entire_binding(),
210 },
211 wgpu::BindGroupEntry {
212 binding: 1,
213 resource: wgpu::BindingResource::TextureView(&texture_view),
214 },
215 ],
216 label: None,
217 });
218
219 let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
220
221 let vertex_buffers = [wgpu::VertexBufferLayout {
222 array_stride: vertex_size as wgpu::BufferAddress,
223 step_mode: wgpu::VertexStepMode::Vertex,
224 attributes: &[
225 wgpu::VertexAttribute {
226 format: wgpu::VertexFormat::Float32x4,
227 offset: 0,
228 shader_location: 0,
229 },
230 wgpu::VertexAttribute {
231 format: wgpu::VertexFormat::Float32x2,
232 offset: 4 * 4,
233 shader_location: 1,
234 },
235 ],
236 }];
237
238 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
239 label: None,
240 layout: Some(&pipeline_layout),
241 vertex: wgpu::VertexState {
242 module: &shader,
243 entry_point: Some("vs_main"),
244 compilation_options: Default::default(),
245 buffers: &vertex_buffers,
246 },
247 fragment: Some(wgpu::FragmentState {
248 module: &shader,
249 entry_point: Some("fs_main"),
250 compilation_options: Default::default(),
251 targets: &[Some(config.view_formats[0].into())],
252 }),
253 primitive: wgpu::PrimitiveState {
254 cull_mode: Some(wgpu::Face::Back),
255 ..Default::default()
256 },
257 depth_stencil: None,
258 multisample: wgpu::MultisampleState::default(),
259 multiview: None,
260 cache: None,
261 });
262
263 let pipeline_wire = if device
264 .features()
265 .contains(wgpu::Features::POLYGON_MODE_LINE)
266 {
267 let pipeline_wire = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
268 label: None,
269 layout: Some(&pipeline_layout),
270 vertex: wgpu::VertexState {
271 module: &shader,
272 entry_point: Some("vs_main"),
273 compilation_options: Default::default(),
274 buffers: &vertex_buffers,
275 },
276 fragment: Some(wgpu::FragmentState {
277 module: &shader,
278 entry_point: Some("fs_wire"),
279 compilation_options: Default::default(),
280 targets: &[Some(wgpu::ColorTargetState {
281 format: config.view_formats[0],
282 blend: Some(wgpu::BlendState {
283 color: wgpu::BlendComponent {
284 operation: wgpu::BlendOperation::Add,
285 src_factor: wgpu::BlendFactor::SrcAlpha,
286 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
287 },
288 alpha: wgpu::BlendComponent::REPLACE,
289 }),
290 write_mask: wgpu::ColorWrites::ALL,
291 })],
292 }),
293 primitive: wgpu::PrimitiveState {
294 front_face: wgpu::FrontFace::Ccw,
295 cull_mode: Some(wgpu::Face::Back),
296 polygon_mode: wgpu::PolygonMode::Line,
297 ..Default::default()
298 },
299 depth_stencil: None,
300 multisample: wgpu::MultisampleState::default(),
301 multiview: None,
302 cache: None,
303 });
304 Some(pipeline_wire)
305 } else {
306 None
307 };
308
309 Example {
311 vertex_buf,
312 index_buf,
313 index_count: index_data.len(),
314 bind_group,
315 uniform_buf,
316 pipeline,
317 pipeline_wire,
318 }
319 }
320
321 fn update(&mut self, _event: winit::event::WindowEvent) {
322 }
324
325 fn resize(
326 &mut self,
327 config: &wgpu::SurfaceConfiguration,
328 _device: &wgpu::Device,
329 queue: &wgpu::Queue,
330 ) {
331 let mx_total = Self::generate_matrix(config.width as f32 / config.height as f32);
332 let mx_ref: &[f32; 16] = mx_total.as_ref();
333 queue.write_buffer(&self.uniform_buf, 0, bytemuck::cast_slice(mx_ref));
334 }
335
336 fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
337 let mut encoder =
338 device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
339 {
340 let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
341 label: None,
342 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
343 view,
344 depth_slice: None,
345 resolve_target: None,
346 ops: wgpu::Operations {
347 load: wgpu::LoadOp::Clear(wgpu::Color {
348 r: 0.1,
349 g: 0.2,
350 b: 0.3,
351 a: 1.0,
352 }),
353 store: wgpu::StoreOp::Store,
354 },
355 })],
356 depth_stencil_attachment: None,
357 timestamp_writes: None,
358 occlusion_query_set: None,
359 });
360 rpass.push_debug_group("Prepare data for draw.");
361 rpass.set_pipeline(&self.pipeline);
362 rpass.set_bind_group(0, &self.bind_group, &[]);
363 rpass.set_index_buffer(self.index_buf.slice(..), wgpu::IndexFormat::Uint16);
364 rpass.set_vertex_buffer(0, self.vertex_buf.slice(..));
365 rpass.pop_debug_group();
366 rpass.insert_debug_marker("Draw!");
367 rpass.draw_indexed(0..self.index_count as u32, 0, 0..1);
368 if let Some(ref pipe) = self.pipeline_wire {
369 rpass.set_pipeline(pipe);
370 rpass.draw_indexed(0..self.index_count as u32, 0, 0..1);
371 }
372 }
373
374 queue.submit(Some(encoder.finish()));
375 }
376}
377
378pub fn main() {
379 crate::framework::run::<Example>("cube");
380}
381
382#[cfg(test)]
383#[wgpu_test::gpu_test]
384pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
385 name: "cube",
386 image_path: "/examples/features/src/cube/screenshot.png",
388 width: 1024,
389 height: 768,
390 optional_features: wgpu::Features::default(),
391 base_test_parameters: wgpu_test::TestParameters::default(),
392 comparisons: &[
393 wgpu_test::ComparisonType::Mean(0.04), ],
395 _phantom: std::marker::PhantomData::<Example>,
396};
397
398#[cfg(test)]
399#[wgpu_test::gpu_test]
400pub static TEST_LINES: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams {
401 name: "cube-lines",
402 image_path: "/examples/features/src/cube/screenshot-lines.png",
404 width: 1024,
405 height: 768,
406 optional_features: wgpu::Features::POLYGON_MODE_LINE,
407 base_test_parameters: wgpu_test::TestParameters::default(),
408 comparisons: &[
410 wgpu_test::ComparisonType::Mean(0.05), wgpu_test::ComparisonType::Percentile {
412 percentile: 0.95,
413 threshold: 0.36,
414 }, ],
416 _phantom: std::marker::PhantomData::<Example>,
417};