1use std::{
2 marker::PhantomData,
3 num::{
4 NonZeroU32,
5 NonZeroU64,
6 },
7};
8
9use bitvec::vec::BitVec;
10use ratatui::style::Color;
11use rustybuzz::UnicodeBuffer;
12use web_time::{
13 Duration,
14 Instant,
15};
16use wgpu::{
17 include_wgsl,
18 util::{
19 BufferInitDescriptor,
20 DeviceExt,
21 },
22 vertex_attr_array,
23 AddressMode,
24 Backends,
25 BindGroupDescriptor,
26 BindGroupEntry,
27 BindGroupLayoutDescriptor,
28 BindGroupLayoutEntry,
29 BindingResource,
30 BindingType,
31 BlendState,
32 Buffer,
33 BufferBindingType,
34 BufferDescriptor,
35 BufferUsages,
36 ColorTargetState,
37 ColorWrites,
38 Device,
39 Extent3d,
40 FilterMode,
41 FragmentState,
42 Instance,
43 InstanceDescriptor,
44 InstanceFlags,
45 Limits,
46 MultisampleState,
47 PipelineCompilationOptions,
48 PipelineLayoutDescriptor,
49 PresentMode,
50 PrimitiveState,
51 PrimitiveTopology,
52 RenderPipelineDescriptor,
53 Sampler,
54 SamplerBindingType,
55 SamplerDescriptor,
56 ShaderStages,
57 Surface,
58 SurfaceTarget,
59 TextureDescriptor,
60 TextureDimension,
61 TextureFormat,
62 TextureSampleType,
63 TextureUsages,
64 TextureView,
65 TextureViewDescriptor,
66 TextureViewDimension,
67 VertexBufferLayout,
68 VertexState,
69 VertexStepMode,
70};
71
72use crate::{
73 backend::{
74 build_wgpu_state,
75 c2c,
76 private::Token,
77 wgpu_backend::WgpuBackend,
78 Dimensions,
79 PostProcessor,
80 RenderSurface,
81 TextBgVertexMember,
82 TextCacheBgPipeline,
83 TextCacheFgPipeline,
84 TextVertexMember,
85 Viewport,
86 },
87 colors::{
88 named::{
89 BLACK,
90 WHITE,
91 },
92 Rgb,
93 },
94 fonts::{
95 Font,
96 Fonts,
97 },
98 shaders::DefaultPostProcessor,
99 utils::{
100 plan_cache::PlanCache,
101 text_atlas::Atlas,
102 },
103 Error,
104 Result,
105};
106
107const CACHE_WIDTH: u32 = 1800;
108const CACHE_HEIGHT: u32 = 1200;
109
110pub struct Builder<'a, P: PostProcessor = DefaultPostProcessor> {
116 user_data: P::UserData,
117 fonts: Fonts<'a>,
118 instance: Option<Instance>,
119 limits: Option<Limits>,
120 present_mode: Option<PresentMode>,
121 width: NonZeroU32,
122 height: NonZeroU32,
123 viewport: Viewport,
124 reset_fg: Rgb,
125 reset_bg: Rgb,
126 fast_blink: Duration,
127 slow_blink: Duration,
128}
129
130impl<'a, P: PostProcessor> Builder<'a, P>
131where
132 P::UserData: Default,
133{
134 pub fn from_font(font: Font<'a>) -> Self {
137 Self {
138 user_data: Default::default(),
139 instance: None,
140 fonts: Fonts::new(font, 24),
141 limits: None,
142 present_mode: None,
143 width: NonZeroU32::new(1).unwrap(),
144 height: NonZeroU32::new(1).unwrap(),
145 viewport: Viewport::Full,
146 reset_fg: BLACK,
147 reset_bg: WHITE,
148 fast_blink: Duration::from_millis(200),
149 slow_blink: Duration::from_millis(1000),
150 }
151 }
152}
153
154impl<'a, P: PostProcessor> Builder<'a, P> {
155 pub fn from_font_and_user_data(font: Font<'a>, user_data: P::UserData) -> Self {
158 Self {
159 user_data,
160 instance: None,
161 fonts: Fonts::new(font, 24),
162 limits: None,
163 present_mode: None,
164 width: NonZeroU32::new(1).unwrap(),
165 height: NonZeroU32::new(1).unwrap(),
166 viewport: Viewport::Full,
167 reset_fg: BLACK,
168 reset_bg: WHITE,
169 fast_blink: Duration::from_millis(200),
170 slow_blink: Duration::from_millis(1000),
171 }
172 }
173
174 #[must_use]
176 pub fn with_instance(mut self, instance: Instance) -> Self {
177 self.instance = Some(instance);
178 self
179 }
180
181 #[must_use]
184 pub fn with_viewport(mut self, viewport: Viewport) -> Self {
185 self.viewport = viewport;
186 self
187 }
188
189 #[must_use]
191 pub fn with_font_size_px(mut self, size: u32) -> Self {
192 self.fonts.set_size_px(size);
193 self
194 }
195
196 pub fn with_fonts<I: IntoIterator<Item = Font<'a>>>(mut self, fonts: I) -> Self {
204 self.fonts.add_fonts(fonts);
205 self
206 }
207
208 #[must_use]
213 pub fn with_regular_fonts<I: IntoIterator<Item = Font<'a>>>(mut self, fonts: I) -> Self {
214 self.fonts.add_regular_fonts(fonts);
215 self
216 }
217
218 #[must_use]
223 pub fn with_bold_fonts<I: IntoIterator<Item = Font<'a>>>(mut self, fonts: I) -> Self {
224 self.fonts.add_bold_fonts(fonts);
225 self
226 }
227
228 #[must_use]
233 pub fn with_italic_fonts<I: IntoIterator<Item = Font<'a>>>(mut self, fonts: I) -> Self {
234 self.fonts.add_italic_fonts(fonts);
235 self
236 }
237
238 #[must_use]
243 pub fn with_bold_italic_fonts<I: IntoIterator<Item = Font<'a>>>(mut self, fonts: I) -> Self {
244 self.fonts.add_bold_italic_fonts(fonts);
245 self
246 }
247
248 #[must_use]
251 pub fn with_limits(mut self, limits: Limits) -> Self {
252 self.limits = Some(limits);
253 self
254 }
255
256 #[must_use]
258 pub fn with_present_mode(mut self, mode: PresentMode) -> Self {
259 self.present_mode = Some(mode);
260 self
261 }
262
263 #[must_use]
266 pub fn with_dimensions(mut self, dimensions: Dimensions) -> Self {
267 self.width = dimensions.width;
268 self.height = dimensions.height;
269 self
270 }
271
272 #[must_use]
275 pub fn with_width_and_height(mut self, dimensions: Dimensions) -> Self {
276 self.width = dimensions.width;
277 self.height = dimensions.height;
278 self
279 }
280
281 #[must_use]
284 pub fn with_fg_color(mut self, fg: Color) -> Self {
285 self.reset_fg = c2c(fg, self.reset_fg);
286 self
287 }
288
289 #[must_use]
292 pub fn with_bg_color(mut self, bg: Color) -> Self {
293 self.reset_bg = c2c(bg, self.reset_bg);
294 self
295 }
296
297 #[must_use]
302 pub fn with_rapid_blink_millis(mut self, millis: u64) -> Self {
303 self.fast_blink = Duration::from_millis(millis);
304 self
305 }
306
307 #[must_use]
312 pub fn with_slow_blink_millis(mut self, millis: u64) -> Self {
313 self.slow_blink = Duration::from_millis(millis);
314 self
315 }
316}
317
318impl<'a, P: PostProcessor> Builder<'a, P> {
319 pub async fn build_with_target<'s>(
322 mut self,
323 target: impl Into<SurfaceTarget<'s>>,
324 ) -> Result<WgpuBackend<'a, 's, P>> {
325 let instance = self.instance.get_or_insert_with(|| {
326 wgpu::Instance::new(&InstanceDescriptor {
327 backends: Backends::default(),
328 flags: InstanceFlags::default(),
329 ..Default::default()
330 })
331 });
332 let surface = instance
333 .create_surface(target)
334 .map_err(Error::SurfaceCreationFailed)?;
335
336 self.build_with_surface(surface).await
337 }
338
339 pub async fn build_with_surface<'s>(
344 self,
345 surface: Surface<'s>,
346 ) -> Result<WgpuBackend<'a, 's, P>> {
347 self.build_with_render_surface(surface).await
348 }
349
350 #[cfg(test)]
351 pub(crate) async fn build_headless(
352 self,
353 ) -> Result<WgpuBackend<'a, 'static, P, super::HeadlessSurface>> {
354 self.build_with_render_surface(super::HeadlessSurface::default())
355 .await
356 }
357
358 #[cfg(test)]
359 pub(crate) async fn build_headless_with_format(
360 self,
361 format: TextureFormat,
362 ) -> Result<WgpuBackend<'a, 'static, P, super::HeadlessSurface>> {
363 self.build_with_render_surface(super::HeadlessSurface::new(format))
364 .await
365 }
366
367 async fn build_with_render_surface<'s, S: RenderSurface<'s> + 's>(
368 mut self,
369 mut surface: S,
370 ) -> Result<WgpuBackend<'a, 's, P, S>> {
371 let instance = self.instance.get_or_insert_with(|| {
372 wgpu::Instance::new(&InstanceDescriptor {
373 backends: Backends::default(),
374 flags: InstanceFlags::default(),
375 ..Default::default()
376 })
377 });
378
379 let adapter = instance
380 .request_adapter(&wgpu::RequestAdapterOptions {
381 compatible_surface: surface.wgpu_surface(Token),
382 ..Default::default()
383 })
384 .await
385 .ok_or(Error::AdapterRequestFailed)?;
386
387 let limits = if let Some(limits) = self.limits {
388 min_limits(&adapter, limits)
389 } else {
390 adapter.limits()
391 };
392
393 let (device, queue) = adapter
394 .request_device(
395 &wgpu::DeviceDescriptor {
396 required_limits: limits.clone(),
397 ..Default::default()
398 },
399 None,
400 )
401 .await
402 .map_err(Error::DeviceRequestFailed)?;
403
404 let mut surface_config = surface
405 .get_default_config(
406 &adapter,
407 self.width.get().min(limits.max_texture_dimension_2d),
408 self.height.get().min(limits.max_texture_dimension_2d),
409 Token,
410 )
411 .ok_or(Error::SurfaceConfigurationRequestFailed)?;
412
413 if let Some(mode) = self.present_mode {
414 surface_config.present_mode = mode;
415 }
416
417 surface.configure(&device, &surface_config, Token);
418
419 let (inset_width, inset_height) = match self.viewport {
420 Viewport::Full => (0, 0),
421 Viewport::Shrink { width, height } => (width, height),
422 };
423
424 let drawable_width = surface_config.width - inset_width;
425 let drawable_height = surface_config.height - inset_height;
426
427 info!(
428 "char width x height: {}x{}",
429 self.fonts.min_width_px(),
430 self.fonts.height_px()
431 );
432
433 let text_cache = device.create_texture(&TextureDescriptor {
434 label: Some("Text Atlas"),
435 size: Extent3d {
436 width: CACHE_WIDTH,
437 height: CACHE_HEIGHT,
438 depth_or_array_layers: 1,
439 },
440 mip_level_count: 1,
441 sample_count: 1,
442 dimension: TextureDimension::D2,
443 format: TextureFormat::Rgba8Unorm,
444 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
445 view_formats: &[],
446 });
447
448 let text_cache_view = text_cache.create_view(&TextureViewDescriptor::default());
449
450 let text_mask = device.create_texture(&TextureDescriptor {
451 label: Some("Text Mask"),
452 size: Extent3d {
453 width: CACHE_WIDTH,
454 height: CACHE_HEIGHT,
455 depth_or_array_layers: 1,
456 },
457 mip_level_count: 1,
458 sample_count: 1,
459 dimension: TextureDimension::D2,
460 format: TextureFormat::R8Unorm,
461 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST,
462 view_formats: &[],
463 });
464
465 let text_mask_view = text_mask.create_view(&TextureViewDescriptor::default());
466
467 let sampler = device.create_sampler(&SamplerDescriptor {
468 address_mode_u: AddressMode::ClampToEdge,
469 address_mode_v: AddressMode::ClampToEdge,
470 address_mode_w: AddressMode::ClampToEdge,
471 mag_filter: FilterMode::Nearest,
472 min_filter: FilterMode::Nearest,
473 mipmap_filter: FilterMode::Nearest,
474 ..Default::default()
475 });
476
477 let text_screen_size_buffer = device.create_buffer(&BufferDescriptor {
478 label: Some("Text Uniforms Buffer"),
479 size: size_of::<[f32; 4]>() as u64,
480 mapped_at_creation: false,
481 usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
482 });
483
484 let atlas_size_buffer = device.create_buffer_init(&BufferInitDescriptor {
485 label: Some("Atlas Size buffer"),
486 contents: bytemuck::cast_slice(&[CACHE_WIDTH as f32, CACHE_HEIGHT as f32, 0.0, 0.0]),
487 usage: BufferUsages::UNIFORM,
488 });
489
490 let text_bg_compositor = build_text_bg_compositor(&device, &text_screen_size_buffer);
491
492 let text_fg_compositor = build_text_fg_compositor(
493 &device,
494 &text_screen_size_buffer,
495 &atlas_size_buffer,
496 &text_cache_view,
497 &text_mask_view,
498 &sampler,
499 );
500
501 let wgpu_state = build_wgpu_state(
502 &device,
503 (drawable_width / self.fonts.min_width_px()) * self.fonts.min_width_px(),
504 (drawable_height / self.fonts.height_px()) * self.fonts.height_px(),
505 );
506
507 Ok(WgpuBackend {
508 post_process: P::compile(
509 &device,
510 &wgpu_state.text_dest_view,
511 &surface_config,
512 self.user_data,
513 ),
514 cells: vec![],
515 dirty_rows: vec![],
516 dirty_cells: BitVec::new(),
517 rendered: vec![],
518 sourced: vec![],
519 fast_blinking: BitVec::new(),
520 slow_blinking: BitVec::new(),
521 cursor: (0, 0),
522 surface,
523 _surface: PhantomData,
524 surface_config,
525 device,
526 queue,
527 plan_cache: PlanCache::new(self.fonts.count().max(2)),
528 buffer: UnicodeBuffer::new(),
529 row: String::new(),
530 rowmap: vec![],
531 viewport: self.viewport,
532 cached: Atlas::new(&self.fonts, CACHE_WIDTH, CACHE_HEIGHT),
533 text_cache,
534 text_mask,
535 bg_vertices: vec![],
536 text_indices: vec![],
537 text_vertices: vec![],
538 text_screen_size_buffer,
539 text_bg_compositor,
540 text_fg_compositor,
541 wgpu_state,
542 fonts: self.fonts,
543 reset_fg: self.reset_fg,
544 reset_bg: self.reset_bg,
545 fast_duration: self.fast_blink,
546 last_fast_toggle: Instant::now(),
547 show_fast: true,
548 slow_duration: self.slow_blink,
549 last_slow_toggle: Instant::now(),
550 show_slow: true,
551 })
552 }
553}
554
555fn build_text_bg_compositor(device: &Device, screen_size: &Buffer) -> TextCacheBgPipeline {
556 let shader = device.create_shader_module(include_wgsl!("shaders/composite_bg.wgsl"));
557
558 let vertex_shader_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
559 label: Some("Text Bg Compositor Uniforms Binding Layout"),
560 entries: &[BindGroupLayoutEntry {
561 binding: 0,
562 visibility: ShaderStages::VERTEX,
563 ty: BindingType::Buffer {
564 ty: BufferBindingType::Uniform,
565 has_dynamic_offset: false,
566 min_binding_size: Some(NonZeroU64::new(size_of::<[f32; 4]>() as u64).unwrap()),
567 },
568 count: None,
569 }],
570 });
571
572 let fs_uniforms = device.create_bind_group(&BindGroupDescriptor {
573 label: Some("Text Bg Compositor Uniforms Binding"),
574 layout: &vertex_shader_layout,
575 entries: &[BindGroupEntry {
576 binding: 0,
577 resource: screen_size.as_entire_binding(),
578 }],
579 });
580
581 let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
582 label: Some("Text Bg Compositor Layout"),
583 bind_group_layouts: &[&vertex_shader_layout],
584 push_constant_ranges: &[],
585 });
586
587 let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
588 label: Some("Text Bg Compositor Pipeline"),
589 layout: Some(&pipeline_layout),
590 vertex: VertexState {
591 module: &shader,
592 entry_point: Some("vs_main"),
593 compilation_options: PipelineCompilationOptions::default(),
594 buffers: &[VertexBufferLayout {
595 array_stride: size_of::<TextBgVertexMember>() as u64,
596 step_mode: VertexStepMode::Vertex,
597 attributes: &vertex_attr_array![0 => Float32x2, 1 => Uint32],
598 }],
599 },
600 primitive: PrimitiveState {
601 topology: PrimitiveTopology::TriangleList,
602 ..Default::default()
603 },
604 depth_stencil: None,
605 multisample: MultisampleState::default(),
606 fragment: Some(FragmentState {
607 module: &shader,
608 entry_point: Some("fs_main"),
609 compilation_options: PipelineCompilationOptions::default(),
610 targets: &[Some(ColorTargetState {
611 format: TextureFormat::Rgba8Unorm,
612 blend: None,
613 write_mask: ColorWrites::ALL,
614 })],
615 }),
616 multiview: None,
617 cache: None,
618 });
619
620 TextCacheBgPipeline {
621 pipeline,
622 fs_uniforms,
623 }
624}
625
626fn build_text_fg_compositor(
627 device: &Device,
628 screen_size: &Buffer,
629 atlas_size: &Buffer,
630 cache_view: &TextureView,
631 mask_view: &TextureView,
632 sampler: &Sampler,
633) -> TextCacheFgPipeline {
634 let shader = device.create_shader_module(include_wgsl!("shaders/composite_fg.wgsl"));
635
636 let vertex_shader_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
637 label: Some("Text Compositor Uniforms Binding Layout"),
638 entries: &[BindGroupLayoutEntry {
639 binding: 0,
640 visibility: ShaderStages::VERTEX,
641 ty: BindingType::Buffer {
642 ty: BufferBindingType::Uniform,
643 has_dynamic_offset: false,
644 min_binding_size: Some(NonZeroU64::new(size_of::<[f32; 4]>() as u64).unwrap()),
645 },
646 count: None,
647 }],
648 });
649
650 let fragment_shader_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
651 label: Some("Text Compositor Fragment Binding Layout"),
652 entries: &[
653 BindGroupLayoutEntry {
654 binding: 0,
655 visibility: ShaderStages::FRAGMENT,
656 ty: BindingType::Texture {
657 sample_type: TextureSampleType::Float { filterable: true },
658 view_dimension: TextureViewDimension::D2,
659 multisampled: false,
660 },
661 count: None,
662 },
663 BindGroupLayoutEntry {
664 binding: 1,
665 visibility: ShaderStages::FRAGMENT,
666 ty: BindingType::Texture {
667 sample_type: TextureSampleType::Float { filterable: true },
668 view_dimension: TextureViewDimension::D2,
669 multisampled: false,
670 },
671 count: None,
672 },
673 BindGroupLayoutEntry {
674 binding: 2,
675 visibility: ShaderStages::FRAGMENT,
676 ty: BindingType::Sampler(SamplerBindingType::Filtering),
677 count: None,
678 },
679 BindGroupLayoutEntry {
680 binding: 3,
681 visibility: ShaderStages::FRAGMENT,
682 ty: BindingType::Buffer {
683 ty: BufferBindingType::Uniform,
684 has_dynamic_offset: false,
685 min_binding_size: Some(NonZeroU64::new(size_of::<[f32; 4]>() as u64).unwrap()),
686 },
687 count: None,
688 },
689 ],
690 });
691
692 let fs_uniforms = device.create_bind_group(&BindGroupDescriptor {
693 label: Some("Text Compositor Uniforms Binding"),
694 layout: &vertex_shader_layout,
695 entries: &[BindGroupEntry {
696 binding: 0,
697 resource: screen_size.as_entire_binding(),
698 }],
699 });
700
701 let atlas_bindings = device.create_bind_group(&BindGroupDescriptor {
702 label: Some("Text Compositor Fragment Binding"),
703 layout: &fragment_shader_layout,
704 entries: &[
705 BindGroupEntry {
706 binding: 0,
707 resource: BindingResource::TextureView(cache_view),
708 },
709 BindGroupEntry {
710 binding: 1,
711 resource: BindingResource::TextureView(mask_view),
712 },
713 BindGroupEntry {
714 binding: 2,
715 resource: BindingResource::Sampler(sampler),
716 },
717 BindGroupEntry {
718 binding: 3,
719 resource: atlas_size.as_entire_binding(),
720 },
721 ],
722 });
723
724 let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
725 label: Some("Text Compositor Layout"),
726 bind_group_layouts: &[&vertex_shader_layout, &fragment_shader_layout],
727 push_constant_ranges: &[],
728 });
729
730 let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
731 label: Some("Text Compositor Pipeline"),
732 layout: Some(&pipeline_layout),
733 vertex: VertexState {
734 module: &shader,
735 entry_point: Some("vs_main"),
736 compilation_options: PipelineCompilationOptions::default(),
737 buffers: &[VertexBufferLayout {
738 array_stride: size_of::<TextVertexMember>() as u64,
739 step_mode: VertexStepMode::Vertex,
740 attributes: &vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Uint32, 3 => Uint32, 4 => Uint32],
741 }],
742 },
743 primitive: PrimitiveState {
744 topology: PrimitiveTopology::TriangleList,
745 ..Default::default()
746 },
747 depth_stencil: None,
748 multisample: MultisampleState::default(),
749 fragment: Some(FragmentState {
750 module: &shader,
751 entry_point: Some("fs_main"),
752 compilation_options: PipelineCompilationOptions::default(),
753 targets: &[Some(ColorTargetState {
754 format: TextureFormat::Rgba8Unorm,
755 blend: Some(BlendState::ALPHA_BLENDING),
756 write_mask: ColorWrites::ALL,
757 })],
758 }),
759 multiview: None,
760 cache: None,
761 });
762
763 TextCacheFgPipeline {
764 pipeline,
765 fs_uniforms,
766 atlas_bindings,
767 }
768}
769
770fn min_limits(adapter: &wgpu::Adapter, limits: Limits) -> Limits {
771 let Limits {
772 max_texture_dimension_1d: max_texture_dimension_1d_wl,
773 max_texture_dimension_2d: max_texture_dimension_2d_wl,
774 max_texture_dimension_3d: max_texture_dimension_3d_wl,
775 max_texture_array_layers: max_texture_array_layers_wl,
776 max_bind_groups: max_bind_groups_wl,
777 max_bindings_per_bind_group: max_bindings_per_bind_group_wl,
778 max_dynamic_uniform_buffers_per_pipeline_layout:
779 max_dynamic_uniform_buffers_per_pipeline_layout_wl,
780 max_dynamic_storage_buffers_per_pipeline_layout:
781 max_dynamic_storage_buffers_per_pipeline_layout_wl,
782 max_sampled_textures_per_shader_stage: max_sampled_textures_per_shader_stage_wl,
783 max_samplers_per_shader_stage: max_samplers_per_shader_stage_wl,
784 max_storage_buffers_per_shader_stage: max_storage_buffers_per_shader_stage_wl,
785 max_storage_textures_per_shader_stage: max_storage_textures_per_shader_stage_wl,
786 max_uniform_buffers_per_shader_stage: max_uniform_buffers_per_shader_stage_wl,
787 max_uniform_buffer_binding_size: max_uniform_buffer_binding_size_wl,
788 max_storage_buffer_binding_size: max_storage_buffer_binding_size_wl,
789 max_vertex_buffers: max_vertex_buffers_wl,
790 max_buffer_size: max_buffer_size_wl,
791 max_vertex_attributes: max_vertex_attributes_wl,
792 max_vertex_buffer_array_stride: max_vertex_buffer_array_stride_wl,
793 min_uniform_buffer_offset_alignment: min_uniform_buffer_offset_alignment_wl,
794 min_storage_buffer_offset_alignment: min_storage_buffer_offset_alignment_wl,
795 max_inter_stage_shader_components: max_inter_stage_shader_components_wl,
796 max_color_attachments: max_color_attachments_wl,
797 max_color_attachment_bytes_per_sample: max_color_attachment_bytes_per_sample_wl,
798 max_compute_workgroup_storage_size: max_compute_workgroup_storage_size_wl,
799 max_compute_invocations_per_workgroup: max_compute_invocations_per_workgroup_wl,
800 max_compute_workgroup_size_x: max_compute_workgroup_size_x_wl,
801 max_compute_workgroup_size_y: max_compute_workgroup_size_y_wl,
802 max_compute_workgroup_size_z: max_compute_workgroup_size_z_wl,
803 max_compute_workgroups_per_dimension: max_compute_workgroups_per_dimension_wl,
804 min_subgroup_size: min_subgroup_size_wl,
805 max_subgroup_size: max_subgroup_size_wl,
806 max_push_constant_size: max_push_constant_size_wl,
807 max_non_sampler_bindings: max_non_sampler_bindings_wl,
808 } = limits;
809 let Limits {
810 max_texture_dimension_1d: max_texture_dimension_1d_al,
811 max_texture_dimension_2d: max_texture_dimension_2d_al,
812 max_texture_dimension_3d: max_texture_dimension_3d_al,
813 max_texture_array_layers: max_texture_array_layers_al,
814 max_bind_groups: max_bind_groups_al,
815 max_bindings_per_bind_group: max_bindings_per_bind_group_al,
816 max_dynamic_uniform_buffers_per_pipeline_layout:
817 max_dynamic_uniform_buffers_per_pipeline_layout_al,
818 max_dynamic_storage_buffers_per_pipeline_layout:
819 max_dynamic_storage_buffers_per_pipeline_layout_al,
820 max_sampled_textures_per_shader_stage: max_sampled_textures_per_shader_stage_al,
821 max_samplers_per_shader_stage: max_samplers_per_shader_stage_al,
822 max_storage_buffers_per_shader_stage: max_storage_buffers_per_shader_stage_al,
823 max_storage_textures_per_shader_stage: max_storage_textures_per_shader_stage_al,
824 max_uniform_buffers_per_shader_stage: max_uniform_buffers_per_shader_stage_al,
825 max_uniform_buffer_binding_size: max_uniform_buffer_binding_size_al,
826 max_storage_buffer_binding_size: max_storage_buffer_binding_size_al,
827 max_vertex_buffers: max_vertex_buffers_al,
828 max_buffer_size: max_buffer_size_al,
829 max_vertex_attributes: max_vertex_attributes_al,
830 max_vertex_buffer_array_stride: max_vertex_buffer_array_stride_al,
831 min_uniform_buffer_offset_alignment: min_uniform_buffer_offset_alignment_al,
832 min_storage_buffer_offset_alignment: min_storage_buffer_offset_alignment_al,
833 max_inter_stage_shader_components: max_inter_stage_shader_components_al,
834 max_color_attachments: max_color_attachments_al,
835 max_color_attachment_bytes_per_sample: max_color_attachment_bytes_per_sample_al,
836 max_compute_workgroup_storage_size: max_compute_workgroup_storage_size_al,
837 max_compute_invocations_per_workgroup: max_compute_invocations_per_workgroup_al,
838 max_compute_workgroup_size_x: max_compute_workgroup_size_x_al,
839 max_compute_workgroup_size_y: max_compute_workgroup_size_y_al,
840 max_compute_workgroup_size_z: max_compute_workgroup_size_z_al,
841 max_compute_workgroups_per_dimension: max_compute_workgroups_per_dimension_al,
842 min_subgroup_size: min_subgroup_size_al,
843 max_subgroup_size: max_subgroup_size_al,
844 max_push_constant_size: max_push_constant_size_al,
845 max_non_sampler_bindings: max_non_sampler_bindings_al,
846 } = adapter.limits();
847
848 Limits {
849 max_texture_dimension_1d: if max_texture_dimension_1d_wl <= max_texture_dimension_1d_al {
850 max_texture_dimension_1d_wl
851 } else {
852 max_texture_dimension_1d_al
853 },
854 max_texture_dimension_2d: if max_texture_dimension_2d_wl <= max_texture_dimension_2d_al {
855 max_texture_dimension_2d_wl
856 } else {
857 max_texture_dimension_2d_al
858 },
859 max_texture_dimension_3d: if max_texture_dimension_3d_wl <= max_texture_dimension_3d_al {
860 max_texture_dimension_3d_wl
861 } else {
862 max_texture_dimension_3d_al
863 },
864 max_texture_array_layers: if max_texture_array_layers_wl <= max_texture_array_layers_al {
865 max_texture_array_layers_wl
866 } else {
867 max_texture_array_layers_al
868 },
869 max_bind_groups: if max_bind_groups_wl <= max_bind_groups_al {
870 max_bind_groups_wl
871 } else {
872 max_bind_groups_al
873 },
874 max_bindings_per_bind_group: if max_bindings_per_bind_group_wl
875 <= max_bindings_per_bind_group_al
876 {
877 max_bindings_per_bind_group_wl
878 } else {
879 max_bindings_per_bind_group_al
880 },
881 max_dynamic_uniform_buffers_per_pipeline_layout:
882 if max_dynamic_uniform_buffers_per_pipeline_layout_wl
883 <= max_dynamic_uniform_buffers_per_pipeline_layout_al
884 {
885 max_dynamic_uniform_buffers_per_pipeline_layout_wl
886 } else {
887 max_dynamic_uniform_buffers_per_pipeline_layout_al
888 },
889 max_dynamic_storage_buffers_per_pipeline_layout:
890 if max_dynamic_storage_buffers_per_pipeline_layout_wl
891 <= max_dynamic_storage_buffers_per_pipeline_layout_al
892 {
893 max_dynamic_storage_buffers_per_pipeline_layout_wl
894 } else {
895 max_dynamic_storage_buffers_per_pipeline_layout_al
896 },
897 max_sampled_textures_per_shader_stage: if max_sampled_textures_per_shader_stage_wl
898 <= max_sampled_textures_per_shader_stage_al
899 {
900 max_sampled_textures_per_shader_stage_wl
901 } else {
902 max_sampled_textures_per_shader_stage_al
903 },
904 max_samplers_per_shader_stage: if max_samplers_per_shader_stage_wl
905 <= max_samplers_per_shader_stage_al
906 {
907 max_samplers_per_shader_stage_wl
908 } else {
909 max_samplers_per_shader_stage_al
910 },
911 max_storage_buffers_per_shader_stage: if max_storage_buffers_per_shader_stage_wl
912 <= max_storage_buffers_per_shader_stage_al
913 {
914 max_storage_buffers_per_shader_stage_wl
915 } else {
916 max_storage_buffers_per_shader_stage_al
917 },
918 max_storage_textures_per_shader_stage: if max_storage_textures_per_shader_stage_wl
919 <= max_storage_textures_per_shader_stage_al
920 {
921 max_storage_textures_per_shader_stage_wl
922 } else {
923 max_storage_textures_per_shader_stage_al
924 },
925 max_uniform_buffers_per_shader_stage: if max_uniform_buffers_per_shader_stage_wl
926 <= max_uniform_buffers_per_shader_stage_al
927 {
928 max_uniform_buffers_per_shader_stage_wl
929 } else {
930 max_uniform_buffers_per_shader_stage_al
931 },
932 max_uniform_buffer_binding_size: if max_uniform_buffer_binding_size_wl
933 <= max_uniform_buffer_binding_size_al
934 {
935 max_uniform_buffer_binding_size_wl
936 } else {
937 max_uniform_buffer_binding_size_al
938 },
939 max_storage_buffer_binding_size: if max_storage_buffer_binding_size_wl
940 <= max_storage_buffer_binding_size_al
941 {
942 max_storage_buffer_binding_size_wl
943 } else {
944 max_storage_buffer_binding_size_al
945 },
946 max_vertex_buffers: if max_vertex_buffers_wl <= max_vertex_buffers_al {
947 max_vertex_buffers_wl
948 } else {
949 max_vertex_buffers_al
950 },
951 max_buffer_size: if max_buffer_size_wl <= max_buffer_size_al {
952 max_buffer_size_wl
953 } else {
954 max_buffer_size_al
955 },
956 max_vertex_attributes: if max_vertex_attributes_wl <= max_vertex_attributes_al {
957 max_vertex_attributes_wl
958 } else {
959 max_vertex_attributes_al
960 },
961 max_vertex_buffer_array_stride: if max_vertex_buffer_array_stride_wl
962 <= max_vertex_buffer_array_stride_al
963 {
964 max_vertex_buffer_array_stride_wl
965 } else {
966 max_vertex_buffer_array_stride_al
967 },
968 min_uniform_buffer_offset_alignment: if min_uniform_buffer_offset_alignment_wl
969 <= min_uniform_buffer_offset_alignment_al
970 {
971 min_uniform_buffer_offset_alignment_wl
972 } else {
973 min_uniform_buffer_offset_alignment_al
974 },
975 min_storage_buffer_offset_alignment: if min_storage_buffer_offset_alignment_wl
976 <= min_storage_buffer_offset_alignment_al
977 {
978 min_storage_buffer_offset_alignment_wl
979 } else {
980 min_storage_buffer_offset_alignment_al
981 },
982 max_inter_stage_shader_components: if max_inter_stage_shader_components_wl
983 <= max_inter_stage_shader_components_al
984 {
985 max_inter_stage_shader_components_wl
986 } else {
987 max_inter_stage_shader_components_al
988 },
989 max_color_attachments: if max_color_attachments_wl <= max_color_attachments_al {
990 max_color_attachments_wl
991 } else {
992 max_color_attachments_al
993 },
994 max_color_attachment_bytes_per_sample: if max_color_attachment_bytes_per_sample_wl
995 <= max_color_attachment_bytes_per_sample_al
996 {
997 max_color_attachment_bytes_per_sample_wl
998 } else {
999 max_color_attachment_bytes_per_sample_al
1000 },
1001 max_compute_workgroup_storage_size: if max_compute_workgroup_storage_size_wl
1002 <= max_compute_workgroup_storage_size_al
1003 {
1004 max_compute_workgroup_storage_size_wl
1005 } else {
1006 max_compute_workgroup_storage_size_al
1007 },
1008 max_compute_invocations_per_workgroup: if max_compute_invocations_per_workgroup_wl
1009 <= max_compute_invocations_per_workgroup_al
1010 {
1011 max_compute_invocations_per_workgroup_wl
1012 } else {
1013 max_compute_invocations_per_workgroup_al
1014 },
1015 max_compute_workgroup_size_x: if max_compute_workgroup_size_x_wl
1016 <= max_compute_workgroup_size_x_al
1017 {
1018 max_compute_workgroup_size_x_wl
1019 } else {
1020 max_compute_workgroup_size_x_al
1021 },
1022 max_compute_workgroup_size_y: if max_compute_workgroup_size_y_wl
1023 <= max_compute_workgroup_size_y_al
1024 {
1025 max_compute_workgroup_size_y_wl
1026 } else {
1027 max_compute_workgroup_size_y_al
1028 },
1029 max_compute_workgroup_size_z: if max_compute_workgroup_size_z_wl
1030 <= max_compute_workgroup_size_z_al
1031 {
1032 max_compute_workgroup_size_z_wl
1033 } else {
1034 max_compute_workgroup_size_z_al
1035 },
1036 max_compute_workgroups_per_dimension: if max_compute_workgroups_per_dimension_wl
1037 <= max_compute_workgroups_per_dimension_al
1038 {
1039 max_compute_workgroups_per_dimension_wl
1040 } else {
1041 max_compute_workgroups_per_dimension_al
1042 },
1043 min_subgroup_size: if min_subgroup_size_wl <= min_subgroup_size_al {
1044 min_subgroup_size_wl
1045 } else {
1046 min_subgroup_size_al
1047 },
1048 max_subgroup_size: if max_subgroup_size_wl <= max_subgroup_size_al {
1049 max_subgroup_size_wl
1050 } else {
1051 max_subgroup_size_al
1052 },
1053 max_push_constant_size: if max_push_constant_size_wl <= max_push_constant_size_al {
1054 max_push_constant_size_wl
1055 } else {
1056 max_push_constant_size_al
1057 },
1058 max_non_sampler_bindings: if max_non_sampler_bindings_wl <= max_non_sampler_bindings_al {
1059 max_non_sampler_bindings_wl
1060 } else {
1061 max_non_sampler_bindings_al
1062 },
1063 }
1064}