1use super::*;
2use tracing::{info, warn};
3
4fn pick_surface_format(surface_formats: &[wgpu::TextureFormat]) -> wgpu::TextureFormat {
5 const PREFERRED_SURFACE_FORMATS: [wgpu::TextureFormat; 4] = [
6 wgpu::TextureFormat::Bgra8UnormSrgb,
7 wgpu::TextureFormat::Rgba8UnormSrgb,
8 wgpu::TextureFormat::Bgra8Unorm,
9 wgpu::TextureFormat::Rgba8Unorm,
10 ];
11
12 PREFERRED_SURFACE_FORMATS
13 .into_iter()
14 .find(|surface_format| surface_formats.contains(surface_format))
15 .unwrap_or_else(|| {
16 surface_formats
17 .first()
18 .copied()
19 .unwrap_or(wgpu::TextureFormat::Bgra8UnormSrgb)
20 })
21}
22
23fn pick_alpha_mode(alpha_modes: &[CompositeAlphaMode], transparent: bool) -> CompositeAlphaMode {
24 if transparent && alpha_modes.contains(&CompositeAlphaMode::PreMultiplied) {
25 info!("Using PreMultiplied alpha mode for transparency");
26 CompositeAlphaMode::PreMultiplied
27 } else if transparent && alpha_modes.contains(&CompositeAlphaMode::PostMultiplied) {
28 info!("Using PostMultiplied alpha mode for transparency");
29 CompositeAlphaMode::PostMultiplied
30 } else {
31 if transparent {
32 warn!(
33 "Transparency requested but no suitable alpha mode available, falling back to the surface default"
34 );
35 }
36
37 alpha_modes
38 .iter()
39 .copied()
40 .find(|alpha_mode| matches!(alpha_mode, CompositeAlphaMode::Opaque))
41 .unwrap_or_else(|| {
42 alpha_modes
43 .first()
44 .copied()
45 .unwrap_or(CompositeAlphaMode::Opaque)
46 })
47 }
48}
49
50#[derive(Debug, thiserror::Error)]
53pub enum RendererCreationError {
54 #[error("Invalid scale factor: {0} (must be finite and > 0.0)")]
56 InvalidScaleFactor(f64),
57 #[error("No suitable GPU adapter available: {0}")]
59 AdapterNotAvailable(#[from] wgpu::RequestAdapterError),
60 #[error("GPU device creation failed: {0}")]
62 DeviceCreationFailed(#[from] wgpu::RequestDeviceError),
63}
64
65impl<'a> Renderer<'a> {
66 pub async fn new(
67 window: impl Into<SurfaceTarget<'static>>,
68 physical_size: (u32, u32),
69 scale_factor: f64,
70 vsync: bool,
71 transparent: bool,
72 msaa_samples: u32,
73 ) -> Self {
74 let size = physical_size;
75
76 let instance = wgpu::Instance::new(&InstanceDescriptor::default());
77 let surface = instance
78 .create_surface(window)
79 .expect("Failed to create surface");
80
81 let adapter = instance
82 .request_adapter(&wgpu::RequestAdapterOptions {
83 power_preference: wgpu::PowerPreference::HighPerformance,
84 compatible_surface: Some(&surface),
85 force_fallback_adapter: false,
86 })
87 .await
88 .unwrap();
89
90 let (device, queue) = adapter
91 .request_device(&wgpu::DeviceDescriptor {
92 label: None,
93 #[cfg(feature = "performance_measurement")]
94 required_features: wgpu::Features::TIMESTAMP_QUERY
95 | wgpu::Features::DEPTH32FLOAT_STENCIL8,
96 #[cfg(not(feature = "performance_measurement"))]
97 required_features: wgpu::Features::empty(),
98 required_limits: wgpu::Limits::default(),
99 memory_hints: Default::default(),
100 trace: Default::default(),
101 })
102 .await
103 .unwrap();
104
105 let surface_caps = surface.get_capabilities(&adapter);
106 let swapchain_format = pick_surface_format(&surface_caps.formats);
107 let alpha_mode = pick_alpha_mode(&surface_caps.alpha_modes, transparent);
108
109 let config = wgpu::SurfaceConfiguration {
110 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
111 format: swapchain_format,
112 width: size.0,
113 height: size.1,
114 present_mode: if vsync {
115 wgpu::PresentMode::AutoVsync
116 } else {
117 wgpu::PresentMode::AutoNoVsync
118 },
119 desired_maximum_frame_latency: 2,
120 alpha_mode,
121 view_formats: vec![],
122 };
123 surface.configure(&device, &config);
124
125 let msaa_sample_count = Self::validate_sample_count_static(msaa_samples);
126
127 Self::build_from_device(
128 instance,
129 Some(surface),
130 device,
131 queue,
132 config,
133 size,
134 scale_factor,
135 msaa_sample_count,
136 )
137 .expect("Failed to build renderer from device")
138 }
139
140 #[allow(clippy::too_many_arguments)]
143 fn build_from_device(
144 instance: wgpu::Instance,
145 surface: Option<wgpu::Surface<'a>>,
146 device: wgpu::Device,
147 queue: wgpu::Queue,
148 config: wgpu::SurfaceConfiguration,
149 physical_size: (u32, u32),
150 scale_factor: f64,
151 msaa_sample_count: u32,
152 ) -> Result<Self, RendererCreationError> {
153 if !scale_factor.is_finite() || scale_factor <= 0.0 {
154 return Err(RendererCreationError::InvalidScaleFactor(scale_factor));
155 }
156
157 let canvas_logical_size = to_logical(physical_size, scale_factor);
158
159 let (
160 and_uniforms,
161 and_uniform_buffer,
162 and_bind_group,
163 and_texture_bgl_layer0,
164 and_texture_bgl_layer1,
165 and_pipeline,
166 ) = create_pipeline(
167 canvas_logical_size,
168 scale_factor,
169 Self::DEFAULT_FRINGE_WIDTH,
170 &device,
171 &config,
172 PipelineType::EqualIncrementStencil,
173 msaa_sample_count,
174 );
175
176 let (
177 decrementing_uniforms,
178 decrementing_uniform_buffer,
179 decrementing_bind_group,
180 _shape_texture_bind_group_layout_init0,
181 _shape_texture_bind_group_layout_init1,
182 decrementing_pipeline,
183 ) = create_pipeline(
184 canvas_logical_size,
185 scale_factor,
186 Self::DEFAULT_FRINGE_WIDTH,
187 &device,
188 &config,
189 PipelineType::EqualDecrementStencil,
190 msaa_sample_count,
191 );
192
193 let leaf_draw_pipeline = crate::pipeline::create_stencil_keep_color_pipeline(
194 &device,
195 config.format,
196 msaa_sample_count,
197 &and_pipeline.get_bind_group_layout(0),
198 &and_texture_bgl_layer0,
199 &and_texture_bgl_layer1,
200 );
201
202 let device = Arc::new(device);
203 let queue = Arc::new(queue);
204
205 let texture_manager = TextureManager::new(device.clone(), queue.clone());
206
207 let (default_shape_texture_bind_group_layer0, shape_texture_bind_group_layout_layer0) =
208 Self::create_default_shape_texture_bind_group(&device, &queue, &and_texture_bgl_layer0);
209 let (default_shape_texture_bind_group_layer1, shape_texture_bind_group_layout_layer1) =
210 Self::create_default_shape_texture_bind_group(&device, &queue, &and_texture_bgl_layer1);
211
212 let mut renderer = Self {
213 instance,
214 surface,
215 device,
216 queue,
217 config,
218 physical_size,
219 scale_factor,
220 fringe_width: Self::DEFAULT_FRINGE_WIDTH,
221 tessellator: FillTessellator::new(),
222 texture_manager,
223 buffers_pool_manager: PoolManager::new(
224 NonZeroUsize::new(MAX_CACHED_SHAPES).expect("Cache size to be greater than 0"),
225 ),
226 and_pipeline: Arc::new(and_pipeline),
227 and_uniforms,
228 and_uniform_buffer,
229 and_bind_group,
230 shape_texture_bind_group_layout_background: Arc::new(
231 shape_texture_bind_group_layout_layer0,
232 ),
233 shape_texture_bind_group_layout_foreground: Arc::new(
234 shape_texture_bind_group_layout_layer1,
235 ),
236 shape_texture_layout_epoch: 0,
237 default_shape_texture_bind_groups: [
238 Arc::new(default_shape_texture_bind_group_layer0),
239 Arc::new(default_shape_texture_bind_group_layer1),
240 ],
241 decrementing_pipeline: Arc::new(decrementing_pipeline),
242 decrementing_uniforms,
243 decrementing_uniform_buffer,
244 decrementing_bind_group,
245 draw_tree: easy_tree::Tree::new(),
246 metadata_to_clips: HashMap::new(),
247 temp_vertices: Vec::new(),
248 temp_indices: Vec::new(),
249 geometry_dedup_map: HashMap::new(),
250 temp_instance_transforms: Vec::new(),
251 temp_instance_colors: Vec::new(),
252 temp_instance_metadata: Vec::new(),
253 aggregated_vertex_buffer: None,
254 aggregated_index_buffer: None,
255 aggregated_instance_transform_buffer: None,
256 aggregated_instance_color_buffer: None,
257 aggregated_instance_metadata_buffer: None,
258 identity_instance_transform_buffer: None,
259 identity_instance_color_buffer: None,
260 identity_instance_metadata_buffer: None,
261 shape_cache: HashMap::new(),
262 argb_cs_bgl: None,
263 argb_cs_pipeline: None,
264 argb_swizzle_bind_group: None,
265 argb_params_buffer: None,
266 argb_input_buffer: None,
267 argb_output_storage_buffer: None,
268 argb_readback_buffer: None,
269 argb_input_buffer_size: 0,
270 argb_output_buffer_size: 0,
271 argb_cached_width: 0,
272 argb_cached_height: 0,
273 argb_offscreen_texture: None,
274 rtb_offscreen_texture: None,
275 rtb_readback_buffer: None,
276 rtb_cached_width: 0,
277 rtb_cached_height: 0,
278 msaa_sample_count,
279 msaa_color_texture: None,
280 msaa_color_texture_view: None,
281 depth_stencil_texture: None,
282 depth_stencil_view: None,
283 loaded_effects: HashMap::new(),
284 group_effects: HashMap::new(),
285 backdrop_effects: HashMap::new(),
286 offscreen_texture_pool: OffscreenTexturePool::new(),
287 composite_pipeline: None,
288 composite_bgl: None,
289 effect_sampler: None,
290 backdrop_snapshot_texture: None,
291 backdrop_snapshot_view: None,
292 stencil_only_pipeline: None,
293 backdrop_color_pipeline: None,
294 leaf_draw_pipeline: Arc::new(leaf_draw_pipeline),
295 #[cfg(feature = "render_metrics")]
296 render_loop_metrics_tracker: RenderLoopMetricsTracker::default(),
297 #[cfg(feature = "render_metrics")]
298 last_phase_timings: Default::default(),
299 #[cfg(feature = "render_metrics")]
300 last_pipeline_switch_counts: Default::default(),
301 scratch: RendererScratch::new(),
302 };
303
304 renderer.recreate_msaa_texture();
305 renderer.recreate_depth_stencil_texture();
306 Ok(renderer)
307 }
308
309 pub fn print_memory_usage_info(&self) {
310 println!("=== Memory Usage Info ===");
311
312 println!("Cached shapes: {}", self.shape_cache.len());
313 println!("Draw tree size: {}", self.draw_tree.len());
314 println!(
315 "Metadata to clips mappings: {}",
316 self.metadata_to_clips.len()
317 );
318
319 println!("\n--- Temporary Vectors ---");
320 println!(
321 "Temp vertices: {} items, {} capacity, ~{} bytes",
322 self.temp_vertices.len(),
323 self.temp_vertices.capacity(),
324 self.temp_vertices.capacity() * std::mem::size_of::<crate::vertex::CustomVertex>()
325 );
326 println!(
327 "Temp indices: {} items, {} capacity, ~{} bytes",
328 self.temp_indices.len(),
329 self.temp_indices.capacity(),
330 self.temp_indices.capacity() * std::mem::size_of::<u16>()
331 );
332 println!(
333 "Temp instance transforms: {} items, {} capacity, ~{} bytes",
334 self.temp_instance_transforms.len(),
335 self.temp_instance_transforms.capacity(),
336 self.temp_instance_transforms.capacity() * std::mem::size_of::<InstanceTransform>()
337 );
338 println!(
339 "Temp instance colors: {} items, {} capacity, ~{} bytes",
340 self.temp_instance_colors.len(),
341 self.temp_instance_colors.capacity(),
342 self.temp_instance_colors.capacity() * std::mem::size_of::<InstanceColor>()
343 );
344 println!(
345 "Temp instance metadata: {} items, {} capacity, ~{} bytes",
346 self.temp_instance_metadata.len(),
347 self.temp_instance_metadata.capacity(),
348 self.temp_instance_metadata.capacity() * std::mem::size_of::<InstanceMetadata>()
349 );
350
351 println!("\n--- GPU Buffers ---");
352 if let Some(buf) = &self.aggregated_vertex_buffer {
353 println!("Aggregated vertex buffer: {} bytes", buf.size());
354 }
355 if let Some(buf) = &self.aggregated_index_buffer {
356 println!("Aggregated index buffer: {} bytes", buf.size());
357 }
358 if let Some(buf) = &self.aggregated_instance_transform_buffer {
359 println!("Aggregated instance transform buffer: {} bytes", buf.size());
360 }
361 if let Some(buf) = &self.aggregated_instance_color_buffer {
362 println!("Aggregated instance color buffer: {} bytes", buf.size());
363 }
364 if let Some(buf) = &self.aggregated_instance_metadata_buffer {
365 println!("Aggregated instance metadata buffer: {} bytes", buf.size());
366 }
367 if let Some(buf) = &self.identity_instance_transform_buffer {
368 println!("Identity instance transform buffer: {} bytes", buf.size());
369 }
370 if let Some(buf) = &self.identity_instance_color_buffer {
371 println!("Identity instance color buffer: {} bytes", buf.size());
372 }
373 if let Some(buf) = &self.identity_instance_metadata_buffer {
374 println!("Identity instance metadata buffer: {} bytes", buf.size());
375 }
376
377 println!("\n--- ARGB Compute Buffers ---");
378 if let Some(buf) = &self.argb_input_buffer {
379 println!(
380 "ARGB input buffer: {} bytes (cached size: {})",
381 buf.size(),
382 self.argb_input_buffer_size
383 );
384 }
385 if let Some(buf) = &self.argb_output_storage_buffer {
386 println!(
387 "ARGB output storage buffer: {} bytes (cached size: {})",
388 buf.size(),
389 self.argb_output_buffer_size
390 );
391 }
392 if let Some(buf) = &self.argb_readback_buffer {
393 println!("ARGB readback buffer: {} bytes", buf.size());
394 }
395 if let Some(buf) = &self.argb_params_buffer {
396 println!("ARGB params buffer: {} bytes", buf.size());
397 }
398 if let Some(tex) = &self.argb_offscreen_texture {
399 let size = tex.size();
400 println!(
401 "ARGB offscreen texture: {}x{} (cached: {}x{})",
402 size.width, size.height, self.argb_cached_width, self.argb_cached_height
403 );
404 }
405
406 println!("\n--- Render-to-Buffer Caches ---");
407 if let Some(tex) = &self.rtb_offscreen_texture {
408 let size = tex.size();
409 println!(
410 "RTB offscreen texture: {}x{} (cached: {}x{})",
411 size.width, size.height, self.rtb_cached_width, self.rtb_cached_height
412 );
413 }
414 if let Some(buf) = &self.rtb_readback_buffer {
415 println!("RTB readback buffer: {} bytes", buf.size());
416 }
417
418 println!("\n--- Uniform Buffers ---");
419 println!(
420 "AND uniform buffer: {} bytes",
421 self.and_uniform_buffer.size()
422 );
423 println!(
424 "Decrementing uniform buffer: {} bytes",
425 self.decrementing_uniform_buffer.size()
426 );
427
428 println!("\n--- Texture Manager ---");
429 println!("{:?}", self.texture_manager.size());
430
431 println!("\n--- Buffer Pool Manager ---");
432 self.buffers_pool_manager.print_sizes();
433
434 println!("=========================");
435 }
436
437 fn create_default_shape_texture_bind_group(
438 device: &Arc<wgpu::Device>,
439 queue: &Arc<wgpu::Queue>,
440 shape_texture_bind_group_layout: &wgpu::BindGroupLayout,
441 ) -> (wgpu::BindGroup, wgpu::BindGroupLayout) {
442 let texture = device.create_texture(&wgpu::TextureDescriptor {
443 label: Some("default_transparent_texture"),
444 size: wgpu::Extent3d {
445 width: 1,
446 height: 1,
447 depth_or_array_layers: 1,
448 },
449 mip_level_count: 1,
450 sample_count: 1,
451 dimension: wgpu::TextureDimension::D2,
452 format: wgpu::TextureFormat::Rgba8UnormSrgb,
453 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
454 view_formats: &[],
455 });
456 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
457 let transparent: [u8; 4] = [0, 0, 0, 0];
458 queue.write_texture(
459 wgpu::TexelCopyTextureInfo {
460 texture: &texture,
461 mip_level: 0,
462 origin: wgpu::Origin3d::ZERO,
463 aspect: wgpu::TextureAspect::All,
464 },
465 &transparent,
466 wgpu::TexelCopyBufferLayout {
467 offset: 0,
468 bytes_per_row: Some(4),
469 rows_per_image: Some(1),
470 },
471 wgpu::Extent3d {
472 width: 1,
473 height: 1,
474 depth_or_array_layers: 1,
475 },
476 );
477
478 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
479 address_mode_u: wgpu::AddressMode::ClampToEdge,
480 address_mode_v: wgpu::AddressMode::ClampToEdge,
481 address_mode_w: wgpu::AddressMode::ClampToEdge,
482 mag_filter: wgpu::FilterMode::Linear,
483 min_filter: wgpu::FilterMode::Linear,
484 mipmap_filter: wgpu::FilterMode::Linear,
485 ..Default::default()
486 });
487
488 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
489 layout: shape_texture_bind_group_layout,
490 entries: &[
491 wgpu::BindGroupEntry {
492 binding: 0,
493 resource: wgpu::BindingResource::TextureView(&view),
494 },
495 wgpu::BindGroupEntry {
496 binding: 1,
497 resource: wgpu::BindingResource::Sampler(&sampler),
498 },
499 ],
500 label: Some("default_shape_texture_bind_group_transparent"),
501 });
502
503 (bind_group, shape_texture_bind_group_layout.clone())
504 }
505
506 pub async fn new_transparent(
507 window: impl Into<SurfaceTarget<'static>>,
508 physical_size: (u32, u32),
509 scale_factor: f64,
510 vsync: bool,
511 msaa_samples: u32,
512 ) -> Self {
513 Self::new(
514 window,
515 physical_size,
516 scale_factor,
517 vsync,
518 true,
519 msaa_samples,
520 )
521 .await
522 }
523
524 pub async fn try_new_headless(
532 physical_size: (u32, u32),
533 scale_factor: f64,
534 ) -> Result<Self, RendererCreationError> {
535 let size = physical_size;
536
537 let instance = wgpu::Instance::new(&InstanceDescriptor::default());
538
539 let adapter = instance
540 .request_adapter(&wgpu::RequestAdapterOptions {
541 power_preference: wgpu::PowerPreference::HighPerformance,
542 compatible_surface: None,
543 force_fallback_adapter: false,
544 })
545 .await?;
546
547 let (device, queue) = adapter
548 .request_device(&wgpu::DeviceDescriptor {
549 label: None,
550 #[cfg(feature = "performance_measurement")]
551 required_features: wgpu::Features::TIMESTAMP_QUERY
552 | wgpu::Features::DEPTH32FLOAT_STENCIL8,
553 #[cfg(not(feature = "performance_measurement"))]
554 required_features: wgpu::Features::empty(),
555 required_limits: wgpu::Limits::default(),
556 memory_hints: Default::default(),
557 trace: Default::default(),
558 })
559 .await?;
560
561 let swapchain_format = wgpu::TextureFormat::Bgra8UnormSrgb;
562
563 let config = wgpu::SurfaceConfiguration {
564 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC,
565 format: swapchain_format,
566 width: size.0,
567 height: size.1,
568 present_mode: wgpu::PresentMode::AutoVsync,
569 desired_maximum_frame_latency: 2,
570 alpha_mode: CompositeAlphaMode::Opaque,
571 view_formats: vec![],
572 };
573
574 let msaa_sample_count = 1;
575
576 Self::build_from_device(
577 instance,
578 None,
579 device,
580 queue,
581 config,
582 size,
583 scale_factor,
584 msaa_sample_count,
585 )
586 }
587
588 pub async fn new_headless(physical_size: (u32, u32), scale_factor: f64) -> Self {
598 Self::try_new_headless(physical_size, scale_factor)
599 .await
600 .expect("Failed to create headless renderer")
601 }
602
603 pub(super) fn recreate_pipelines(&mut self) {
604 let canvas_logical_size = to_logical(self.physical_size, self.scale_factor);
605
606 let (
607 and_uniforms,
608 and_uniform_buffer,
609 and_bind_group,
610 and_texture_bgl_layer0,
611 and_texture_bgl_layer1,
612 and_pipeline,
613 ) = create_pipeline(
614 canvas_logical_size,
615 self.scale_factor,
616 self.fringe_width,
617 &self.device,
618 &self.config,
619 PipelineType::EqualIncrementStencil,
620 self.msaa_sample_count,
621 );
622
623 let (
624 decrementing_uniforms,
625 decrementing_uniform_buffer,
626 decrementing_bind_group,
627 _,
628 _,
629 decrementing_pipeline,
630 ) = create_pipeline(
631 canvas_logical_size,
632 self.scale_factor,
633 self.fringe_width,
634 &self.device,
635 &self.config,
636 PipelineType::EqualDecrementStencil,
637 self.msaa_sample_count,
638 );
639
640 self.and_pipeline = Arc::new(and_pipeline);
641 self.and_uniforms = and_uniforms;
642 self.and_uniform_buffer = and_uniform_buffer;
643 self.and_bind_group = and_bind_group;
644
645 self.decrementing_pipeline = Arc::new(decrementing_pipeline);
646 self.decrementing_uniforms = decrementing_uniforms;
647 self.decrementing_uniform_buffer = decrementing_uniform_buffer;
648 self.decrementing_bind_group = decrementing_bind_group;
649
650 self.shape_texture_bind_group_layout_background = Arc::new(and_texture_bgl_layer0);
651 self.shape_texture_bind_group_layout_foreground = Arc::new(and_texture_bgl_layer1);
652 self.shape_texture_layout_epoch += 1;
653
654 let (default_shape_texture_bind_group_background, _) =
655 Self::create_default_shape_texture_bind_group(
656 &self.device,
657 &self.queue,
658 &self.shape_texture_bind_group_layout_background,
659 );
660 let (default_shape_texture_bind_group_foreground, _) =
661 Self::create_default_shape_texture_bind_group(
662 &self.device,
663 &self.queue,
664 &self.shape_texture_bind_group_layout_foreground,
665 );
666 self.default_shape_texture_bind_groups = [
667 Arc::new(default_shape_texture_bind_group_background),
668 Arc::new(default_shape_texture_bind_group_foreground),
669 ];
670
671 self.composite_pipeline = None;
672 self.composite_bgl = None;
673
674 self.leaf_draw_pipeline = Arc::new(crate::pipeline::create_stencil_keep_color_pipeline(
675 &self.device,
676 self.config.format,
677 self.msaa_sample_count,
678 &self.and_pipeline.get_bind_group_layout(0),
679 &self.shape_texture_bind_group_layout_background,
680 &self.shape_texture_bind_group_layout_foreground,
681 ));
682 }
683}