1use std::mem::size_of;
14
15use bytemuck::{Pod, Zeroable};
16
17use crate::shaders::{
18 AA_SOLID_WGSL, AA_TEXTURE_WGSL, GRADIENT_WGSL, LAYER_WGSL, LCB_FLATTEN_WGSL, LCB_WGSL,
19 LCD_WGSL, SOLID_WGSL, TEXT_GRAY_WGSL, TEX_DOWNSAMPLE_3X_WGSL, TEX_DOWNSAMPLE_4X_WGSL, TEX_WGSL,
20};
21
22#[repr(C)]
28#[derive(Clone, Copy, Debug, Pod, Zeroable)]
29pub(crate) struct SolidUniforms {
30 pub resolution: [f32; 2],
31 pub _pad: [f32; 2],
32 pub color: [f32; 4],
33}
34const _: () = assert!(size_of::<SolidUniforms>() == 32);
35
36#[repr(C)]
40#[derive(Clone, Copy, Debug, Pod, Zeroable)]
41pub(crate) struct AaTexUniforms {
42 pub resolution: [f32; 2],
43 pub _pad: [f32; 2],
44 pub color: [f32; 4],
45}
46const _: () = assert!(size_of::<AaTexUniforms>() == 32);
47
48#[repr(C)]
54#[derive(Clone, Copy, Debug, Pod, Zeroable)]
55pub(crate) struct TexUniforms {
56 pub resolution: [f32; 2],
57 pub _pad: [f32; 2],
58 pub tint: [f32; 4],
59}
60const _: () = assert!(size_of::<TexUniforms>() == 32);
61
62#[repr(C)]
64#[derive(Clone, Copy, Debug, Pod, Zeroable)]
65pub(crate) struct LayerUniforms {
66 pub resolution: [f32; 2],
67 pub alpha: f32,
68 pub mask_enabled: u32,
69 pub layer_size: [f32; 2],
70 pub mask_radius: f32,
71 pub _pad0: f32,
72 pub mask_rect: [f32; 4],
73}
74const _: () = assert!(size_of::<LayerUniforms>() == 48);
75
76#[repr(C)]
78#[derive(Clone, Copy, Debug, Pod, Zeroable)]
79pub(crate) struct LcdUniforms {
80 pub resolution: [f32; 2],
81 pub channel: u32,
82 pub _pad: u32,
83 pub color: [f32; 4],
84}
85const _: () = assert!(size_of::<LcdUniforms>() == 32);
86
87#[repr(C)]
94#[derive(Clone, Copy, Debug, Pod, Zeroable)]
95pub(crate) struct LcbUniforms {
96 pub resolution: [f32; 2],
97 pub channel: u32,
98 pub global_alpha: f32,
99}
100const _: () = assert!(size_of::<LcbUniforms>() == 16);
101
102const BLEND_STANDARD: wgpu::BlendState = wgpu::BlendState {
118 color: wgpu::BlendComponent {
119 src_factor: wgpu::BlendFactor::SrcAlpha,
120 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
121 operation: wgpu::BlendOperation::Add,
122 },
123 alpha: wgpu::BlendComponent {
124 src_factor: wgpu::BlendFactor::One,
125 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
126 operation: wgpu::BlendOperation::Add,
127 },
128};
129
130const BLEND_PREMUL: wgpu::BlendState = wgpu::BlendState {
132 color: wgpu::BlendComponent {
133 src_factor: wgpu::BlendFactor::One,
134 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
135 operation: wgpu::BlendOperation::Add,
136 },
137 alpha: wgpu::BlendComponent {
138 src_factor: wgpu::BlendFactor::One,
139 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
140 operation: wgpu::BlendOperation::Add,
141 },
142};
143
144pub struct WgpuPipelines {
157 pub solid_pipeline: wgpu::RenderPipeline,
159 pub solid_bgl: wgpu::BindGroupLayout,
160
161 pub aa_solid_pipeline: wgpu::RenderPipeline,
163 pub aa_solid_bgl: wgpu::BindGroupLayout,
164
165 pub aa_texture_pipeline: wgpu::RenderPipeline,
167 pub aa_texture_bgl0: wgpu::BindGroupLayout,
168 pub aa_texture_bgl1: wgpu::BindGroupLayout,
169
170 pub gradient_pipeline: wgpu::RenderPipeline,
172 pub gradient_bgl0: wgpu::BindGroupLayout,
173 pub gradient_bgl1: wgpu::BindGroupLayout,
174
175 pub tex_pipeline: wgpu::RenderPipeline,
177 pub tex_bgl0: wgpu::BindGroupLayout,
178 pub tex_bgl1: wgpu::BindGroupLayout,
179
180 pub tex_downsample_3x_pipeline: wgpu::RenderPipeline,
187
188 pub tex_downsample_4x_pipeline: wgpu::RenderPipeline,
194
195 pub layer_pipeline: wgpu::RenderPipeline,
197 pub layer_mesh_pipeline: wgpu::RenderPipeline,
201 pub layer_bgl0: wgpu::BindGroupLayout,
202 pub layer_bgl1: wgpu::BindGroupLayout,
203
204 pub lcd_r: wgpu::RenderPipeline,
206 pub lcd_g: wgpu::RenderPipeline,
207 pub lcd_b: wgpu::RenderPipeline,
208 pub lcd_bgl0: wgpu::BindGroupLayout,
209 pub lcd_bgl1: wgpu::BindGroupLayout,
210
211 pub lcb_r: wgpu::RenderPipeline,
213 pub lcb_g: wgpu::RenderPipeline,
214 pub lcb_b: wgpu::RenderPipeline,
215 pub lcb_bgl0: wgpu::BindGroupLayout,
216 pub lcb_bgl1: wgpu::BindGroupLayout,
217
218 pub text_gray: wgpu::RenderPipeline,
224 pub lcb_flatten: wgpu::RenderPipeline,
228
229 pub nearest_sampler: wgpu::Sampler,
232 pub linear_sampler: wgpu::Sampler,
234}
235
236impl WgpuPipelines {
237 pub(crate) fn new(
245 device: &wgpu::Device,
246 surface_format: wgpu::TextureFormat,
247 sample_count: u32,
248 ) -> Self {
249 let nearest_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
251 label: Some("nearest"),
252 address_mode_u: wgpu::AddressMode::ClampToEdge,
253 address_mode_v: wgpu::AddressMode::ClampToEdge,
254 address_mode_w: wgpu::AddressMode::ClampToEdge,
255 mag_filter: wgpu::FilterMode::Nearest,
256 min_filter: wgpu::FilterMode::Nearest,
257 mipmap_filter: wgpu::MipmapFilterMode::Nearest,
258 ..Default::default()
259 });
260 let linear_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
261 label: Some("linear"),
262 address_mode_u: wgpu::AddressMode::ClampToEdge,
263 address_mode_v: wgpu::AddressMode::ClampToEdge,
264 address_mode_w: wgpu::AddressMode::ClampToEdge,
265 mag_filter: wgpu::FilterMode::Linear,
266 min_filter: wgpu::FilterMode::Linear,
267 mipmap_filter: wgpu::MipmapFilterMode::Linear,
268 ..Default::default()
269 });
270
271 let solid_sm = mk_shader(device, "solid", SOLID_WGSL);
273 let aa_solid_sm = mk_shader(device, "aa_solid", AA_SOLID_WGSL);
274 let gradient_sm = mk_shader(device, "gradient", GRADIENT_WGSL);
275 let aa_texture_sm = mk_shader(device, "aa_texture", AA_TEXTURE_WGSL);
276 let tex_sm = mk_shader(device, "tex", TEX_WGSL);
277 let layer_sm = mk_shader(device, "layer", LAYER_WGSL);
278 let lcd_sm = mk_shader(device, "lcd", LCD_WGSL);
279 let lcb_sm = mk_shader(device, "lcb", LCB_WGSL);
280
281 let solid_bgl = mk_uniform_bgl(device, "solid", size_of::<SolidUniforms>() as u64);
283 let aa_solid_bgl = mk_uniform_bgl(device, "aa_solid", size_of::<SolidUniforms>() as u64);
284 let gradient_bgl0 = mk_uniform_bgl(
285 device,
286 "gradient0",
287 size_of::<crate::gradient::GradientUniforms>() as u64,
288 );
289 let gradient_bgl1 = mk_tex1_bgl(device, "gradient1");
290 let aa_texture_bgl0 =
291 mk_uniform_bgl(device, "aa_texture0", size_of::<AaTexUniforms>() as u64);
292 let aa_texture_bgl1 = mk_tex1_bgl(device, "aa_texture1");
293 let tex_bgl0 = mk_uniform_bgl(device, "tex0", size_of::<TexUniforms>() as u64);
294 let tex_bgl1 = mk_tex1_bgl(device, "tex1");
295 let layer_bgl0 = mk_uniform_bgl(device, "layer0", size_of::<LayerUniforms>() as u64);
296 let layer_bgl1 = mk_tex1_bgl(device, "layer1");
297 let lcd_bgl0 = mk_uniform_bgl(device, "lcd0", size_of::<LcdUniforms>() as u64);
298 let lcd_bgl1 = mk_tex1_bgl(device, "lcd1");
299 let lcb_bgl0 = mk_uniform_bgl(device, "lcb0", size_of::<LcbUniforms>() as u64);
300 let lcb_bgl1 = mk_tex2_bgl(device, "lcb1");
301
302 let solid_pl = mk_layout(device, "solid", &[&solid_bgl]);
304 let aa_solid_pl = mk_layout(device, "aa_solid", &[&aa_solid_bgl]);
305 let gradient_pl = mk_layout(device, "gradient", &[&gradient_bgl0, &gradient_bgl1]);
306 let aa_texture_pl = mk_layout(device, "aa_texture", &[&aa_texture_bgl0, &aa_texture_bgl1]);
307 let tex_pl = mk_layout(device, "tex", &[&tex_bgl0, &tex_bgl1]);
308 let layer_pl = mk_layout(device, "layer", &[&layer_bgl0, &layer_bgl1]);
309 let lcd_pl = mk_layout(device, "lcd", &[&lcd_bgl0, &lcd_bgl1]);
310 let lcb_pl = mk_layout(device, "lcb", &[&lcb_bgl0, &lcb_bgl1]);
311
312 let pos2_attrs = [wgpu::VertexAttribute {
316 format: wgpu::VertexFormat::Float32x2,
317 offset: 0,
318 shader_location: 0,
319 }];
320 let pos2_alpha_attrs = [
321 wgpu::VertexAttribute {
322 format: wgpu::VertexFormat::Float32x2,
323 offset: 0,
324 shader_location: 0,
325 },
326 wgpu::VertexAttribute {
327 format: wgpu::VertexFormat::Float32,
328 offset: 8,
329 shader_location: 1,
330 },
331 ];
332 let pos2_uv2_attrs = [
333 wgpu::VertexAttribute {
334 format: wgpu::VertexFormat::Float32x2,
335 offset: 0,
336 shader_location: 0,
337 },
338 wgpu::VertexAttribute {
339 format: wgpu::VertexFormat::Float32x2,
340 offset: 8,
341 shader_location: 1,
342 },
343 ];
344
345 let vbl_pos2 = || wgpu::VertexBufferLayout {
349 array_stride: 8,
350 step_mode: wgpu::VertexStepMode::Vertex,
351 attributes: &pos2_attrs,
352 };
353 let vbl_pos2_alpha = || wgpu::VertexBufferLayout {
354 array_stride: 12,
355 step_mode: wgpu::VertexStepMode::Vertex,
356 attributes: &pos2_alpha_attrs,
357 };
358 let vbl_pos2_uv2 = || wgpu::VertexBufferLayout {
359 array_stride: 16,
360 step_mode: wgpu::VertexStepMode::Vertex,
361 attributes: &pos2_uv2_attrs,
362 };
363
364 let solid_pipeline = build_pipeline(
366 device,
367 "solid",
368 &solid_pl,
369 &solid_sm,
370 &solid_sm,
371 &[vbl_pos2()],
372 surface_format,
373 Some(BLEND_STANDARD),
374 wgpu::ColorWrites::ALL,
375 sample_count,
376 );
377 let aa_solid_pipeline = build_pipeline(
378 device,
379 "aa_solid",
380 &aa_solid_pl,
381 &aa_solid_sm,
382 &aa_solid_sm,
383 &[vbl_pos2_alpha()],
384 surface_format,
385 Some(BLEND_STANDARD),
386 wgpu::ColorWrites::ALL,
387 sample_count,
388 );
389 let aa_texture_pipeline = build_pipeline(
394 device,
395 "aa_texture",
396 &aa_texture_pl,
397 &aa_texture_sm,
398 &aa_texture_sm,
399 &[vbl_pos2_uv2()],
400 surface_format,
401 Some(BLEND_PREMUL),
402 wgpu::ColorWrites::ALL,
403 sample_count,
404 );
405 let gradient_pipeline = build_pipeline(
406 device,
407 "gradient",
408 &gradient_pl,
409 &gradient_sm,
410 &gradient_sm,
411 &[vbl_pos2_alpha()],
412 surface_format,
413 Some(BLEND_STANDARD),
414 wgpu::ColorWrites::ALL,
415 sample_count,
416 );
417 let tex_pipeline = build_pipeline(
418 device,
419 "tex",
420 &tex_pl,
421 &tex_sm,
422 &tex_sm,
423 &[vbl_pos2_uv2()],
424 surface_format,
425 Some(BLEND_STANDARD),
426 wgpu::ColorWrites::ALL,
427 sample_count,
428 );
429 let tex_ds3_sm = mk_shader(device, "tex_downsample_3x", TEX_DOWNSAMPLE_3X_WGSL);
430 let tex_downsample_3x_pipeline = build_pipeline(
431 device,
432 "tex_downsample_3x",
433 &tex_pl,
434 &tex_ds3_sm,
435 &tex_ds3_sm,
436 &[vbl_pos2_uv2()],
437 surface_format,
438 Some(BLEND_STANDARD),
439 wgpu::ColorWrites::ALL,
440 sample_count,
441 );
442 let tex_ds4_sm = mk_shader(device, "tex_downsample_4x", TEX_DOWNSAMPLE_4X_WGSL);
443 let tex_downsample_4x_pipeline = build_pipeline(
444 device,
445 "tex_downsample_4x",
446 &tex_pl,
447 &tex_ds4_sm,
448 &tex_ds4_sm,
449 &[vbl_pos2_uv2()],
450 surface_format,
451 Some(BLEND_STANDARD),
452 wgpu::ColorWrites::ALL,
453 sample_count,
454 );
455 let layer_pipeline = build_pipeline(
456 device,
457 "layer",
458 &layer_pl,
459 &layer_sm,
460 &layer_sm,
461 &[vbl_pos2_uv2()],
462 surface_format,
463 Some(BLEND_PREMUL),
464 wgpu::ColorWrites::ALL,
465 sample_count,
466 );
467 let layer_mesh_pipeline = crate::layer_mask::build_layer_mesh_pipeline(
468 device,
469 &layer_bgl0,
470 &layer_bgl1,
471 surface_format,
472 BLEND_PREMUL,
473 sample_count,
474 );
475 let lcd_r = build_pipeline(
476 device,
477 "lcd_r",
478 &lcd_pl,
479 &lcd_sm,
480 &lcd_sm,
481 &[vbl_pos2_uv2()],
482 surface_format,
483 Some(BLEND_STANDARD),
484 wgpu::ColorWrites::RED,
485 sample_count,
486 );
487 let lcd_g = build_pipeline(
488 device,
489 "lcd_g",
490 &lcd_pl,
491 &lcd_sm,
492 &lcd_sm,
493 &[vbl_pos2_uv2()],
494 surface_format,
495 Some(BLEND_STANDARD),
496 wgpu::ColorWrites::GREEN,
497 sample_count,
498 );
499 let lcd_b = build_pipeline(
500 device,
501 "lcd_b",
502 &lcd_pl,
503 &lcd_sm,
504 &lcd_sm,
505 &[vbl_pos2_uv2()],
506 surface_format,
507 Some(BLEND_STANDARD),
508 wgpu::ColorWrites::BLUE,
509 sample_count,
510 );
511 let lcb_r = build_pipeline(
512 device,
513 "lcb_r",
514 &lcb_pl,
515 &lcb_sm,
516 &lcb_sm,
517 &[vbl_pos2_uv2()],
518 surface_format,
519 Some(BLEND_PREMUL),
520 wgpu::ColorWrites::RED,
521 sample_count,
522 );
523 let lcb_g = build_pipeline(
524 device,
525 "lcb_g",
526 &lcb_pl,
527 &lcb_sm,
528 &lcb_sm,
529 &[vbl_pos2_uv2()],
530 surface_format,
531 Some(BLEND_PREMUL),
532 wgpu::ColorWrites::GREEN,
533 sample_count,
534 );
535 let lcb_b = build_pipeline(
536 device,
537 "lcb_b",
538 &lcb_pl,
539 &lcb_sm,
540 &lcb_sm,
541 &[vbl_pos2_uv2()],
542 surface_format,
543 Some(BLEND_PREMUL),
544 wgpu::ColorWrites::BLUE,
545 sample_count,
546 );
547
548 let text_gray_sm = mk_shader(device, "text_gray", TEXT_GRAY_WGSL);
553 let text_gray = build_pipeline(
554 device,
555 "text_gray",
556 &lcd_pl,
557 &text_gray_sm,
558 &text_gray_sm,
559 &[vbl_pos2_uv2()],
560 surface_format,
561 Some(BLEND_STANDARD),
562 wgpu::ColorWrites::ALL,
563 sample_count,
564 );
565 let lcb_flatten_sm = mk_shader(device, "lcb_flatten", LCB_FLATTEN_WGSL);
566 let lcb_flatten = build_pipeline(
567 device,
568 "lcb_flatten",
569 &lcb_pl,
570 &lcb_flatten_sm,
571 &lcb_flatten_sm,
572 &[vbl_pos2_uv2()],
573 surface_format,
574 Some(BLEND_PREMUL),
575 wgpu::ColorWrites::ALL,
576 sample_count,
577 );
578
579 Self {
580 solid_pipeline,
581 solid_bgl,
582 aa_solid_pipeline,
583 aa_solid_bgl,
584 aa_texture_pipeline,
585 aa_texture_bgl0,
586 aa_texture_bgl1,
587 gradient_pipeline,
588 gradient_bgl0,
589 gradient_bgl1,
590 tex_pipeline,
591 tex_bgl0,
592 tex_bgl1,
593 tex_downsample_3x_pipeline,
594 tex_downsample_4x_pipeline,
595 layer_pipeline,
596 layer_mesh_pipeline,
597 layer_bgl0,
598 layer_bgl1,
599 lcd_r,
600 lcd_g,
601 lcd_b,
602 lcd_bgl0,
603 lcd_bgl1,
604 lcb_r,
605 lcb_g,
606 lcb_b,
607 lcb_bgl0,
608 lcb_bgl1,
609 text_gray,
610 lcb_flatten,
611 nearest_sampler,
612 linear_sampler,
613 }
614 }
615}
616
617fn mk_shader(device: &wgpu::Device, label: &str, src: &str) -> wgpu::ShaderModule {
622 device.create_shader_module(wgpu::ShaderModuleDescriptor {
623 label: Some(label),
624 source: wgpu::ShaderSource::Wgsl(src.into()),
625 })
626}
627
628fn mk_uniform_bgl(device: &wgpu::Device, label: &str, size: u64) -> wgpu::BindGroupLayout {
631 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
632 label: Some(label),
633 entries: &[wgpu::BindGroupLayoutEntry {
634 binding: 0,
635 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
636 ty: wgpu::BindingType::Buffer {
637 ty: wgpu::BufferBindingType::Uniform,
638 has_dynamic_offset: false,
639 min_binding_size: wgpu::BufferSize::new(size),
640 },
641 count: None,
642 }],
643 })
644}
645
646fn mk_tex1_bgl(device: &wgpu::Device, label: &str) -> wgpu::BindGroupLayout {
649 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
650 label: Some(label),
651 entries: &[
652 wgpu::BindGroupLayoutEntry {
653 binding: 0,
654 visibility: wgpu::ShaderStages::FRAGMENT,
655 ty: wgpu::BindingType::Texture {
656 sample_type: wgpu::TextureSampleType::Float { filterable: true },
657 view_dimension: wgpu::TextureViewDimension::D2,
658 multisampled: false,
659 },
660 count: None,
661 },
662 wgpu::BindGroupLayoutEntry {
663 binding: 1,
664 visibility: wgpu::ShaderStages::FRAGMENT,
665 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
666 count: None,
667 },
668 ],
669 })
670}
671
672fn mk_tex2_bgl(device: &wgpu::Device, label: &str) -> wgpu::BindGroupLayout {
675 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
676 label: Some(label),
677 entries: &[
678 wgpu::BindGroupLayoutEntry {
679 binding: 0,
680 visibility: wgpu::ShaderStages::FRAGMENT,
681 ty: wgpu::BindingType::Texture {
682 sample_type: wgpu::TextureSampleType::Float { filterable: true },
683 view_dimension: wgpu::TextureViewDimension::D2,
684 multisampled: false,
685 },
686 count: None,
687 },
688 wgpu::BindGroupLayoutEntry {
689 binding: 1,
690 visibility: wgpu::ShaderStages::FRAGMENT,
691 ty: wgpu::BindingType::Texture {
692 sample_type: wgpu::TextureSampleType::Float { filterable: true },
693 view_dimension: wgpu::TextureViewDimension::D2,
694 multisampled: false,
695 },
696 count: None,
697 },
698 wgpu::BindGroupLayoutEntry {
699 binding: 2,
700 visibility: wgpu::ShaderStages::FRAGMENT,
701 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
702 count: None,
703 },
704 ],
705 })
706}
707
708fn mk_layout(
709 device: &wgpu::Device,
710 label: &str,
711 bgls: &[&wgpu::BindGroupLayout],
712) -> wgpu::PipelineLayout {
713 let opt: Vec<Option<&wgpu::BindGroupLayout>> = bgls.iter().copied().map(Some).collect();
714 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
715 label: Some(label),
716 bind_group_layouts: &opt,
717 immediate_size: 0,
718 })
719}
720
721fn build_pipeline(
726 device: &wgpu::Device,
727 label: &str,
728 layout: &wgpu::PipelineLayout,
729 vs: &wgpu::ShaderModule,
730 fs: &wgpu::ShaderModule,
731 vertex_buffers: &[wgpu::VertexBufferLayout<'_>],
732 surface_format: wgpu::TextureFormat,
733 blend: Option<wgpu::BlendState>,
734 write_mask: wgpu::ColorWrites,
735 sample_count: u32,
736) -> wgpu::RenderPipeline {
737 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
738 label: Some(label),
739 layout: Some(layout),
740 vertex: wgpu::VertexState {
741 module: vs,
742 entry_point: Some("vs_main"),
743 compilation_options: Default::default(),
744 buffers: vertex_buffers,
745 },
746 fragment: Some(wgpu::FragmentState {
747 module: fs,
748 entry_point: Some("fs_main"),
749 compilation_options: Default::default(),
750 targets: &[Some(wgpu::ColorTargetState {
751 format: surface_format,
752 blend,
753 write_mask,
754 })],
755 }),
756 primitive: wgpu::PrimitiveState {
757 topology: wgpu::PrimitiveTopology::TriangleList,
758 cull_mode: None,
759 ..Default::default()
760 },
761 depth_stencil: None,
762 multisample: wgpu::MultisampleState {
763 count: sample_count,
764 mask: !0,
765 alpha_to_coverage_enabled: false,
766 },
767 multiview_mask: None,
768 cache: None,
769 })
770}