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_bgl0: wgpu::BindGroupLayout,
198 pub layer_bgl1: wgpu::BindGroupLayout,
199
200 pub lcd_r: wgpu::RenderPipeline,
202 pub lcd_g: wgpu::RenderPipeline,
203 pub lcd_b: wgpu::RenderPipeline,
204 pub lcd_bgl0: wgpu::BindGroupLayout,
205 pub lcd_bgl1: wgpu::BindGroupLayout,
206
207 pub lcb_r: wgpu::RenderPipeline,
209 pub lcb_g: wgpu::RenderPipeline,
210 pub lcb_b: wgpu::RenderPipeline,
211 pub lcb_bgl0: wgpu::BindGroupLayout,
212 pub lcb_bgl1: wgpu::BindGroupLayout,
213
214 pub text_gray: wgpu::RenderPipeline,
220 pub lcb_flatten: wgpu::RenderPipeline,
224
225 pub nearest_sampler: wgpu::Sampler,
228 pub linear_sampler: wgpu::Sampler,
230}
231
232impl WgpuPipelines {
233 pub(crate) fn new(
241 device: &wgpu::Device,
242 surface_format: wgpu::TextureFormat,
243 sample_count: u32,
244 ) -> Self {
245 let nearest_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
247 label: Some("nearest"),
248 address_mode_u: wgpu::AddressMode::ClampToEdge,
249 address_mode_v: wgpu::AddressMode::ClampToEdge,
250 address_mode_w: wgpu::AddressMode::ClampToEdge,
251 mag_filter: wgpu::FilterMode::Nearest,
252 min_filter: wgpu::FilterMode::Nearest,
253 mipmap_filter: wgpu::MipmapFilterMode::Nearest,
254 ..Default::default()
255 });
256 let linear_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
257 label: Some("linear"),
258 address_mode_u: wgpu::AddressMode::ClampToEdge,
259 address_mode_v: wgpu::AddressMode::ClampToEdge,
260 address_mode_w: wgpu::AddressMode::ClampToEdge,
261 mag_filter: wgpu::FilterMode::Linear,
262 min_filter: wgpu::FilterMode::Linear,
263 mipmap_filter: wgpu::MipmapFilterMode::Linear,
264 ..Default::default()
265 });
266
267 let solid_sm = mk_shader(device, "solid", SOLID_WGSL);
269 let aa_solid_sm = mk_shader(device, "aa_solid", AA_SOLID_WGSL);
270 let gradient_sm = mk_shader(device, "gradient", GRADIENT_WGSL);
271 let aa_texture_sm = mk_shader(device, "aa_texture", AA_TEXTURE_WGSL);
272 let tex_sm = mk_shader(device, "tex", TEX_WGSL);
273 let layer_sm = mk_shader(device, "layer", LAYER_WGSL);
274 let lcd_sm = mk_shader(device, "lcd", LCD_WGSL);
275 let lcb_sm = mk_shader(device, "lcb", LCB_WGSL);
276
277 let solid_bgl = mk_uniform_bgl(device, "solid", size_of::<SolidUniforms>() as u64);
279 let aa_solid_bgl = mk_uniform_bgl(device, "aa_solid", size_of::<SolidUniforms>() as u64);
280 let gradient_bgl0 = mk_uniform_bgl(
281 device,
282 "gradient0",
283 size_of::<crate::gradient::GradientUniforms>() as u64,
284 );
285 let gradient_bgl1 = mk_tex1_bgl(device, "gradient1");
286 let aa_texture_bgl0 =
287 mk_uniform_bgl(device, "aa_texture0", size_of::<AaTexUniforms>() as u64);
288 let aa_texture_bgl1 = mk_tex1_bgl(device, "aa_texture1");
289 let tex_bgl0 = mk_uniform_bgl(device, "tex0", size_of::<TexUniforms>() as u64);
290 let tex_bgl1 = mk_tex1_bgl(device, "tex1");
291 let layer_bgl0 = mk_uniform_bgl(device, "layer0", size_of::<LayerUniforms>() as u64);
292 let layer_bgl1 = mk_tex1_bgl(device, "layer1");
293 let lcd_bgl0 = mk_uniform_bgl(device, "lcd0", size_of::<LcdUniforms>() as u64);
294 let lcd_bgl1 = mk_tex1_bgl(device, "lcd1");
295 let lcb_bgl0 = mk_uniform_bgl(device, "lcb0", size_of::<LcbUniforms>() as u64);
296 let lcb_bgl1 = mk_tex2_bgl(device, "lcb1");
297
298 let solid_pl = mk_layout(device, "solid", &[&solid_bgl]);
300 let aa_solid_pl = mk_layout(device, "aa_solid", &[&aa_solid_bgl]);
301 let gradient_pl = mk_layout(device, "gradient", &[&gradient_bgl0, &gradient_bgl1]);
302 let aa_texture_pl = mk_layout(device, "aa_texture", &[&aa_texture_bgl0, &aa_texture_bgl1]);
303 let tex_pl = mk_layout(device, "tex", &[&tex_bgl0, &tex_bgl1]);
304 let layer_pl = mk_layout(device, "layer", &[&layer_bgl0, &layer_bgl1]);
305 let lcd_pl = mk_layout(device, "lcd", &[&lcd_bgl0, &lcd_bgl1]);
306 let lcb_pl = mk_layout(device, "lcb", &[&lcb_bgl0, &lcb_bgl1]);
307
308 let pos2_attrs = [wgpu::VertexAttribute {
312 format: wgpu::VertexFormat::Float32x2,
313 offset: 0,
314 shader_location: 0,
315 }];
316 let pos2_alpha_attrs = [
317 wgpu::VertexAttribute {
318 format: wgpu::VertexFormat::Float32x2,
319 offset: 0,
320 shader_location: 0,
321 },
322 wgpu::VertexAttribute {
323 format: wgpu::VertexFormat::Float32,
324 offset: 8,
325 shader_location: 1,
326 },
327 ];
328 let pos2_uv2_attrs = [
329 wgpu::VertexAttribute {
330 format: wgpu::VertexFormat::Float32x2,
331 offset: 0,
332 shader_location: 0,
333 },
334 wgpu::VertexAttribute {
335 format: wgpu::VertexFormat::Float32x2,
336 offset: 8,
337 shader_location: 1,
338 },
339 ];
340
341 let vbl_pos2 = || wgpu::VertexBufferLayout {
345 array_stride: 8,
346 step_mode: wgpu::VertexStepMode::Vertex,
347 attributes: &pos2_attrs,
348 };
349 let vbl_pos2_alpha = || wgpu::VertexBufferLayout {
350 array_stride: 12,
351 step_mode: wgpu::VertexStepMode::Vertex,
352 attributes: &pos2_alpha_attrs,
353 };
354 let vbl_pos2_uv2 = || wgpu::VertexBufferLayout {
355 array_stride: 16,
356 step_mode: wgpu::VertexStepMode::Vertex,
357 attributes: &pos2_uv2_attrs,
358 };
359
360 let solid_pipeline = build_pipeline(
362 device,
363 "solid",
364 &solid_pl,
365 &solid_sm,
366 &solid_sm,
367 &[vbl_pos2()],
368 surface_format,
369 Some(BLEND_STANDARD),
370 wgpu::ColorWrites::ALL,
371 sample_count,
372 );
373 let aa_solid_pipeline = build_pipeline(
374 device,
375 "aa_solid",
376 &aa_solid_pl,
377 &aa_solid_sm,
378 &aa_solid_sm,
379 &[vbl_pos2_alpha()],
380 surface_format,
381 Some(BLEND_STANDARD),
382 wgpu::ColorWrites::ALL,
383 sample_count,
384 );
385 let aa_texture_pipeline = build_pipeline(
390 device,
391 "aa_texture",
392 &aa_texture_pl,
393 &aa_texture_sm,
394 &aa_texture_sm,
395 &[vbl_pos2_uv2()],
396 surface_format,
397 Some(BLEND_PREMUL),
398 wgpu::ColorWrites::ALL,
399 sample_count,
400 );
401 let gradient_pipeline = build_pipeline(
402 device,
403 "gradient",
404 &gradient_pl,
405 &gradient_sm,
406 &gradient_sm,
407 &[vbl_pos2_alpha()],
408 surface_format,
409 Some(BLEND_STANDARD),
410 wgpu::ColorWrites::ALL,
411 sample_count,
412 );
413 let tex_pipeline = build_pipeline(
414 device,
415 "tex",
416 &tex_pl,
417 &tex_sm,
418 &tex_sm,
419 &[vbl_pos2_uv2()],
420 surface_format,
421 Some(BLEND_STANDARD),
422 wgpu::ColorWrites::ALL,
423 sample_count,
424 );
425 let tex_ds3_sm = mk_shader(device, "tex_downsample_3x", TEX_DOWNSAMPLE_3X_WGSL);
426 let tex_downsample_3x_pipeline = build_pipeline(
427 device,
428 "tex_downsample_3x",
429 &tex_pl,
430 &tex_ds3_sm,
431 &tex_ds3_sm,
432 &[vbl_pos2_uv2()],
433 surface_format,
434 Some(BLEND_STANDARD),
435 wgpu::ColorWrites::ALL,
436 sample_count,
437 );
438 let tex_ds4_sm = mk_shader(device, "tex_downsample_4x", TEX_DOWNSAMPLE_4X_WGSL);
439 let tex_downsample_4x_pipeline = build_pipeline(
440 device,
441 "tex_downsample_4x",
442 &tex_pl,
443 &tex_ds4_sm,
444 &tex_ds4_sm,
445 &[vbl_pos2_uv2()],
446 surface_format,
447 Some(BLEND_STANDARD),
448 wgpu::ColorWrites::ALL,
449 sample_count,
450 );
451 let layer_pipeline = build_pipeline(
452 device,
453 "layer",
454 &layer_pl,
455 &layer_sm,
456 &layer_sm,
457 &[vbl_pos2_uv2()],
458 surface_format,
459 Some(BLEND_PREMUL),
460 wgpu::ColorWrites::ALL,
461 sample_count,
462 );
463 let lcd_r = build_pipeline(
464 device,
465 "lcd_r",
466 &lcd_pl,
467 &lcd_sm,
468 &lcd_sm,
469 &[vbl_pos2_uv2()],
470 surface_format,
471 Some(BLEND_STANDARD),
472 wgpu::ColorWrites::RED,
473 sample_count,
474 );
475 let lcd_g = build_pipeline(
476 device,
477 "lcd_g",
478 &lcd_pl,
479 &lcd_sm,
480 &lcd_sm,
481 &[vbl_pos2_uv2()],
482 surface_format,
483 Some(BLEND_STANDARD),
484 wgpu::ColorWrites::GREEN,
485 sample_count,
486 );
487 let lcd_b = build_pipeline(
488 device,
489 "lcd_b",
490 &lcd_pl,
491 &lcd_sm,
492 &lcd_sm,
493 &[vbl_pos2_uv2()],
494 surface_format,
495 Some(BLEND_STANDARD),
496 wgpu::ColorWrites::BLUE,
497 sample_count,
498 );
499 let lcb_r = build_pipeline(
500 device,
501 "lcb_r",
502 &lcb_pl,
503 &lcb_sm,
504 &lcb_sm,
505 &[vbl_pos2_uv2()],
506 surface_format,
507 Some(BLEND_PREMUL),
508 wgpu::ColorWrites::RED,
509 sample_count,
510 );
511 let lcb_g = build_pipeline(
512 device,
513 "lcb_g",
514 &lcb_pl,
515 &lcb_sm,
516 &lcb_sm,
517 &[vbl_pos2_uv2()],
518 surface_format,
519 Some(BLEND_PREMUL),
520 wgpu::ColorWrites::GREEN,
521 sample_count,
522 );
523 let lcb_b = build_pipeline(
524 device,
525 "lcb_b",
526 &lcb_pl,
527 &lcb_sm,
528 &lcb_sm,
529 &[vbl_pos2_uv2()],
530 surface_format,
531 Some(BLEND_PREMUL),
532 wgpu::ColorWrites::BLUE,
533 sample_count,
534 );
535
536 let text_gray_sm = mk_shader(device, "text_gray", TEXT_GRAY_WGSL);
541 let text_gray = build_pipeline(
542 device,
543 "text_gray",
544 &lcd_pl,
545 &text_gray_sm,
546 &text_gray_sm,
547 &[vbl_pos2_uv2()],
548 surface_format,
549 Some(BLEND_STANDARD),
550 wgpu::ColorWrites::ALL,
551 sample_count,
552 );
553 let lcb_flatten_sm = mk_shader(device, "lcb_flatten", LCB_FLATTEN_WGSL);
554 let lcb_flatten = build_pipeline(
555 device,
556 "lcb_flatten",
557 &lcb_pl,
558 &lcb_flatten_sm,
559 &lcb_flatten_sm,
560 &[vbl_pos2_uv2()],
561 surface_format,
562 Some(BLEND_PREMUL),
563 wgpu::ColorWrites::ALL,
564 sample_count,
565 );
566
567 Self {
568 solid_pipeline,
569 solid_bgl,
570 aa_solid_pipeline,
571 aa_solid_bgl,
572 aa_texture_pipeline,
573 aa_texture_bgl0,
574 aa_texture_bgl1,
575 gradient_pipeline,
576 gradient_bgl0,
577 gradient_bgl1,
578 tex_pipeline,
579 tex_bgl0,
580 tex_bgl1,
581 tex_downsample_3x_pipeline,
582 tex_downsample_4x_pipeline,
583 layer_pipeline,
584 layer_bgl0,
585 layer_bgl1,
586 lcd_r,
587 lcd_g,
588 lcd_b,
589 lcd_bgl0,
590 lcd_bgl1,
591 lcb_r,
592 lcb_g,
593 lcb_b,
594 lcb_bgl0,
595 lcb_bgl1,
596 text_gray,
597 lcb_flatten,
598 nearest_sampler,
599 linear_sampler,
600 }
601 }
602}
603
604fn mk_shader(device: &wgpu::Device, label: &str, src: &str) -> wgpu::ShaderModule {
609 device.create_shader_module(wgpu::ShaderModuleDescriptor {
610 label: Some(label),
611 source: wgpu::ShaderSource::Wgsl(src.into()),
612 })
613}
614
615fn mk_uniform_bgl(device: &wgpu::Device, label: &str, size: u64) -> wgpu::BindGroupLayout {
618 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
619 label: Some(label),
620 entries: &[wgpu::BindGroupLayoutEntry {
621 binding: 0,
622 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
623 ty: wgpu::BindingType::Buffer {
624 ty: wgpu::BufferBindingType::Uniform,
625 has_dynamic_offset: false,
626 min_binding_size: wgpu::BufferSize::new(size),
627 },
628 count: None,
629 }],
630 })
631}
632
633fn mk_tex1_bgl(device: &wgpu::Device, label: &str) -> wgpu::BindGroupLayout {
636 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
637 label: Some(label),
638 entries: &[
639 wgpu::BindGroupLayoutEntry {
640 binding: 0,
641 visibility: wgpu::ShaderStages::FRAGMENT,
642 ty: wgpu::BindingType::Texture {
643 sample_type: wgpu::TextureSampleType::Float { filterable: true },
644 view_dimension: wgpu::TextureViewDimension::D2,
645 multisampled: false,
646 },
647 count: None,
648 },
649 wgpu::BindGroupLayoutEntry {
650 binding: 1,
651 visibility: wgpu::ShaderStages::FRAGMENT,
652 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
653 count: None,
654 },
655 ],
656 })
657}
658
659fn mk_tex2_bgl(device: &wgpu::Device, label: &str) -> wgpu::BindGroupLayout {
662 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
663 label: Some(label),
664 entries: &[
665 wgpu::BindGroupLayoutEntry {
666 binding: 0,
667 visibility: wgpu::ShaderStages::FRAGMENT,
668 ty: wgpu::BindingType::Texture {
669 sample_type: wgpu::TextureSampleType::Float { filterable: true },
670 view_dimension: wgpu::TextureViewDimension::D2,
671 multisampled: false,
672 },
673 count: None,
674 },
675 wgpu::BindGroupLayoutEntry {
676 binding: 1,
677 visibility: wgpu::ShaderStages::FRAGMENT,
678 ty: wgpu::BindingType::Texture {
679 sample_type: wgpu::TextureSampleType::Float { filterable: true },
680 view_dimension: wgpu::TextureViewDimension::D2,
681 multisampled: false,
682 },
683 count: None,
684 },
685 wgpu::BindGroupLayoutEntry {
686 binding: 2,
687 visibility: wgpu::ShaderStages::FRAGMENT,
688 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
689 count: None,
690 },
691 ],
692 })
693}
694
695fn mk_layout(
696 device: &wgpu::Device,
697 label: &str,
698 bgls: &[&wgpu::BindGroupLayout],
699) -> wgpu::PipelineLayout {
700 let opt: Vec<Option<&wgpu::BindGroupLayout>> = bgls.iter().copied().map(Some).collect();
701 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
702 label: Some(label),
703 bind_group_layouts: &opt,
704 immediate_size: 0,
705 })
706}
707
708fn build_pipeline(
713 device: &wgpu::Device,
714 label: &str,
715 layout: &wgpu::PipelineLayout,
716 vs: &wgpu::ShaderModule,
717 fs: &wgpu::ShaderModule,
718 vertex_buffers: &[wgpu::VertexBufferLayout<'_>],
719 surface_format: wgpu::TextureFormat,
720 blend: Option<wgpu::BlendState>,
721 write_mask: wgpu::ColorWrites,
722 sample_count: u32,
723) -> wgpu::RenderPipeline {
724 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
725 label: Some(label),
726 layout: Some(layout),
727 vertex: wgpu::VertexState {
728 module: vs,
729 entry_point: Some("vs_main"),
730 compilation_options: Default::default(),
731 buffers: vertex_buffers,
732 },
733 fragment: Some(wgpu::FragmentState {
734 module: fs,
735 entry_point: Some("fs_main"),
736 compilation_options: Default::default(),
737 targets: &[Some(wgpu::ColorTargetState {
738 format: surface_format,
739 blend,
740 write_mask,
741 })],
742 }),
743 primitive: wgpu::PrimitiveState {
744 topology: wgpu::PrimitiveTopology::TriangleList,
745 cull_mode: None,
746 ..Default::default()
747 },
748 depth_stencil: None,
749 multisample: wgpu::MultisampleState {
750 count: sample_count,
751 mask: !0,
752 alpha_to_coverage_enabled: false,
753 },
754 multiview_mask: None,
755 cache: None,
756 })
757}