1mod msaa;
3
4use crate::Buffer;
5use crate::core::{Point, Rectangle, Size, Transformation, Vector};
6use crate::graphics::Antialiasing;
7use crate::graphics::mesh::{self, Mesh};
8
9use rustc_hash::FxHashMap;
10use std::collections::hash_map;
11use std::sync::Weak;
12
13const INITIAL_INDEX_COUNT: usize = 1_000;
14const INITIAL_VERTEX_COUNT: usize = 1_000;
15
16pub type Batch = Vec<Item>;
17
18#[derive(Debug)]
19pub enum Item {
20 Group {
21 transformation: Transformation,
22 meshes: Vec<Mesh>,
23 },
24 Cached {
25 transformation: Transformation,
26 cache: mesh::Cache,
27 },
28}
29
30#[derive(Debug)]
31struct Upload {
32 layer: Layer,
33 transformation: Transformation,
34 version: usize,
35 batch: Weak<[Mesh]>,
36}
37
38#[derive(Debug, Default)]
39pub struct Storage {
40 uploads: FxHashMap<mesh::Id, Upload>,
41}
42
43impl Storage {
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 fn get(&self, cache: &mesh::Cache) -> Option<&Upload> {
49 if cache.is_empty() {
50 return None;
51 }
52
53 self.uploads.get(&cache.id())
54 }
55
56 fn prepare(
57 &mut self,
58 device: &wgpu::Device,
59 encoder: &mut wgpu::CommandEncoder,
60 belt: &mut wgpu::util::StagingBelt,
61 solid: &solid::Pipeline,
62 gradient: &gradient::Pipeline,
63 cache: &mesh::Cache,
64 new_transformation: Transformation,
65 ) {
66 match self.uploads.entry(cache.id()) {
67 hash_map::Entry::Occupied(entry) => {
68 let upload = entry.into_mut();
69
70 if !cache.is_empty()
71 && (upload.version != cache.version()
72 || upload.transformation != new_transformation)
73 {
74 upload.layer.prepare(
75 device,
76 encoder,
77 belt,
78 solid,
79 gradient,
80 cache.batch(),
81 new_transformation,
82 );
83
84 upload.batch = cache.downgrade();
85 upload.version = cache.version();
86 upload.transformation = new_transformation;
87 }
88 }
89 hash_map::Entry::Vacant(entry) => {
90 let mut layer = Layer::new(device, solid, gradient);
91
92 layer.prepare(
93 device,
94 encoder,
95 belt,
96 solid,
97 gradient,
98 cache.batch(),
99 new_transformation,
100 );
101
102 let _ = entry.insert(Upload {
103 layer,
104 transformation: new_transformation,
105 version: 0,
106 batch: cache.downgrade(),
107 });
108
109 log::debug!(
110 "New mesh upload: {:?} (total: {})",
111 cache.id(),
112 self.uploads.len()
113 );
114 }
115 }
116 }
117
118 pub fn trim(&mut self) {
119 self.uploads
120 .retain(|_id, upload| upload.batch.strong_count() > 0);
121 }
122}
123
124#[derive(Debug, Clone)]
125pub struct Pipeline {
126 msaa: Option<msaa::Pipeline>,
127 solid: solid::Pipeline,
128 gradient: gradient::Pipeline,
129}
130
131pub struct State {
132 msaa: Option<msaa::State>,
133 layers: Vec<Layer>,
134 prepare_layer: usize,
135 storage: Storage,
136}
137
138impl State {
139 pub fn new(device: &wgpu::Device, pipeline: &Pipeline) -> Self {
140 Self {
141 msaa: pipeline
142 .msaa
143 .as_ref()
144 .map(|pipeline| msaa::State::new(device, pipeline)),
145 layers: Vec::new(),
146 prepare_layer: 0,
147 storage: Storage::new(),
148 }
149 }
150
151 pub fn prepare(
152 &mut self,
153 pipeline: &Pipeline,
154 device: &wgpu::Device,
155 belt: &mut wgpu::util::StagingBelt,
156 encoder: &mut wgpu::CommandEncoder,
157 items: &[Item],
158 scale: Transformation,
159 target_size: Size<u32>,
160 ) {
161 let projection =
162 if let Some((state, pipeline)) = self.msaa.as_mut().zip(pipeline.msaa.as_ref()) {
163 state.prepare(device, encoder, belt, pipeline, target_size) * scale
164 } else {
165 Transformation::orthographic(target_size.width, target_size.height) * scale
166 };
167
168 for item in items {
169 match item {
170 Item::Group {
171 transformation,
172 meshes,
173 } => {
174 if self.layers.len() <= self.prepare_layer {
175 self.layers
176 .push(Layer::new(device, &pipeline.solid, &pipeline.gradient));
177 }
178
179 let layer = &mut self.layers[self.prepare_layer];
180 layer.prepare(
181 device,
182 encoder,
183 belt,
184 &pipeline.solid,
185 &pipeline.gradient,
186 meshes,
187 projection * *transformation,
188 );
189
190 self.prepare_layer += 1;
191 }
192 Item::Cached {
193 transformation,
194 cache,
195 } => {
196 self.storage.prepare(
197 device,
198 encoder,
199 belt,
200 &pipeline.solid,
201 &pipeline.gradient,
202 cache,
203 projection * *transformation,
204 );
205 }
206 }
207 }
208 }
209
210 pub fn render(
211 &mut self,
212 pipeline: &Pipeline,
213 encoder: &mut wgpu::CommandEncoder,
214 target: &wgpu::TextureView,
215 start: usize,
216 batch: &Batch,
217 bounds: Rectangle,
218 screen_transformation: Transformation,
219 ) -> usize {
220 let mut layer_count = 0;
221
222 let items = batch.iter().filter_map(|item| match item {
223 Item::Group {
224 transformation,
225 meshes,
226 } => {
227 let layer = &self.layers[start + layer_count];
228 layer_count += 1;
229
230 Some((
231 layer,
232 meshes.as_slice(),
233 screen_transformation * *transformation,
234 ))
235 }
236 Item::Cached {
237 transformation,
238 cache,
239 } => {
240 let upload = self.storage.get(cache)?;
241
242 Some((
243 &upload.layer,
244 cache.batch(),
245 screen_transformation * *transformation,
246 ))
247 }
248 });
249
250 render(
251 encoder,
252 target,
253 self.msaa.as_ref().zip(pipeline.msaa.as_ref()),
254 &pipeline.solid,
255 &pipeline.gradient,
256 bounds,
257 items,
258 );
259
260 layer_count
261 }
262
263 pub fn trim(&mut self) {
264 self.storage.trim();
265
266 self.prepare_layer = 0;
267 }
268}
269
270impl Pipeline {
271 pub fn new(
272 device: &wgpu::Device,
273 format: wgpu::TextureFormat,
274 antialiasing: Option<Antialiasing>,
275 ) -> Pipeline {
276 Pipeline {
277 msaa: antialiasing.map(|a| msaa::Pipeline::new(device, format, a)),
278 solid: solid::Pipeline::new(device, format, antialiasing),
279 gradient: gradient::Pipeline::new(device, format, antialiasing),
280 }
281 }
282}
283
284fn render<'a>(
285 encoder: &mut wgpu::CommandEncoder,
286 target: &wgpu::TextureView,
287 mut msaa: Option<(&msaa::State, &msaa::Pipeline)>,
288 solid: &solid::Pipeline,
289 gradient: &gradient::Pipeline,
290 bounds: Rectangle,
291 group: impl Iterator<Item = (&'a Layer, &'a [Mesh], Transformation)>,
292) {
293 {
294 let mut render_pass = if let Some((_state, pipeline)) = &mut msaa {
295 pipeline.render_pass(encoder)
296 } else {
297 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
298 label: Some("iced_wgpu.triangle.render_pass"),
299 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
300 view: target,
301 depth_slice: None,
302 resolve_target: None,
303 ops: wgpu::Operations {
304 load: wgpu::LoadOp::Load,
305 store: wgpu::StoreOp::Store,
306 },
307 })],
308 depth_stencil_attachment: None,
309 timestamp_writes: None,
310 occlusion_query_set: None,
311 })
312 };
313
314 for (layer, meshes, transformation) in group {
315 layer.render(
316 solid,
317 gradient,
318 meshes,
319 bounds,
320 transformation,
321 &mut render_pass,
322 );
323 }
324 }
325
326 if let Some((state, pipeline)) = msaa {
327 state.render(pipeline, encoder, target);
328 }
329}
330
331#[derive(Debug)]
332pub struct Layer {
333 index_buffer: Buffer<u32>,
334 solid: solid::Layer,
335 gradient: gradient::Layer,
336}
337
338impl Layer {
339 fn new(device: &wgpu::Device, solid: &solid::Pipeline, gradient: &gradient::Pipeline) -> Self {
340 Self {
341 index_buffer: Buffer::new(
342 device,
343 "iced_wgpu.triangle.index_buffer",
344 INITIAL_INDEX_COUNT,
345 wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
346 ),
347 solid: solid::Layer::new(device, &solid.constants_layout),
348 gradient: gradient::Layer::new(device, &gradient.constants_layout),
349 }
350 }
351
352 fn prepare(
353 &mut self,
354 device: &wgpu::Device,
355 encoder: &mut wgpu::CommandEncoder,
356 belt: &mut wgpu::util::StagingBelt,
357 solid: &solid::Pipeline,
358 gradient: &gradient::Pipeline,
359 meshes: &[Mesh],
360 transformation: Transformation,
361 ) {
362 let count = mesh::attribute_count_of(meshes);
364
365 let _ = self.index_buffer.resize(device, count.indices);
370 let _ = self.solid.vertices.resize(device, count.solid_vertices);
371 let _ = self
372 .gradient
373 .vertices
374 .resize(device, count.gradient_vertices);
375
376 if self.solid.uniforms.resize(device, count.solids) {
377 self.solid.constants =
378 solid::Layer::bind_group(device, &self.solid.uniforms.raw, &solid.constants_layout);
379 }
380
381 if self.gradient.uniforms.resize(device, count.gradients) {
382 self.gradient.constants = gradient::Layer::bind_group(
383 device,
384 &self.gradient.uniforms.raw,
385 &gradient.constants_layout,
386 );
387 }
388
389 let mut solid_vertex_offset = 0;
390 let mut solid_uniform_offset = 0;
391 let mut gradient_vertex_offset = 0;
392 let mut gradient_uniform_offset = 0;
393 let mut index_offset = 0;
394
395 for mesh in meshes {
396 let clip_bounds = mesh.clip_bounds() * transformation;
397 let snap_distance = clip_bounds
398 .snap()
399 .map(|snapped_bounds| {
400 Point::new(snapped_bounds.x as f32, snapped_bounds.y as f32)
401 - clip_bounds.position()
402 })
403 .unwrap_or(Vector::ZERO);
404
405 let uniforms = Uniforms::new(
406 transformation
407 * mesh.transformation()
408 * Transformation::translate(snap_distance.x, snap_distance.y),
409 );
410
411 let indices = mesh.indices();
412
413 index_offset += self
414 .index_buffer
415 .write(device, encoder, belt, index_offset, indices);
416
417 match mesh {
418 Mesh::Solid { buffers, .. } => {
419 solid_vertex_offset += self.solid.vertices.write(
420 device,
421 encoder,
422 belt,
423 solid_vertex_offset,
424 &buffers.vertices,
425 );
426
427 solid_uniform_offset += self.solid.uniforms.write(
428 device,
429 encoder,
430 belt,
431 solid_uniform_offset,
432 &[uniforms],
433 );
434 }
435 Mesh::Gradient { buffers, .. } => {
436 gradient_vertex_offset += self.gradient.vertices.write(
437 device,
438 encoder,
439 belt,
440 gradient_vertex_offset,
441 &buffers.vertices,
442 );
443
444 gradient_uniform_offset += self.gradient.uniforms.write(
445 device,
446 encoder,
447 belt,
448 gradient_uniform_offset,
449 &[uniforms],
450 );
451 }
452 }
453 }
454 }
455
456 fn render<'a>(
457 &'a self,
458 solid: &'a solid::Pipeline,
459 gradient: &'a gradient::Pipeline,
460 meshes: &[Mesh],
461 bounds: Rectangle,
462 transformation: Transformation,
463 render_pass: &mut wgpu::RenderPass<'a>,
464 ) {
465 let mut num_solids = 0;
466 let mut num_gradients = 0;
467 let mut solid_offset = 0;
468 let mut gradient_offset = 0;
469 let mut index_offset = 0;
470 let mut last_is_solid = None;
471
472 for mesh in meshes {
473 let Some(clip_bounds) = bounds
474 .intersection(&(mesh.clip_bounds() * transformation))
475 .and_then(Rectangle::snap)
476 else {
477 match mesh {
478 Mesh::Solid { buffers, .. } => {
479 solid_offset += buffers.vertices.len();
480 num_solids += 1;
481 }
482 Mesh::Gradient { buffers, .. } => {
483 gradient_offset += buffers.vertices.len();
484 num_gradients += 1;
485 }
486 }
487 continue;
488 };
489
490 render_pass.set_scissor_rect(
491 clip_bounds.x,
492 clip_bounds.y,
493 clip_bounds.width,
494 clip_bounds.height,
495 );
496
497 match mesh {
498 Mesh::Solid { buffers, .. } => {
499 if !last_is_solid.unwrap_or(false) {
500 render_pass.set_pipeline(&solid.pipeline);
501
502 last_is_solid = Some(true);
503 }
504
505 render_pass.set_bind_group(
506 0,
507 &self.solid.constants,
508 &[(num_solids * std::mem::size_of::<Uniforms>()) as u32],
509 );
510
511 render_pass.set_vertex_buffer(
512 0,
513 self.solid
514 .vertices
515 .range(solid_offset, solid_offset + buffers.vertices.len()),
516 );
517
518 num_solids += 1;
519 solid_offset += buffers.vertices.len();
520 }
521 Mesh::Gradient { buffers, .. } => {
522 if last_is_solid.unwrap_or(true) {
523 render_pass.set_pipeline(&gradient.pipeline);
524
525 last_is_solid = Some(false);
526 }
527
528 render_pass.set_bind_group(
529 0,
530 &self.gradient.constants,
531 &[(num_gradients * std::mem::size_of::<Uniforms>()) as u32],
532 );
533
534 render_pass.set_vertex_buffer(
535 0,
536 self.gradient
537 .vertices
538 .range(gradient_offset, gradient_offset + buffers.vertices.len()),
539 );
540
541 num_gradients += 1;
542 gradient_offset += buffers.vertices.len();
543 }
544 };
545
546 render_pass.set_index_buffer(
547 self.index_buffer
548 .range(index_offset, index_offset + mesh.indices().len()),
549 wgpu::IndexFormat::Uint32,
550 );
551
552 render_pass.draw_indexed(0..mesh.indices().len() as u32, 0, 0..1);
553
554 index_offset += mesh.indices().len();
555 }
556 }
557}
558
559fn fragment_target(texture_format: wgpu::TextureFormat) -> wgpu::ColorTargetState {
560 wgpu::ColorTargetState {
561 format: texture_format,
562 blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
563 write_mask: wgpu::ColorWrites::ALL,
564 }
565}
566
567fn primitive_state() -> wgpu::PrimitiveState {
568 wgpu::PrimitiveState {
569 topology: wgpu::PrimitiveTopology::TriangleList,
570 front_face: wgpu::FrontFace::Cw,
571 ..Default::default()
572 }
573}
574
575fn multisample_state(antialiasing: Option<Antialiasing>) -> wgpu::MultisampleState {
576 wgpu::MultisampleState {
577 count: antialiasing.map(Antialiasing::sample_count).unwrap_or(1),
578 mask: !0,
579 alpha_to_coverage_enabled: false,
580 }
581}
582
583#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
584#[repr(C)]
585pub struct Uniforms {
586 transform: [f32; 16],
587 _padding: [f32; 48],
590}
591
592impl Uniforms {
593 pub fn new(transform: Transformation) -> Self {
594 Self {
595 transform: transform.into(),
596 _padding: [0.0; 48],
597 }
598 }
599
600 pub fn entry() -> wgpu::BindGroupLayoutEntry {
601 wgpu::BindGroupLayoutEntry {
602 binding: 0,
603 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
604 ty: wgpu::BindingType::Buffer {
605 ty: wgpu::BufferBindingType::Uniform,
606 has_dynamic_offset: true,
607 min_binding_size: wgpu::BufferSize::new(std::mem::size_of::<Self>() as u64),
608 },
609 count: None,
610 }
611 }
612
613 pub fn min_size() -> Option<wgpu::BufferSize> {
614 wgpu::BufferSize::new(std::mem::size_of::<Self>() as u64)
615 }
616}
617
618mod solid {
619 use crate::Buffer;
620 use crate::graphics::Antialiasing;
621 use crate::graphics::mesh;
622 use crate::triangle;
623
624 #[derive(Debug, Clone)]
625 pub struct Pipeline {
626 pub pipeline: wgpu::RenderPipeline,
627 pub constants_layout: wgpu::BindGroupLayout,
628 }
629
630 #[derive(Debug)]
631 pub struct Layer {
632 pub vertices: Buffer<mesh::SolidVertex2D>,
633 pub uniforms: Buffer<triangle::Uniforms>,
634 pub constants: wgpu::BindGroup,
635 }
636
637 impl Layer {
638 pub fn new(device: &wgpu::Device, constants_layout: &wgpu::BindGroupLayout) -> Self {
639 let vertices = Buffer::new(
640 device,
641 "iced_wgpu.triangle.solid.vertex_buffer",
642 triangle::INITIAL_VERTEX_COUNT,
643 wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
644 );
645
646 let uniforms = Buffer::new(
647 device,
648 "iced_wgpu.triangle.solid.uniforms",
649 1,
650 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
651 );
652
653 let constants = Self::bind_group(device, &uniforms.raw, constants_layout);
654
655 Self {
656 vertices,
657 uniforms,
658 constants,
659 }
660 }
661
662 pub fn bind_group(
663 device: &wgpu::Device,
664 buffer: &wgpu::Buffer,
665 layout: &wgpu::BindGroupLayout,
666 ) -> wgpu::BindGroup {
667 device.create_bind_group(&wgpu::BindGroupDescriptor {
668 label: Some("iced_wgpu.triangle.solid.bind_group"),
669 layout,
670 entries: &[wgpu::BindGroupEntry {
671 binding: 0,
672 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
673 buffer,
674 offset: 0,
675 size: triangle::Uniforms::min_size(),
676 }),
677 }],
678 })
679 }
680 }
681
682 impl Pipeline {
683 pub fn new(
684 device: &wgpu::Device,
685 format: wgpu::TextureFormat,
686 antialiasing: Option<Antialiasing>,
687 ) -> Self {
688 let constants_layout =
689 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
690 label: Some("iced_wgpu.triangle.solid.bind_group_layout"),
691 entries: &[triangle::Uniforms::entry()],
692 });
693
694 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
695 label: Some("iced_wgpu.triangle.solid.pipeline_layout"),
696 bind_group_layouts: &[&constants_layout],
697 push_constant_ranges: &[],
698 });
699
700 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
701 label: Some("iced_wgpu.triangle.solid.shader"),
702 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(concat!(
703 include_str!("shader/triangle.wgsl"),
704 "\n",
705 include_str!("shader/triangle/solid.wgsl"),
706 "\n",
707 include_str!("shader/color.wgsl"),
708 ))),
709 });
710
711 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
712 label: Some("iced_wgpu::triangle::solid pipeline"),
713 layout: Some(&layout),
714 vertex: wgpu::VertexState {
715 module: &shader,
716 entry_point: Some("solid_vs_main"),
717 buffers: &[wgpu::VertexBufferLayout {
718 array_stride: std::mem::size_of::<mesh::SolidVertex2D>() as u64,
719 step_mode: wgpu::VertexStepMode::Vertex,
720 attributes: &wgpu::vertex_attr_array!(
721 0 => Float32x2,
723 1 => Float32x4,
725 ),
726 }],
727 compilation_options: wgpu::PipelineCompilationOptions::default(),
728 },
729 fragment: Some(wgpu::FragmentState {
730 module: &shader,
731 entry_point: Some("solid_fs_main"),
732 targets: &[Some(triangle::fragment_target(format))],
733 compilation_options: wgpu::PipelineCompilationOptions::default(),
734 }),
735 primitive: triangle::primitive_state(),
736 depth_stencil: None,
737 multisample: triangle::multisample_state(antialiasing),
738 multiview: None,
739 cache: None,
740 });
741
742 Self {
743 pipeline,
744 constants_layout,
745 }
746 }
747 }
748}
749
750mod gradient {
751 use crate::Buffer;
752 use crate::graphics::Antialiasing;
753 use crate::graphics::mesh;
754 use crate::triangle;
755
756 #[derive(Debug, Clone)]
757 pub struct Pipeline {
758 pub pipeline: wgpu::RenderPipeline,
759 pub constants_layout: wgpu::BindGroupLayout,
760 }
761
762 #[derive(Debug)]
763 pub struct Layer {
764 pub vertices: Buffer<mesh::GradientVertex2D>,
765 pub uniforms: Buffer<triangle::Uniforms>,
766 pub constants: wgpu::BindGroup,
767 }
768
769 impl Layer {
770 pub fn new(device: &wgpu::Device, constants_layout: &wgpu::BindGroupLayout) -> Self {
771 let vertices = Buffer::new(
772 device,
773 "iced_wgpu.triangle.gradient.vertex_buffer",
774 triangle::INITIAL_VERTEX_COUNT,
775 wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
776 );
777
778 let uniforms = Buffer::new(
779 device,
780 "iced_wgpu.triangle.gradient.uniforms",
781 1,
782 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
783 );
784
785 let constants = Self::bind_group(device, &uniforms.raw, constants_layout);
786
787 Self {
788 vertices,
789 uniforms,
790 constants,
791 }
792 }
793
794 pub fn bind_group(
795 device: &wgpu::Device,
796 uniform_buffer: &wgpu::Buffer,
797 layout: &wgpu::BindGroupLayout,
798 ) -> wgpu::BindGroup {
799 device.create_bind_group(&wgpu::BindGroupDescriptor {
800 label: Some("iced_wgpu.triangle.gradient.bind_group"),
801 layout,
802 entries: &[wgpu::BindGroupEntry {
803 binding: 0,
804 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
805 buffer: uniform_buffer,
806 offset: 0,
807 size: triangle::Uniforms::min_size(),
808 }),
809 }],
810 })
811 }
812 }
813
814 impl Pipeline {
815 pub fn new(
816 device: &wgpu::Device,
817 format: wgpu::TextureFormat,
818 antialiasing: Option<Antialiasing>,
819 ) -> Self {
820 let constants_layout =
821 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
822 label: Some("iced_wgpu.triangle.gradient.bind_group_layout"),
823 entries: &[triangle::Uniforms::entry()],
824 });
825
826 let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
827 label: Some("iced_wgpu.triangle.gradient.pipeline_layout"),
828 bind_group_layouts: &[&constants_layout],
829 push_constant_ranges: &[],
830 });
831
832 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
833 label: Some("iced_wgpu.triangle.gradient.shader"),
834 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(concat!(
835 include_str!("shader/triangle.wgsl"),
836 "\n",
837 include_str!("shader/triangle/gradient.wgsl"),
838 "\n",
839 include_str!("shader/color.wgsl"),
840 "\n",
841 include_str!("shader/color/linear_rgb.wgsl")
842 ))),
843 });
844
845 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
846 label: Some("iced_wgpu.triangle.gradient.pipeline"),
847 layout: Some(&layout),
848 vertex: wgpu::VertexState {
849 module: &shader,
850 entry_point: Some("gradient_vs_main"),
851 buffers: &[wgpu::VertexBufferLayout {
852 array_stride: std::mem::size_of::<mesh::GradientVertex2D>() as u64,
853 step_mode: wgpu::VertexStepMode::Vertex,
854 attributes: &wgpu::vertex_attr_array!(
855 0 => Float32x2,
857 1 => Uint32x4,
859 2 => Uint32x4,
861 3 => Uint32x4,
863 4 => Uint32x4,
865 5 => Uint32x4,
867 6 => Float32x4
869 ),
870 }],
871 compilation_options: wgpu::PipelineCompilationOptions::default(),
872 },
873 fragment: Some(wgpu::FragmentState {
874 module: &shader,
875 entry_point: Some("gradient_fs_main"),
876 targets: &[Some(triangle::fragment_target(format))],
877 compilation_options: wgpu::PipelineCompilationOptions::default(),
878 }),
879 primitive: triangle::primitive_state(),
880 depth_stencil: None,
881 multisample: triangle::multisample_state(antialiasing),
882 multiview: None,
883 cache: None,
884 });
885
886 Self {
887 pipeline,
888 constants_layout,
889 }
890 }
891 }
892}