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