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