iced_wgpu/
triangle.rs

1//! Draw meshes of triangles.
2mod 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        // Count the total amount of vertices & indices we need to handle
363        let count = mesh::attribute_count_of(meshes);
364
365        // Then we ensure the current attribute buffers are big enough, resizing if necessary.
366        // We are not currently using the return value of these functions as we have no system in
367        // place to calculate mesh diff, or to know whether or not that would be more performant for
368        // the majority of use cases. Therefore we will write GPU data every frame (for now).
369        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    /// Uniform values must be 256-aligned;
588    /// see: [`wgpu::Limits`] `min_uniform_buffer_offset_alignment`.
589    _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                            // Position
722                            0 => Float32x2,
723                            // Color
724                            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                            // Position
856                            0 => Float32x2,
857                            // Colors 1-2
858                            1 => Uint32x4,
859                            // Colors 3-4
860                            2 => Uint32x4,
861                            // Colors 5-6
862                            3 => Uint32x4,
863                            // Colors 7-8
864                            4 => Uint32x4,
865                            // Offsets
866                            5 => Uint32x4,
867                            // Direction
868                            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}