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