1mod 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 let count = mesh::attribute_count_of(meshes);
406
407 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 _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 0 => Float32x2,
779 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 0 => Float32x2,
951 1 => Uint32x4,
953 2 => Uint32x4,
955 3 => Uint32x4,
957 4 => Uint32x4,
959 5 => Uint32x4,
961 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}