1mod msaa;
3
4use crate::Buffer;
5use crate::core::{Point, Rectangle, Size, Transformation, Vector};
6use crate::graphics::Antialiasing;
7use crate::graphics::mesh::{self, Mesh};
8
9use rustc_hash::FxHashMap;
10use std::collections::hash_map;
11use std::sync::atomic::{self, AtomicU64};
12use std::sync::{self, Arc};
13
14const INITIAL_INDEX_COUNT: usize = 1_000;
15const INITIAL_VERTEX_COUNT: usize = 1_000;
16
17pub type Batch = Vec<Item>;
18
19#[derive(Debug)]
20pub enum Item {
21 Group {
22 transformation: Transformation,
23 meshes: Vec<Mesh>,
24 },
25 Cached {
26 transformation: Transformation,
27 cache: Cache,
28 },
29}
30
31#[derive(Debug, Clone)]
32pub struct Cache {
33 id: Id,
34 batch: Arc<[Mesh]>,
35 version: usize,
36}
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
39pub struct Id(u64);
40
41impl Cache {
42 pub fn new(meshes: Vec<Mesh>) -> Option<Self> {
43 static NEXT_ID: AtomicU64 = AtomicU64::new(0);
44
45 if meshes.is_empty() {
46 return None;
47 }
48
49 Some(Self {
50 id: Id(NEXT_ID.fetch_add(1, atomic::Ordering::Relaxed)),
51 batch: Arc::from(meshes),
52 version: 0,
53 })
54 }
55
56 pub fn update(&mut self, meshes: Vec<Mesh>) {
57 self.batch = Arc::from(meshes);
58 self.version += 1;
59 }
60}
61
62#[derive(Debug)]
63struct Upload {
64 layer: Layer,
65 transformation: Transformation,
66 version: usize,
67 batch: sync::Weak<[Mesh]>,
68}
69
70#[derive(Debug, Default)]
71pub struct Storage {
72 uploads: FxHashMap<Id, Upload>,
73}
74
75impl Storage {
76 pub fn new() -> Self {
77 Self::default()
78 }
79
80 fn get(&self, cache: &Cache) -> Option<&Upload> {
81 if cache.batch.is_empty() {
82 return None;
83 }
84
85 self.uploads.get(&cache.id)
86 }
87
88 fn prepare(
89 &mut self,
90 device: &wgpu::Device,
91 encoder: &mut wgpu::CommandEncoder,
92 belt: &mut wgpu::util::StagingBelt,
93 solid: &solid::Pipeline,
94 gradient: &gradient::Pipeline,
95 cache: &Cache,
96 new_transformation: Transformation,
97 ) {
98 match self.uploads.entry(cache.id) {
99 hash_map::Entry::Occupied(entry) => {
100 let upload = entry.into_mut();
101
102 if !cache.batch.is_empty()
103 && (upload.version != cache.version
104 || upload.transformation != new_transformation)
105 {
106 upload.layer.prepare(
107 device,
108 encoder,
109 belt,
110 solid,
111 gradient,
112 &cache.batch,
113 new_transformation,
114 );
115
116 upload.batch = Arc::downgrade(&cache.batch);
117 upload.version = cache.version;
118 upload.transformation = new_transformation;
119 }
120 }
121 hash_map::Entry::Vacant(entry) => {
122 let mut layer = Layer::new(device, solid, gradient);
123
124 layer.prepare(
125 device,
126 encoder,
127 belt,
128 solid,
129 gradient,
130 &cache.batch,
131 new_transformation,
132 );
133
134 let _ = entry.insert(Upload {
135 layer,
136 transformation: new_transformation,
137 version: 0,
138 batch: Arc::downgrade(&cache.batch),
139 });
140
141 log::debug!(
142 "New mesh upload: {} (total: {})",
143 cache.id.0,
144 self.uploads.len()
145 );
146 }
147 }
148 }
149
150 pub fn trim(&mut self) {
151 self.uploads
152 .retain(|_id, upload| upload.batch.strong_count() > 0);
153 }
154}
155
156#[derive(Debug, Clone)]
157pub struct Pipeline {
158 msaa: Option<msaa::Pipeline>,
159 solid: solid::Pipeline,
160 gradient: gradient::Pipeline,
161}
162
163pub struct State {
164 msaa: Option<msaa::State>,
165 layers: Vec<Layer>,
166 prepare_layer: usize,
167 storage: Storage,
168}
169
170impl State {
171 pub fn new(device: &wgpu::Device, pipeline: &Pipeline) -> Self {
172 Self {
173 msaa: pipeline
174 .msaa
175 .as_ref()
176 .map(|pipeline| msaa::State::new(device, pipeline)),
177 layers: Vec::new(),
178 prepare_layer: 0,
179 storage: Storage::new(),
180 }
181 }
182
183 pub fn prepare(
184 &mut self,
185 pipeline: &Pipeline,
186 device: &wgpu::Device,
187 belt: &mut wgpu::util::StagingBelt,
188 encoder: &mut wgpu::CommandEncoder,
189 items: &[Item],
190 scale: Transformation,
191 target_size: Size<u32>,
192 ) {
193 let projection = if let Some((state, pipeline)) =
194 self.msaa.as_mut().zip(pipeline.msaa.as_ref())
195 {
196 state.prepare(device, encoder, belt, pipeline, target_size) * scale
197 } else {
198 Transformation::orthographic(target_size.width, target_size.height)
199 * scale
200 };
201
202 for item in items {
203 match item {
204 Item::Group {
205 transformation,
206 meshes,
207 } => {
208 if self.layers.len() <= self.prepare_layer {
209 self.layers.push(Layer::new(
210 device,
211 &pipeline.solid,
212 &pipeline.gradient,
213 ));
214 }
215
216 let layer = &mut self.layers[self.prepare_layer];
217 layer.prepare(
218 device,
219 encoder,
220 belt,
221 &pipeline.solid,
222 &pipeline.gradient,
223 meshes,
224 projection * *transformation,
225 );
226
227 self.prepare_layer += 1;
228 }
229 Item::Cached {
230 transformation,
231 cache,
232 } => {
233 self.storage.prepare(
234 device,
235 encoder,
236 belt,
237 &pipeline.solid,
238 &pipeline.gradient,
239 cache,
240 projection * *transformation,
241 );
242 }
243 }
244 }
245 }
246
247 pub fn render(
248 &mut self,
249 pipeline: &Pipeline,
250 encoder: &mut wgpu::CommandEncoder,
251 target: &wgpu::TextureView,
252 start: usize,
253 batch: &Batch,
254 bounds: Rectangle,
255 screen_transformation: Transformation,
256 ) -> usize {
257 let mut layer_count = 0;
258
259 let items = batch.iter().filter_map(|item| match item {
260 Item::Group {
261 transformation,
262 meshes,
263 } => {
264 let layer = &self.layers[start + layer_count];
265 layer_count += 1;
266
267 Some((
268 layer,
269 meshes.as_slice(),
270 screen_transformation * *transformation,
271 ))
272 }
273 Item::Cached {
274 transformation,
275 cache,
276 } => {
277 let upload = self.storage.get(cache)?;
278
279 Some((
280 &upload.layer,
281 &cache.batch,
282 screen_transformation * *transformation,
283 ))
284 }
285 });
286
287 render(
288 encoder,
289 target,
290 self.msaa.as_ref().zip(pipeline.msaa.as_ref()),
291 &pipeline.solid,
292 &pipeline.gradient,
293 bounds,
294 items,
295 );
296
297 layer_count
298 }
299
300 pub fn trim(&mut self) {
301 self.storage.trim();
302
303 self.prepare_layer = 0;
304 }
305}
306
307impl Pipeline {
308 pub fn new(
309 device: &wgpu::Device,
310 format: wgpu::TextureFormat,
311 antialiasing: Option<Antialiasing>,
312 ) -> Pipeline {
313 Pipeline {
314 msaa: antialiasing.map(|a| msaa::Pipeline::new(device, format, a)),
315 solid: solid::Pipeline::new(device, format, antialiasing),
316 gradient: gradient::Pipeline::new(device, format, antialiasing),
317 }
318 }
319}
320
321fn render<'a>(
322 encoder: &mut wgpu::CommandEncoder,
323 target: &wgpu::TextureView,
324 mut msaa: Option<(&msaa::State, &msaa::Pipeline)>,
325 solid: &solid::Pipeline,
326 gradient: &gradient::Pipeline,
327 bounds: Rectangle,
328 group: impl Iterator<Item = (&'a Layer, &'a [Mesh], Transformation)>,
329) {
330 {
331 let mut render_pass = if let Some((_state, pipeline)) = &mut msaa {
332 pipeline.render_pass(encoder)
333 } else {
334 encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
335 label: Some("iced_wgpu.triangle.render_pass"),
336 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
337 view: target,
338 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 clip_bounds = mesh.clip_bounds() * transformation;
449 let snap_distance = clip_bounds
450 .snap()
451 .map(|snapped_bounds| {
452 Point::new(snapped_bounds.x as f32, snapped_bounds.y as f32)
453 - clip_bounds.position()
454 })
455 .unwrap_or(Vector::ZERO);
456
457 let uniforms = Uniforms::new(
458 transformation
459 * mesh.transformation()
460 * Transformation::translate(
461 snap_distance.x,
462 snap_distance.y,
463 ),
464 );
465
466 let indices = mesh.indices();
467
468 index_offset += self.index_buffer.write(
469 device,
470 encoder,
471 belt,
472 index_offset,
473 indices,
474 );
475
476 self.index_strides.push(indices.len() as u32);
477
478 match mesh {
479 Mesh::Solid { buffers, .. } => {
480 solid_vertex_offset += self.solid.vertices.write(
481 device,
482 encoder,
483 belt,
484 solid_vertex_offset,
485 &buffers.vertices,
486 );
487
488 solid_uniform_offset += self.solid.uniforms.write(
489 device,
490 encoder,
491 belt,
492 solid_uniform_offset,
493 &[uniforms],
494 );
495 }
496 Mesh::Gradient { buffers, .. } => {
497 gradient_vertex_offset += self.gradient.vertices.write(
498 device,
499 encoder,
500 belt,
501 gradient_vertex_offset,
502 &buffers.vertices,
503 );
504
505 gradient_uniform_offset += self.gradient.uniforms.write(
506 device,
507 encoder,
508 belt,
509 gradient_uniform_offset,
510 &[uniforms],
511 );
512 }
513 }
514 }
515 }
516
517 fn render<'a>(
518 &'a self,
519 solid: &'a solid::Pipeline,
520 gradient: &'a gradient::Pipeline,
521 meshes: &[Mesh],
522 bounds: Rectangle,
523 transformation: Transformation,
524 render_pass: &mut wgpu::RenderPass<'a>,
525 ) {
526 let mut num_solids = 0;
527 let mut num_gradients = 0;
528 let mut last_is_solid = None;
529
530 for (index, mesh) in meshes.iter().enumerate() {
531 let Some(clip_bounds) = bounds
532 .intersection(&(mesh.clip_bounds() * transformation))
533 .and_then(Rectangle::snap)
534 else {
535 match mesh {
536 Mesh::Solid { .. } => {
537 num_solids += 1;
538 }
539 Mesh::Gradient { .. } => {
540 num_gradients += 1;
541 }
542 }
543 continue;
544 };
545
546 render_pass.set_scissor_rect(
547 clip_bounds.x,
548 clip_bounds.y,
549 clip_bounds.width,
550 clip_bounds.height,
551 );
552
553 match mesh {
554 Mesh::Solid { .. } => {
555 if !last_is_solid.unwrap_or(false) {
556 render_pass.set_pipeline(&solid.pipeline);
557
558 last_is_solid = Some(true);
559 }
560
561 render_pass.set_bind_group(
562 0,
563 &self.solid.constants,
564 &[(num_solids * std::mem::size_of::<Uniforms>())
565 as u32],
566 );
567
568 render_pass.set_vertex_buffer(
569 0,
570 self.solid.vertices.slice_from_index(num_solids),
571 );
572
573 num_solids += 1;
574 }
575 Mesh::Gradient { .. } => {
576 if last_is_solid.unwrap_or(true) {
577 render_pass.set_pipeline(&gradient.pipeline);
578
579 last_is_solid = Some(false);
580 }
581
582 render_pass.set_bind_group(
583 0,
584 &self.gradient.constants,
585 &[(num_gradients * std::mem::size_of::<Uniforms>())
586 as u32],
587 );
588
589 render_pass.set_vertex_buffer(
590 0,
591 self.gradient.vertices.slice_from_index(num_gradients),
592 );
593
594 num_gradients += 1;
595 }
596 };
597
598 render_pass.set_index_buffer(
599 self.index_buffer.slice_from_index(index),
600 wgpu::IndexFormat::Uint32,
601 );
602
603 render_pass.draw_indexed(0..self.index_strides[index], 0, 0..1);
604 }
605 }
606}
607
608fn fragment_target(
609 texture_format: wgpu::TextureFormat,
610) -> wgpu::ColorTargetState {
611 wgpu::ColorTargetState {
612 format: texture_format,
613 blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
614 write_mask: wgpu::ColorWrites::ALL,
615 }
616}
617
618fn primitive_state() -> wgpu::PrimitiveState {
619 wgpu::PrimitiveState {
620 topology: wgpu::PrimitiveTopology::TriangleList,
621 front_face: wgpu::FrontFace::Cw,
622 ..Default::default()
623 }
624}
625
626fn multisample_state(
627 antialiasing: Option<Antialiasing>,
628) -> wgpu::MultisampleState {
629 wgpu::MultisampleState {
630 count: antialiasing.map(Antialiasing::sample_count).unwrap_or(1),
631 mask: !0,
632 alpha_to_coverage_enabled: false,
633 }
634}
635
636#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
637#[repr(C)]
638pub struct Uniforms {
639 transform: [f32; 16],
640 _padding: [f32; 48],
643}
644
645impl Uniforms {
646 pub fn new(transform: Transformation) -> Self {
647 Self {
648 transform: transform.into(),
649 _padding: [0.0; 48],
650 }
651 }
652
653 pub fn entry() -> wgpu::BindGroupLayoutEntry {
654 wgpu::BindGroupLayoutEntry {
655 binding: 0,
656 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
657 ty: wgpu::BindingType::Buffer {
658 ty: wgpu::BufferBindingType::Uniform,
659 has_dynamic_offset: true,
660 min_binding_size: wgpu::BufferSize::new(
661 std::mem::size_of::<Self>() as u64,
662 ),
663 },
664 count: None,
665 }
666 }
667
668 pub fn min_size() -> Option<wgpu::BufferSize> {
669 wgpu::BufferSize::new(std::mem::size_of::<Self>() as u64)
670 }
671}
672
673mod solid {
674 use crate::Buffer;
675 use crate::graphics::Antialiasing;
676 use crate::graphics::mesh;
677 use crate::triangle;
678
679 #[derive(Debug, Clone)]
680 pub struct Pipeline {
681 pub pipeline: wgpu::RenderPipeline,
682 pub constants_layout: wgpu::BindGroupLayout,
683 }
684
685 #[derive(Debug)]
686 pub struct Layer {
687 pub vertices: Buffer<mesh::SolidVertex2D>,
688 pub uniforms: Buffer<triangle::Uniforms>,
689 pub constants: wgpu::BindGroup,
690 }
691
692 impl Layer {
693 pub fn new(
694 device: &wgpu::Device,
695 constants_layout: &wgpu::BindGroupLayout,
696 ) -> Self {
697 let vertices = Buffer::new(
698 device,
699 "iced_wgpu.triangle.solid.vertex_buffer",
700 triangle::INITIAL_VERTEX_COUNT,
701 wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
702 );
703
704 let uniforms = Buffer::new(
705 device,
706 "iced_wgpu.triangle.solid.uniforms",
707 1,
708 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
709 );
710
711 let constants =
712 Self::bind_group(device, &uniforms.raw, constants_layout);
713
714 Self {
715 vertices,
716 uniforms,
717 constants,
718 }
719 }
720
721 pub fn bind_group(
722 device: &wgpu::Device,
723 buffer: &wgpu::Buffer,
724 layout: &wgpu::BindGroupLayout,
725 ) -> wgpu::BindGroup {
726 device.create_bind_group(&wgpu::BindGroupDescriptor {
727 label: Some("iced_wgpu.triangle.solid.bind_group"),
728 layout,
729 entries: &[wgpu::BindGroupEntry {
730 binding: 0,
731 resource: wgpu::BindingResource::Buffer(
732 wgpu::BufferBinding {
733 buffer,
734 offset: 0,
735 size: triangle::Uniforms::min_size(),
736 },
737 ),
738 }],
739 })
740 }
741 }
742
743 impl Pipeline {
744 pub fn new(
745 device: &wgpu::Device,
746 format: wgpu::TextureFormat,
747 antialiasing: Option<Antialiasing>,
748 ) -> Self {
749 let constants_layout = device.create_bind_group_layout(
750 &wgpu::BindGroupLayoutDescriptor {
751 label: Some("iced_wgpu.triangle.solid.bind_group_layout"),
752 entries: &[triangle::Uniforms::entry()],
753 },
754 );
755
756 let layout = device.create_pipeline_layout(
757 &wgpu::PipelineLayoutDescriptor {
758 label: Some("iced_wgpu.triangle.solid.pipeline_layout"),
759 bind_group_layouts: &[&constants_layout],
760 push_constant_ranges: &[],
761 },
762 );
763
764 let shader =
765 device.create_shader_module(wgpu::ShaderModuleDescriptor {
766 label: Some("iced_wgpu.triangle.solid.shader"),
767 source: wgpu::ShaderSource::Wgsl(
768 std::borrow::Cow::Borrowed(concat!(
769 include_str!("shader/triangle.wgsl"),
770 "\n",
771 include_str!("shader/triangle/solid.wgsl"),
772 "\n",
773 include_str!("shader/color.wgsl"),
774 )),
775 ),
776 });
777
778 let pipeline =
779 device.create_render_pipeline(
780 &wgpu::RenderPipelineDescriptor {
781 label: Some("iced_wgpu::triangle::solid pipeline"),
782 layout: Some(&layout),
783 vertex: wgpu::VertexState {
784 module: &shader,
785 entry_point: Some("solid_vs_main"),
786 buffers: &[wgpu::VertexBufferLayout {
787 array_stride: std::mem::size_of::<
788 mesh::SolidVertex2D,
789 >(
790 )
791 as u64,
792 step_mode: wgpu::VertexStepMode::Vertex,
793 attributes: &wgpu::vertex_attr_array!(
794 0 => Float32x2,
796 1 => Float32x4,
798 ),
799 }],
800 compilation_options:
801 wgpu::PipelineCompilationOptions::default(),
802 },
803 fragment: Some(wgpu::FragmentState {
804 module: &shader,
805 entry_point: Some("solid_fs_main"),
806 targets: &[Some(triangle::fragment_target(format))],
807 compilation_options:
808 wgpu::PipelineCompilationOptions::default(),
809 }),
810 primitive: triangle::primitive_state(),
811 depth_stencil: None,
812 multisample: triangle::multisample_state(antialiasing),
813 multiview: None,
814 cache: None,
815 },
816 );
817
818 Self {
819 pipeline,
820 constants_layout,
821 }
822 }
823 }
824}
825
826mod gradient {
827 use crate::Buffer;
828 use crate::graphics::Antialiasing;
829 use crate::graphics::color;
830 use crate::graphics::mesh;
831 use crate::triangle;
832
833 #[derive(Debug, Clone)]
834 pub struct Pipeline {
835 pub pipeline: wgpu::RenderPipeline,
836 pub constants_layout: wgpu::BindGroupLayout,
837 }
838
839 #[derive(Debug)]
840 pub struct Layer {
841 pub vertices: Buffer<mesh::GradientVertex2D>,
842 pub uniforms: Buffer<triangle::Uniforms>,
843 pub constants: wgpu::BindGroup,
844 }
845
846 impl Layer {
847 pub fn new(
848 device: &wgpu::Device,
849 constants_layout: &wgpu::BindGroupLayout,
850 ) -> Self {
851 let vertices = Buffer::new(
852 device,
853 "iced_wgpu.triangle.gradient.vertex_buffer",
854 triangle::INITIAL_VERTEX_COUNT,
855 wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
856 );
857
858 let uniforms = Buffer::new(
859 device,
860 "iced_wgpu.triangle.gradient.uniforms",
861 1,
862 wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
863 );
864
865 let constants =
866 Self::bind_group(device, &uniforms.raw, constants_layout);
867
868 Self {
869 vertices,
870 uniforms,
871 constants,
872 }
873 }
874
875 pub fn bind_group(
876 device: &wgpu::Device,
877 uniform_buffer: &wgpu::Buffer,
878 layout: &wgpu::BindGroupLayout,
879 ) -> wgpu::BindGroup {
880 device.create_bind_group(&wgpu::BindGroupDescriptor {
881 label: Some("iced_wgpu.triangle.gradient.bind_group"),
882 layout,
883 entries: &[wgpu::BindGroupEntry {
884 binding: 0,
885 resource: wgpu::BindingResource::Buffer(
886 wgpu::BufferBinding {
887 buffer: uniform_buffer,
888 offset: 0,
889 size: triangle::Uniforms::min_size(),
890 },
891 ),
892 }],
893 })
894 }
895 }
896
897 impl Pipeline {
898 pub fn new(
899 device: &wgpu::Device,
900 format: wgpu::TextureFormat,
901 antialiasing: Option<Antialiasing>,
902 ) -> Self {
903 let constants_layout = device.create_bind_group_layout(
904 &wgpu::BindGroupLayoutDescriptor {
905 label: Some(
906 "iced_wgpu.triangle.gradient.bind_group_layout",
907 ),
908 entries: &[triangle::Uniforms::entry()],
909 },
910 );
911
912 let layout = device.create_pipeline_layout(
913 &wgpu::PipelineLayoutDescriptor {
914 label: Some("iced_wgpu.triangle.gradient.pipeline_layout"),
915 bind_group_layouts: &[&constants_layout],
916 push_constant_ranges: &[],
917 },
918 );
919
920 let shader =
921 device.create_shader_module(wgpu::ShaderModuleDescriptor {
922 label: Some("iced_wgpu.triangle.gradient.shader"),
923 source: wgpu::ShaderSource::Wgsl(
924 std::borrow::Cow::Borrowed(
925 if color::GAMMA_CORRECTION {
926 concat!(
927 include_str!("shader/triangle.wgsl"),
928 "\n",
929 include_str!(
930 "shader/triangle/gradient.wgsl"
931 ),
932 "\n",
933 include_str!("shader/color.wgsl"),
934 "\n",
935 include_str!("shader/color/oklab.wgsl")
936 )
937 } else {
938 concat!(
939 include_str!("shader/triangle.wgsl"),
940 "\n",
941 include_str!(
942 "shader/triangle/gradient.wgsl"
943 ),
944 "\n",
945 include_str!("shader/color.wgsl"),
946 "\n",
947 include_str!(
948 "shader/color/linear_rgb.wgsl"
949 )
950 )
951 },
952 ),
953 ),
954 });
955
956 let pipeline = device.create_render_pipeline(
957 &wgpu::RenderPipelineDescriptor {
958 label: Some("iced_wgpu.triangle.gradient.pipeline"),
959 layout: Some(&layout),
960 vertex: wgpu::VertexState {
961 module: &shader,
962 entry_point: Some("gradient_vs_main"),
963 buffers: &[wgpu::VertexBufferLayout {
964 array_stride: std::mem::size_of::<
965 mesh::GradientVertex2D,
966 >()
967 as u64,
968 step_mode: wgpu::VertexStepMode::Vertex,
969 attributes: &wgpu::vertex_attr_array!(
970 0 => Float32x2,
972 1 => Uint32x4,
974 2 => Uint32x4,
976 3 => Uint32x4,
978 4 => Uint32x4,
980 5 => Uint32x4,
982 6 => Float32x4
984 ),
985 }],
986 compilation_options:
987 wgpu::PipelineCompilationOptions::default(),
988 },
989 fragment: Some(wgpu::FragmentState {
990 module: &shader,
991 entry_point: Some("gradient_fs_main"),
992 targets: &[Some(triangle::fragment_target(format))],
993 compilation_options:
994 wgpu::PipelineCompilationOptions::default(),
995 }),
996 primitive: triangle::primitive_state(),
997 depth_stencil: None,
998 multisample: triangle::multisample_state(antialiasing),
999 multiview: None,
1000 cache: None,
1001 },
1002 );
1003
1004 Self {
1005 pipeline,
1006 constants_layout,
1007 }
1008 }
1009 }
1010}