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::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                        )),
758                    ),
759                });
760
761            let pipeline =
762                device.create_render_pipeline(
763                    &wgpu::RenderPipelineDescriptor {
764                        label: Some("iced_wgpu::triangle::solid pipeline"),
765                        layout: Some(&layout),
766                        vertex: wgpu::VertexState {
767                            module: &shader,
768                            entry_point: Some("solid_vs_main"),
769                            buffers: &[wgpu::VertexBufferLayout {
770                                array_stride: std::mem::size_of::<
771                                    mesh::SolidVertex2D,
772                                >(
773                                )
774                                    as u64,
775                                step_mode: wgpu::VertexStepMode::Vertex,
776                                attributes: &wgpu::vertex_attr_array!(
777                                    // Position
778                                    0 => Float32x2,
779                                    // Color
780                                    1 => Float32x4,
781                                ),
782                            }],
783                            compilation_options:
784                                wgpu::PipelineCompilationOptions::default(),
785                        },
786                        fragment: Some(wgpu::FragmentState {
787                            module: &shader,
788                            entry_point: Some("solid_fs_main"),
789                            targets: &[Some(triangle::fragment_target(format))],
790                            compilation_options:
791                                wgpu::PipelineCompilationOptions::default(),
792                        }),
793                        primitive: triangle::primitive_state(),
794                        depth_stencil: None,
795                        multisample: triangle::multisample_state(antialiasing),
796                        multiview: None,
797                        cache: None,
798                    },
799                );
800
801            Self {
802                pipeline,
803                constants_layout,
804            }
805        }
806    }
807}
808
809mod gradient {
810    use crate::Buffer;
811    use crate::graphics::Antialiasing;
812    use crate::graphics::color;
813    use crate::graphics::mesh;
814    use crate::triangle;
815
816    #[derive(Debug, Clone)]
817    pub struct Pipeline {
818        pub pipeline: wgpu::RenderPipeline,
819        pub constants_layout: wgpu::BindGroupLayout,
820    }
821
822    #[derive(Debug)]
823    pub struct Layer {
824        pub vertices: Buffer<mesh::GradientVertex2D>,
825        pub uniforms: Buffer<triangle::Uniforms>,
826        pub constants: wgpu::BindGroup,
827    }
828
829    impl Layer {
830        pub fn new(
831            device: &wgpu::Device,
832            constants_layout: &wgpu::BindGroupLayout,
833        ) -> Self {
834            let vertices = Buffer::new(
835                device,
836                "iced_wgpu.triangle.gradient.vertex_buffer",
837                triangle::INITIAL_VERTEX_COUNT,
838                wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
839            );
840
841            let uniforms = Buffer::new(
842                device,
843                "iced_wgpu.triangle.gradient.uniforms",
844                1,
845                wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
846            );
847
848            let constants =
849                Self::bind_group(device, &uniforms.raw, constants_layout);
850
851            Self {
852                vertices,
853                uniforms,
854                constants,
855            }
856        }
857
858        pub fn bind_group(
859            device: &wgpu::Device,
860            uniform_buffer: &wgpu::Buffer,
861            layout: &wgpu::BindGroupLayout,
862        ) -> wgpu::BindGroup {
863            device.create_bind_group(&wgpu::BindGroupDescriptor {
864                label: Some("iced_wgpu.triangle.gradient.bind_group"),
865                layout,
866                entries: &[wgpu::BindGroupEntry {
867                    binding: 0,
868                    resource: wgpu::BindingResource::Buffer(
869                        wgpu::BufferBinding {
870                            buffer: uniform_buffer,
871                            offset: 0,
872                            size: triangle::Uniforms::min_size(),
873                        },
874                    ),
875                }],
876            })
877        }
878    }
879
880    impl Pipeline {
881        pub fn new(
882            device: &wgpu::Device,
883            format: wgpu::TextureFormat,
884            antialiasing: Option<Antialiasing>,
885        ) -> Self {
886            let constants_layout = device.create_bind_group_layout(
887                &wgpu::BindGroupLayoutDescriptor {
888                    label: Some(
889                        "iced_wgpu.triangle.gradient.bind_group_layout",
890                    ),
891                    entries: &[triangle::Uniforms::entry()],
892                },
893            );
894
895            let layout = device.create_pipeline_layout(
896                &wgpu::PipelineLayoutDescriptor {
897                    label: Some("iced_wgpu.triangle.gradient.pipeline_layout"),
898                    bind_group_layouts: &[&constants_layout],
899                    push_constant_ranges: &[],
900                },
901            );
902
903            let shader =
904                device.create_shader_module(wgpu::ShaderModuleDescriptor {
905                    label: Some("iced_wgpu.triangle.gradient.shader"),
906                    source: wgpu::ShaderSource::Wgsl(
907                        std::borrow::Cow::Borrowed(
908                            if color::GAMMA_CORRECTION {
909                                concat!(
910                                    include_str!("shader/triangle.wgsl"),
911                                    "\n",
912                                    include_str!(
913                                        "shader/triangle/gradient.wgsl"
914                                    ),
915                                    "\n",
916                                    include_str!("shader/color/oklab.wgsl")
917                                )
918                            } else {
919                                concat!(
920                                    include_str!("shader/triangle.wgsl"),
921                                    "\n",
922                                    include_str!(
923                                        "shader/triangle/gradient.wgsl"
924                                    ),
925                                    "\n",
926                                    include_str!(
927                                        "shader/color/linear_rgb.wgsl"
928                                    )
929                                )
930                            },
931                        ),
932                    ),
933                });
934
935            let pipeline = device.create_render_pipeline(
936                &wgpu::RenderPipelineDescriptor {
937                    label: Some("iced_wgpu.triangle.gradient.pipeline"),
938                    layout: Some(&layout),
939                    vertex: wgpu::VertexState {
940                        module: &shader,
941                        entry_point: Some("gradient_vs_main"),
942                        buffers: &[wgpu::VertexBufferLayout {
943                            array_stride: std::mem::size_of::<
944                                mesh::GradientVertex2D,
945                            >()
946                                as u64,
947                            step_mode: wgpu::VertexStepMode::Vertex,
948                            attributes: &wgpu::vertex_attr_array!(
949                                // Position
950                                0 => Float32x2,
951                                // Colors 1-2
952                                1 => Uint32x4,
953                                // Colors 3-4
954                                2 => Uint32x4,
955                                // Colors 5-6
956                                3 => Uint32x4,
957                                // Colors 7-8
958                                4 => Uint32x4,
959                                // Offsets
960                                5 => Uint32x4,
961                                // Direction
962                                6 => Float32x4
963                            ),
964                        }],
965                        compilation_options:
966                            wgpu::PipelineCompilationOptions::default(),
967                    },
968                    fragment: Some(wgpu::FragmentState {
969                        module: &shader,
970                        entry_point: Some("gradient_fs_main"),
971                        targets: &[Some(triangle::fragment_target(format))],
972                        compilation_options:
973                            wgpu::PipelineCompilationOptions::default(),
974                    }),
975                    primitive: triangle::primitive_state(),
976                    depth_stencil: None,
977                    multisample: triangle::multisample_state(antialiasing),
978                    multiview: None,
979                    cache: None,
980                },
981            );
982
983            Self {
984                pipeline,
985                constants_layout,
986            }
987        }
988    }
989}