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