iced_wgpu/
triangle.rs

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