1use crate::{CompositorGpuHint, WgpuAtlas, WgpuContext};
2use bytemuck::{Pod, Zeroable};
3use log::warn;
4#[cfg(not(target_family = "wasm"))]
5use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
6use rgpui::{
7 AtlasTextureId, Background, Bounds, DevicePixels, GpuSpecs, MonochromeSprite, Path, Point,
8 PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, SubpixelSprite,
9 Underline, get_gamma_correction_ratios,
10};
11use std::cell::RefCell;
12use std::num::NonZeroU64;
13use std::rc::Rc;
14use std::sync::{Arc, Mutex};
15
16#[repr(C)]
17#[derive(Clone, Copy, Pod, Zeroable)]
18struct GlobalParams {
20 viewport_size: [f32; 2],
21 premultiplied_alpha: u32,
22 pad: u32,
23}
24
25#[repr(C)]
26#[derive(Clone, Copy, Pod, Zeroable)]
27struct PodBounds {
29 origin: [f32; 2],
30 size: [f32; 2],
31}
32
33impl From<Bounds<ScaledPixels>> for PodBounds {
34 fn from(bounds: Bounds<ScaledPixels>) -> Self {
35 Self {
36 origin: [bounds.origin.x.0, bounds.origin.y.0],
37 size: [bounds.size.width.0, bounds.size.height.0],
38 }
39 }
40}
41
42#[repr(C)]
43#[derive(Clone, Copy, Pod, Zeroable)]
44struct SurfaceParams {
46 bounds: PodBounds,
47 content_mask: PodBounds,
48}
49
50#[repr(C)]
51#[derive(Clone, Copy, Pod, Zeroable)]
52struct GammaParams {
54 gamma_ratios: [f32; 4],
55 grayscale_enhanced_contrast: f32,
56 subpixel_enhanced_contrast: f32,
57 is_bgr: u32,
58 _pad: u32,
59}
60
61#[derive(Clone, Debug)]
62#[repr(C)]
63struct PathSprite {
65 bounds: Bounds<ScaledPixels>,
66}
67
68#[derive(Clone, Debug)]
69#[repr(C)]
70struct PathRasterizationVertex {
72 xy_position: Point<ScaledPixels>,
73 st_position: Point<f32>,
74 color: Background,
75 bounds: Bounds<ScaledPixels>,
76}
77
78pub struct WgpuSurfaceConfig {
79 pub size: Size<DevicePixels>,
80 pub transparent: bool,
81 pub preferred_present_mode: Option<wgpu::PresentMode>,
86}
87
88struct WgpuPipelines {
90 quads: wgpu::RenderPipeline,
91 shadows: wgpu::RenderPipeline,
92 path_rasterization: wgpu::RenderPipeline,
93 paths: wgpu::RenderPipeline,
94 underlines: wgpu::RenderPipeline,
95 mono_sprites: wgpu::RenderPipeline,
96 subpixel_sprites: Option<wgpu::RenderPipeline>,
97 poly_sprites: wgpu::RenderPipeline,
98 #[allow(dead_code)]
99 surfaces: wgpu::RenderPipeline,
100}
101
102struct WgpuBindGroupLayouts {
104 globals: wgpu::BindGroupLayout,
105 instances: wgpu::BindGroupLayout,
106 instances_with_texture: wgpu::BindGroupLayout,
107 surfaces: wgpu::BindGroupLayout,
108}
109
110pub type GpuContext = Rc<RefCell<Option<WgpuContext>>>;
112
113struct WgpuResources {
115 device: Arc<wgpu::Device>,
116 queue: Arc<wgpu::Queue>,
117 surface: wgpu::Surface<'static>,
118 pipelines: WgpuPipelines,
119 bind_group_layouts: WgpuBindGroupLayouts,
120 atlas_sampler: wgpu::Sampler,
121 globals_buffer: wgpu::Buffer,
122 globals_bind_group: wgpu::BindGroup,
123 path_globals_bind_group: wgpu::BindGroup,
124 instance_buffer: wgpu::Buffer,
125 path_intermediate_texture: Option<wgpu::Texture>,
126 path_intermediate_view: Option<wgpu::TextureView>,
127 path_msaa_texture: Option<wgpu::Texture>,
128 path_msaa_view: Option<wgpu::TextureView>,
129}
130
131impl WgpuResources {
132 fn invalidate_intermediate_textures(&mut self) {
134 self.path_intermediate_texture = None;
135 self.path_intermediate_view = None;
136 self.path_msaa_texture = None;
137 self.path_msaa_view = None;
138 }
139}
140
141pub struct WgpuRenderer {
142 #[allow(dead_code)]
144 context: Option<GpuContext>,
145 #[allow(dead_code)]
147 compositor_gpu: Option<CompositorGpuHint>,
148 resources: Option<WgpuResources>,
149 surface_config: wgpu::SurfaceConfiguration,
150 atlas: Arc<WgpuAtlas>,
151 path_globals_offset: u64,
152 gamma_offset: u64,
153 instance_buffer_capacity: u64,
154 max_buffer_size: u64,
155 storage_buffer_alignment: u64,
156 rendering_params: RenderingParameters,
157 is_bgr: bool,
158 dual_source_blending: bool,
159 adapter_info: wgpu::AdapterInfo,
160 transparent_alpha_mode: wgpu::CompositeAlphaMode,
161 opaque_alpha_mode: wgpu::CompositeAlphaMode,
162 max_texture_size: u32,
163 last_error: Arc<Mutex<Option<String>>>,
164 failed_frame_count: u32,
165 device_lost: std::sync::Arc<std::sync::atomic::AtomicBool>,
166 surface_configured: bool,
167 needs_redraw: bool,
168}
169
170impl WgpuRenderer {
171 fn resources(&self) -> &WgpuResources {
173 self.resources
174 .as_ref()
175 .expect("GPU resources not available")
176 }
177
178 fn resources_mut(&mut self) -> &mut WgpuResources {
180 self.resources
181 .as_mut()
182 .expect("GPU resources not available")
183 }
184
185 #[cfg(not(target_family = "wasm"))]
193 pub fn new<W>(
194 gpu_context: GpuContext,
195 window: &W,
196 config: WgpuSurfaceConfig,
197 compositor_gpu: Option<CompositorGpuHint>,
198 ) -> anyhow::Result<Self>
199 where
200 W: HasWindowHandle + HasDisplayHandle + std::fmt::Debug + Send + Sync + Clone + 'static,
201 {
202 let window_handle = window
203 .window_handle()
204 .map_err(|e| anyhow::anyhow!("Failed to get window handle: {e}"))?;
205
206 let target = wgpu::SurfaceTargetUnsafe::RawHandle {
207 raw_display_handle: None,
209 raw_window_handle: window_handle.as_raw(),
210 };
211
212 let instance = gpu_context
215 .borrow()
216 .as_ref()
217 .map(|ctx| ctx.instance.clone())
218 .unwrap_or_else(|| WgpuContext::instance(Box::new(window.clone())));
219
220 let surface = unsafe {
224 instance
225 .create_surface_unsafe(target)
226 .map_err(|e| anyhow::anyhow!("Failed to create surface: {e}"))?
227 };
228
229 let mut ctx_ref = gpu_context.borrow_mut();
230 let context = match ctx_ref.as_mut() {
231 Some(context) => {
232 context.check_compatible_with_surface(&surface)?;
233 context
234 }
235 None => ctx_ref.insert(WgpuContext::new(instance, &surface, compositor_gpu)?),
236 };
237
238 let atlas = Arc::new(WgpuAtlas::from_context(context));
239
240 Self::new_internal(
241 Some(Rc::clone(&gpu_context)),
242 context,
243 surface,
244 config,
245 compositor_gpu,
246 atlas,
247 )
248 }
249
250 #[cfg(target_family = "wasm")]
251 pub fn new_from_canvas(
252 context: &WgpuContext,
253 canvas: &web_sys::HtmlCanvasElement,
254 config: WgpuSurfaceConfig,
255 ) -> anyhow::Result<Self> {
256 let surface = context
257 .instance
258 .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
259 .map_err(|e| anyhow::anyhow!("Failed to create surface: {e}"))?;
260
261 let atlas = Arc::new(WgpuAtlas::from_context(context));
262
263 Self::new_internal(None, context, surface, config, None, atlas)
264 }
265
266 fn new_internal(
267 gpu_context: Option<GpuContext>,
268 context: &WgpuContext,
269 surface: wgpu::Surface<'static>,
270 config: WgpuSurfaceConfig,
271 compositor_gpu: Option<CompositorGpuHint>,
272 atlas: Arc<WgpuAtlas>,
273 ) -> anyhow::Result<Self> {
274 let surface_caps = surface.get_capabilities(&context.adapter);
275 let preferred_formats = [
276 wgpu::TextureFormat::Bgra8Unorm,
277 wgpu::TextureFormat::Rgba8Unorm,
278 ];
279 let surface_format = preferred_formats
280 .iter()
281 .find(|f| surface_caps.formats.contains(f))
282 .copied()
283 .or_else(|| surface_caps.formats.iter().find(|f| !f.is_srgb()).copied())
284 .or_else(|| surface_caps.formats.first().copied())
285 .ok_or_else(|| {
286 anyhow::anyhow!(
287 "Surface reports no supported texture formats for adapter {:?}",
288 context.adapter.get_info().name
289 )
290 })?;
291
292 let pick_alpha_mode =
293 |preferences: &[wgpu::CompositeAlphaMode]| -> anyhow::Result<wgpu::CompositeAlphaMode> {
294 preferences
295 .iter()
296 .find(|p| surface_caps.alpha_modes.contains(p))
297 .copied()
298 .or_else(|| surface_caps.alpha_modes.first().copied())
299 .ok_or_else(|| {
300 anyhow::anyhow!(
301 "Surface reports no supported alpha modes for adapter {:?}",
302 context.adapter.get_info().name
303 )
304 })
305 };
306
307 let transparent_alpha_mode = pick_alpha_mode(&[
308 wgpu::CompositeAlphaMode::PreMultiplied,
309 wgpu::CompositeAlphaMode::Inherit,
310 ])?;
311
312 let opaque_alpha_mode = pick_alpha_mode(&[
313 wgpu::CompositeAlphaMode::Opaque,
314 wgpu::CompositeAlphaMode::Inherit,
315 ])?;
316
317 let alpha_mode = if config.transparent {
318 transparent_alpha_mode
319 } else {
320 opaque_alpha_mode
321 };
322
323 let device = Arc::clone(&context.device);
324 let max_texture_size = device.limits().max_texture_dimension_2d;
325
326 let requested_width = config.size.width.0 as u32;
327 let requested_height = config.size.height.0 as u32;
328 let clamped_width = requested_width.min(max_texture_size);
329 let clamped_height = requested_height.min(max_texture_size);
330
331 if clamped_width != requested_width || clamped_height != requested_height {
332 warn!(
333 "Requested surface size ({}, {}) exceeds maximum texture dimension {}. \
334 Clamping to ({}, {}). Window content may not fill the entire window.",
335 requested_width, requested_height, max_texture_size, clamped_width, clamped_height
336 );
337 }
338
339 let surface_config = wgpu::SurfaceConfiguration {
340 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
341 format: surface_format,
342 width: clamped_width.max(1),
343 height: clamped_height.max(1),
344 present_mode: config
345 .preferred_present_mode
346 .filter(|mode| surface_caps.present_modes.contains(mode))
347 .unwrap_or(wgpu::PresentMode::Fifo),
348 desired_maximum_frame_latency: 2,
349 alpha_mode,
350 view_formats: vec![],
351 };
352 surface.configure(&context.device, &surface_config);
355
356 let queue = Arc::clone(&context.queue);
357 let dual_source_blending = context.supports_dual_source_blending();
358
359 let rendering_params = RenderingParameters::new(&context.adapter, surface_format);
360 let bind_group_layouts = Self::create_bind_group_layouts(&device);
361 let pipelines = Self::create_pipelines(
362 &device,
363 &bind_group_layouts,
364 surface_format,
365 alpha_mode,
366 rendering_params.path_sample_count,
367 dual_source_blending,
368 );
369
370 let atlas_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
371 label: Some("atlas_sampler"),
372 mag_filter: wgpu::FilterMode::Linear,
373 min_filter: wgpu::FilterMode::Linear,
374 ..Default::default()
375 });
376
377 let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment as u64;
378 let globals_size = std::mem::size_of::<GlobalParams>() as u64;
379 let gamma_size = std::mem::size_of::<GammaParams>() as u64;
380 let path_globals_offset = globals_size.next_multiple_of(uniform_alignment);
381 let gamma_offset = (path_globals_offset + globals_size).next_multiple_of(uniform_alignment);
382
383 let globals_buffer = device.create_buffer(&wgpu::BufferDescriptor {
384 label: Some("globals_buffer"),
385 size: gamma_offset + gamma_size,
386 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
387 mapped_at_creation: false,
388 });
389
390 let max_buffer_size = device.limits().max_buffer_size;
391 let storage_buffer_alignment = device.limits().min_storage_buffer_offset_alignment as u64;
392 let initial_instance_buffer_capacity = 2 * 1024 * 1024;
393 let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor {
394 label: Some("instance_buffer"),
395 size: initial_instance_buffer_capacity,
396 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
397 mapped_at_creation: false,
398 });
399
400 let globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
401 label: Some("globals_bind_group"),
402 layout: &bind_group_layouts.globals,
403 entries: &[
404 wgpu::BindGroupEntry {
405 binding: 0,
406 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
407 buffer: &globals_buffer,
408 offset: 0,
409 size: Some(NonZeroU64::new(globals_size).unwrap()),
410 }),
411 },
412 wgpu::BindGroupEntry {
413 binding: 1,
414 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
415 buffer: &globals_buffer,
416 offset: gamma_offset,
417 size: Some(NonZeroU64::new(gamma_size).unwrap()),
418 }),
419 },
420 ],
421 });
422
423 let path_globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
424 label: Some("path_globals_bind_group"),
425 layout: &bind_group_layouts.globals,
426 entries: &[
427 wgpu::BindGroupEntry {
428 binding: 0,
429 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
430 buffer: &globals_buffer,
431 offset: path_globals_offset,
432 size: Some(NonZeroU64::new(globals_size).unwrap()),
433 }),
434 },
435 wgpu::BindGroupEntry {
436 binding: 1,
437 resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
438 buffer: &globals_buffer,
439 offset: gamma_offset,
440 size: Some(NonZeroU64::new(gamma_size).unwrap()),
441 }),
442 },
443 ],
444 });
445
446 let adapter_info = context.adapter.get_info();
447
448 let last_error: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
449 let last_error_clone = Arc::clone(&last_error);
450 device.on_uncaptured_error(Arc::new(move |error| {
451 let mut guard = last_error_clone.lock().unwrap();
452 *guard = Some(error.to_string());
453 }));
454
455 let resources = WgpuResources {
456 device,
457 queue,
458 surface,
459 pipelines,
460 bind_group_layouts,
461 atlas_sampler,
462 globals_buffer,
463 globals_bind_group,
464 path_globals_bind_group,
465 instance_buffer,
466 path_intermediate_texture: None,
469 path_intermediate_view: None,
470 path_msaa_texture: None,
471 path_msaa_view: None,
472 };
473
474 Ok(Self {
475 context: gpu_context,
476 compositor_gpu,
477 resources: Some(resources),
478 surface_config,
479 atlas,
480 path_globals_offset,
481 gamma_offset,
482 instance_buffer_capacity: initial_instance_buffer_capacity,
483 max_buffer_size,
484 storage_buffer_alignment,
485 rendering_params,
486 is_bgr: false,
487 dual_source_blending,
488 adapter_info,
489 transparent_alpha_mode,
490 opaque_alpha_mode,
491 max_texture_size,
492 last_error,
493 failed_frame_count: 0,
494 device_lost: context.device_lost_flag(),
495 surface_configured: true,
496 needs_redraw: false,
497 })
498 }
499
500 fn create_bind_group_layouts(device: &wgpu::Device) -> WgpuBindGroupLayouts {
502 let globals =
503 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
504 label: Some("globals_layout"),
505 entries: &[
506 wgpu::BindGroupLayoutEntry {
507 binding: 0,
508 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
509 ty: wgpu::BindingType::Buffer {
510 ty: wgpu::BufferBindingType::Uniform,
511 has_dynamic_offset: false,
512 min_binding_size: NonZeroU64::new(
513 std::mem::size_of::<GlobalParams>() as u64
514 ),
515 },
516 count: None,
517 },
518 wgpu::BindGroupLayoutEntry {
519 binding: 1,
520 visibility: wgpu::ShaderStages::FRAGMENT,
521 ty: wgpu::BindingType::Buffer {
522 ty: wgpu::BufferBindingType::Uniform,
523 has_dynamic_offset: false,
524 min_binding_size: NonZeroU64::new(
525 std::mem::size_of::<GammaParams>() as u64
526 ),
527 },
528 count: None,
529 },
530 ],
531 });
532
533 let storage_buffer_entry = |binding: u32| wgpu::BindGroupLayoutEntry {
534 binding,
535 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
536 ty: wgpu::BindingType::Buffer {
537 ty: wgpu::BufferBindingType::Storage { read_only: true },
538 has_dynamic_offset: false,
539 min_binding_size: None,
540 },
541 count: None,
542 };
543
544 let instances = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
545 label: Some("instances_layout"),
546 entries: &[storage_buffer_entry(0)],
547 });
548
549 let instances_with_texture =
550 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
551 label: Some("instances_with_texture_layout"),
552 entries: &[
553 storage_buffer_entry(0),
554 wgpu::BindGroupLayoutEntry {
555 binding: 1,
556 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
557 ty: wgpu::BindingType::Texture {
558 sample_type: wgpu::TextureSampleType::Float { filterable: true },
559 view_dimension: wgpu::TextureViewDimension::D2,
560 multisampled: false,
561 },
562 count: None,
563 },
564 wgpu::BindGroupLayoutEntry {
565 binding: 2,
566 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
567 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
568 count: None,
569 },
570 ],
571 });
572
573 let surfaces = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
574 label: Some("surfaces_layout"),
575 entries: &[
576 wgpu::BindGroupLayoutEntry {
577 binding: 0,
578 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
579 ty: wgpu::BindingType::Buffer {
580 ty: wgpu::BufferBindingType::Uniform,
581 has_dynamic_offset: false,
582 min_binding_size: NonZeroU64::new(
583 std::mem::size_of::<SurfaceParams>() as u64
584 ),
585 },
586 count: None,
587 },
588 wgpu::BindGroupLayoutEntry {
589 binding: 1,
590 visibility: wgpu::ShaderStages::FRAGMENT,
591 ty: wgpu::BindingType::Texture {
592 sample_type: wgpu::TextureSampleType::Float { filterable: true },
593 view_dimension: wgpu::TextureViewDimension::D2,
594 multisampled: false,
595 },
596 count: None,
597 },
598 wgpu::BindGroupLayoutEntry {
599 binding: 2,
600 visibility: wgpu::ShaderStages::FRAGMENT,
601 ty: wgpu::BindingType::Texture {
602 sample_type: wgpu::TextureSampleType::Float { filterable: true },
603 view_dimension: wgpu::TextureViewDimension::D2,
604 multisampled: false,
605 },
606 count: None,
607 },
608 wgpu::BindGroupLayoutEntry {
609 binding: 3,
610 visibility: wgpu::ShaderStages::FRAGMENT,
611 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
612 count: None,
613 },
614 ],
615 });
616
617 WgpuBindGroupLayouts {
618 globals,
619 instances,
620 instances_with_texture,
621 surfaces,
622 }
623 }
624
625 fn create_pipelines(
627 device: &wgpu::Device,
628 layouts: &WgpuBindGroupLayouts,
629 surface_format: wgpu::TextureFormat,
630 alpha_mode: wgpu::CompositeAlphaMode,
631 path_sample_count: u32,
632 dual_source_blending: bool,
633 ) -> WgpuPipelines {
634 let device_has_feature = device
643 .features()
644 .contains(wgpu::Features::DUAL_SOURCE_BLENDING);
645 if dual_source_blending && !device_has_feature {
646 log::error!(
647 "BUG: dual_source_blending flag is true but device does not \
648 have DUAL_SOURCE_BLENDING enabled (device features: {:?}). \
649 Falling back to mono text rendering. Please report this at \
650 https://github.com/zed-industries/zed/issues",
651 device.features(),
652 );
653 }
654 let dual_source_blending = dual_source_blending && device_has_feature;
655
656 let base_shader_source = include_str!("shaders.wgsl");
657 let shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
658 label: Some("gpui_shaders"),
659 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(base_shader_source)),
660 });
661
662 let subpixel_shader_source = include_str!("shaders_subpixel.wgsl");
663 let subpixel_shader_module = if dual_source_blending {
664 let combined = format!(
665 "enable dual_source_blending;\n{base_shader_source}\n{subpixel_shader_source}"
666 );
667 Some(device.create_shader_module(wgpu::ShaderModuleDescriptor {
668 label: Some("gpui_subpixel_shaders"),
669 source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Owned(combined)),
670 }))
671 } else {
672 None
673 };
674
675 let blend_mode = match alpha_mode {
676 wgpu::CompositeAlphaMode::PreMultiplied => {
677 wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING
678 }
679 _ => wgpu::BlendState::ALPHA_BLENDING,
680 };
681
682 let color_target = wgpu::ColorTargetState {
683 format: surface_format,
684 blend: Some(blend_mode),
685 write_mask: wgpu::ColorWrites::ALL,
686 };
687
688 let create_pipeline = |name: &str,
689 vs_entry: &str,
690 fs_entry: &str,
691 globals_layout: &wgpu::BindGroupLayout,
692 data_layout: &wgpu::BindGroupLayout,
693 topology: wgpu::PrimitiveTopology,
694 color_targets: &[Option<wgpu::ColorTargetState>],
695 sample_count: u32,
696 module: &wgpu::ShaderModule| {
697 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
698 label: Some(&format!("{name}_layout")),
699 bind_group_layouts: &[Some(globals_layout), Some(data_layout)],
700 immediate_size: 0,
701 });
702
703 device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
704 label: Some(name),
705 layout: Some(&pipeline_layout),
706 vertex: wgpu::VertexState {
707 module,
708 entry_point: Some(vs_entry),
709 buffers: &[],
710 compilation_options: wgpu::PipelineCompilationOptions::default(),
711 },
712 fragment: Some(wgpu::FragmentState {
713 module,
714 entry_point: Some(fs_entry),
715 targets: color_targets,
716 compilation_options: wgpu::PipelineCompilationOptions::default(),
717 }),
718 primitive: wgpu::PrimitiveState {
719 topology,
720 strip_index_format: None,
721 front_face: wgpu::FrontFace::Ccw,
722 cull_mode: None,
723 polygon_mode: wgpu::PolygonMode::Fill,
724 unclipped_depth: false,
725 conservative: false,
726 },
727 depth_stencil: None,
728 multisample: wgpu::MultisampleState {
729 count: sample_count,
730 mask: !0,
731 alpha_to_coverage_enabled: false,
732 },
733 multiview_mask: None,
734 cache: None,
735 })
736 };
737
738 let quads = create_pipeline(
739 "quads",
740 "vs_quad",
741 "fs_quad",
742 &layouts.globals,
743 &layouts.instances,
744 wgpu::PrimitiveTopology::TriangleStrip,
745 &[Some(color_target.clone())],
746 1,
747 &shader_module,
748 );
749
750 let shadows = create_pipeline(
751 "shadows",
752 "vs_shadow",
753 "fs_shadow",
754 &layouts.globals,
755 &layouts.instances,
756 wgpu::PrimitiveTopology::TriangleStrip,
757 &[Some(color_target.clone())],
758 1,
759 &shader_module,
760 );
761
762 let path_rasterization = create_pipeline(
763 "path_rasterization",
764 "vs_path_rasterization",
765 "fs_path_rasterization",
766 &layouts.globals,
767 &layouts.instances,
768 wgpu::PrimitiveTopology::TriangleList,
769 &[Some(wgpu::ColorTargetState {
770 format: surface_format,
771 blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
772 write_mask: wgpu::ColorWrites::ALL,
773 })],
774 path_sample_count,
775 &shader_module,
776 );
777
778 let paths_blend = wgpu::BlendState {
779 color: wgpu::BlendComponent {
780 src_factor: wgpu::BlendFactor::One,
781 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
782 operation: wgpu::BlendOperation::Add,
783 },
784 alpha: wgpu::BlendComponent {
785 src_factor: wgpu::BlendFactor::One,
786 dst_factor: wgpu::BlendFactor::One,
787 operation: wgpu::BlendOperation::Add,
788 },
789 };
790
791 let paths = create_pipeline(
792 "paths",
793 "vs_path",
794 "fs_path",
795 &layouts.globals,
796 &layouts.instances_with_texture,
797 wgpu::PrimitiveTopology::TriangleStrip,
798 &[Some(wgpu::ColorTargetState {
799 format: surface_format,
800 blend: Some(paths_blend),
801 write_mask: wgpu::ColorWrites::ALL,
802 })],
803 1,
804 &shader_module,
805 );
806
807 let underlines = create_pipeline(
808 "underlines",
809 "vs_underline",
810 "fs_underline",
811 &layouts.globals,
812 &layouts.instances,
813 wgpu::PrimitiveTopology::TriangleStrip,
814 &[Some(color_target.clone())],
815 1,
816 &shader_module,
817 );
818
819 let mono_sprites = create_pipeline(
820 "mono_sprites",
821 "vs_mono_sprite",
822 "fs_mono_sprite",
823 &layouts.globals,
824 &layouts.instances_with_texture,
825 wgpu::PrimitiveTopology::TriangleStrip,
826 &[Some(color_target.clone())],
827 1,
828 &shader_module,
829 );
830
831 let subpixel_sprites = if let Some(subpixel_module) = &subpixel_shader_module {
832 let subpixel_blend = wgpu::BlendState {
833 color: wgpu::BlendComponent {
834 src_factor: wgpu::BlendFactor::Src1,
835 dst_factor: wgpu::BlendFactor::OneMinusSrc1,
836 operation: wgpu::BlendOperation::Add,
837 },
838 alpha: wgpu::BlendComponent {
839 src_factor: wgpu::BlendFactor::One,
840 dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
841 operation: wgpu::BlendOperation::Add,
842 },
843 };
844
845 Some(create_pipeline(
846 "subpixel_sprites",
847 "vs_subpixel_sprite",
848 "fs_subpixel_sprite",
849 &layouts.globals,
850 &layouts.instances_with_texture,
851 wgpu::PrimitiveTopology::TriangleStrip,
852 &[Some(wgpu::ColorTargetState {
853 format: surface_format,
854 blend: Some(subpixel_blend),
855 write_mask: wgpu::ColorWrites::COLOR,
856 })],
857 1,
858 subpixel_module,
859 ))
860 } else {
861 None
862 };
863
864 let poly_sprites = create_pipeline(
865 "poly_sprites",
866 "vs_poly_sprite",
867 "fs_poly_sprite",
868 &layouts.globals,
869 &layouts.instances_with_texture,
870 wgpu::PrimitiveTopology::TriangleStrip,
871 &[Some(color_target.clone())],
872 1,
873 &shader_module,
874 );
875
876 let surfaces = create_pipeline(
877 "surfaces",
878 "vs_surface",
879 "fs_surface",
880 &layouts.globals,
881 &layouts.surfaces,
882 wgpu::PrimitiveTopology::TriangleStrip,
883 &[Some(color_target)],
884 1,
885 &shader_module,
886 );
887
888 WgpuPipelines {
889 quads,
890 shadows,
891 path_rasterization,
892 paths,
893 underlines,
894 mono_sprites,
895 subpixel_sprites,
896 poly_sprites,
897 surfaces,
898 }
899 }
900
901 fn create_path_intermediate(
903 device: &wgpu::Device,
904 format: wgpu::TextureFormat,
905 width: u32,
906 height: u32,
907 ) -> (wgpu::Texture, wgpu::TextureView) {
908 let texture = device.create_texture(&wgpu::TextureDescriptor {
909 label: Some("path_intermediate"),
910 size: wgpu::Extent3d {
911 width: width.max(1),
912 height: height.max(1),
913 depth_or_array_layers: 1,
914 },
915 mip_level_count: 1,
916 sample_count: 1,
917 dimension: wgpu::TextureDimension::D2,
918 format,
919 usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
920 view_formats: &[],
921 });
922 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
923 (texture, view)
924 }
925
926 fn create_msaa_if_needed(
928 device: &wgpu::Device,
929 format: wgpu::TextureFormat,
930 width: u32,
931 height: u32,
932 sample_count: u32,
933 ) -> Option<(wgpu::Texture, wgpu::TextureView)> {
934 if sample_count <= 1 {
935 return None;
936 }
937 let texture = device.create_texture(&wgpu::TextureDescriptor {
938 label: Some("path_msaa"),
939 size: wgpu::Extent3d {
940 width: width.max(1),
941 height: height.max(1),
942 depth_or_array_layers: 1,
943 },
944 mip_level_count: 1,
945 sample_count,
946 dimension: wgpu::TextureDimension::D2,
947 format,
948 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
949 view_formats: &[],
950 });
951 let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
952 Some((texture, view))
953 }
954
955 pub fn update_drawable_size(&mut self, size: Size<DevicePixels>) {
956 let width = size.width.0 as u32;
957 let height = size.height.0 as u32;
958
959 if width != self.surface_config.width || height != self.surface_config.height {
960 let clamped_width = width.min(self.max_texture_size);
961 let clamped_height = height.min(self.max_texture_size);
962
963 if clamped_width != width || clamped_height != height {
964 warn!(
965 "Requested surface size ({}, {}) exceeds maximum texture dimension {}. \
966 Clamping to ({}, {}). Window content may not fill the entire window.",
967 width, height, self.max_texture_size, clamped_width, clamped_height
968 );
969 }
970
971 self.surface_config.width = clamped_width.max(1);
972 self.surface_config.height = clamped_height.max(1);
973 let surface_config = self.surface_config.clone();
974
975 let resources = self.resources_mut();
976
977 if let Err(e) = resources.device.poll(wgpu::PollType::Wait {
979 submission_index: None,
980 timeout: None,
981 }) {
982 warn!("在调整大小时轮询设备失败: {e:?}");
983 }
984
985 if let Some(ref texture) = resources.path_intermediate_texture {
987 texture.destroy();
988 }
989 if let Some(ref texture) = resources.path_msaa_texture {
990 texture.destroy();
991 }
992
993 resources
994 .surface
995 .configure(&resources.device, &surface_config);
996
997 resources.invalidate_intermediate_textures();
1001 }
1002 }
1003
1004 fn ensure_intermediate_textures(&mut self) {
1005 if self.resources().path_intermediate_texture.is_some() {
1006 return;
1007 }
1008
1009 let format = self.surface_config.format;
1010 let width = self.surface_config.width;
1011 let height = self.surface_config.height;
1012 let path_sample_count = self.rendering_params.path_sample_count;
1013 let resources = self.resources_mut();
1014
1015 let (t, v) = Self::create_path_intermediate(&resources.device, format, width, height);
1016 resources.path_intermediate_texture = Some(t);
1017 resources.path_intermediate_view = Some(v);
1018
1019 let (path_msaa_texture, path_msaa_view) = Self::create_msaa_if_needed(
1020 &resources.device,
1021 format,
1022 width,
1023 height,
1024 path_sample_count,
1025 )
1026 .map(|(t, v)| (Some(t), Some(v)))
1027 .unwrap_or((None, None));
1028 resources.path_msaa_texture = path_msaa_texture;
1029 resources.path_msaa_view = path_msaa_view;
1030 }
1031
1032 pub fn set_subpixel_layout(&mut self, is_bgr: bool) {
1033 self.is_bgr = is_bgr;
1034 }
1035
1036 pub fn update_transparency(&mut self, transparent: bool) {
1037 let new_alpha_mode = if transparent {
1038 self.transparent_alpha_mode
1039 } else {
1040 self.opaque_alpha_mode
1041 };
1042
1043 if new_alpha_mode != self.surface_config.alpha_mode {
1044 self.surface_config.alpha_mode = new_alpha_mode;
1045 let surface_config = self.surface_config.clone();
1046 let path_sample_count = self.rendering_params.path_sample_count;
1047 let dual_source_blending = self.dual_source_blending;
1048 let resources = self.resources_mut();
1049 resources
1050 .surface
1051 .configure(&resources.device, &surface_config);
1052 resources.pipelines = Self::create_pipelines(
1053 &resources.device,
1054 &resources.bind_group_layouts,
1055 surface_config.format,
1056 surface_config.alpha_mode,
1057 path_sample_count,
1058 dual_source_blending,
1059 );
1060 }
1061 }
1062
1063 #[allow(dead_code)]
1064 pub fn viewport_size(&self) -> Size<DevicePixels> {
1065 Size {
1066 width: DevicePixels(self.surface_config.width as i32),
1067 height: DevicePixels(self.surface_config.height as i32),
1068 }
1069 }
1070
1071 pub fn sprite_atlas(&self) -> &Arc<WgpuAtlas> {
1072 &self.atlas
1073 }
1074
1075 pub fn supports_dual_source_blending(&self) -> bool {
1076 self.dual_source_blending
1077 }
1078
1079 pub fn gpu_specs(&self) -> GpuSpecs {
1080 GpuSpecs {
1081 is_software_emulated: self.adapter_info.device_type == wgpu::DeviceType::Cpu,
1082 device_name: self.adapter_info.name.clone(),
1083 driver_name: self.adapter_info.driver.clone(),
1084 driver_info: self.adapter_info.driver_info.clone(),
1085 }
1086 }
1087
1088 pub fn max_texture_size(&self) -> u32 {
1089 self.max_texture_size
1090 }
1091
1092 pub fn draw(&mut self, scene: &Scene) -> bool {
1093 if !self.surface_configured {
1098 return false;
1099 }
1100
1101 let last_error = self.last_error.lock().unwrap().take();
1102 if let Some(error) = last_error {
1103 self.failed_frame_count += 1;
1104 log::error!(
1105 "GPU 错误在帧期间(失败 {} / 10): {error}",
1106 self.failed_frame_count
1107 );
1108
1109 if self.failed_frame_count > 10 {
1111 panic!("连续 GPU 错误过多。最后错误: {error}");
1112 } else if self.failed_frame_count > 5 {
1113 if let Some(res) = self.resources.as_mut() {
1114 res.invalidate_intermediate_textures();
1115 }
1116 self.atlas.clear();
1117 self.needs_redraw = true;
1118 self.failed_frame_count = 0;
1119 return false;
1120 }
1121 } else {
1122 self.failed_frame_count = 0;
1123 }
1124
1125 self.atlas.before_frame();
1126
1127 let frame = match self.resources().surface.get_current_texture() {
1128 wgpu::CurrentSurfaceTexture::Success(frame) => frame,
1129 wgpu::CurrentSurfaceTexture::Suboptimal(frame) => {
1130 drop(frame);
1132 let surface_config = self.surface_config.clone();
1133 let resources = self.resources_mut();
1134 resources
1135 .surface
1136 .configure(&resources.device, &surface_config);
1137 return false;
1138 }
1139 wgpu::CurrentSurfaceTexture::Lost | wgpu::CurrentSurfaceTexture::Outdated => {
1140 let surface_config = self.surface_config.clone();
1141 let resources = self.resources_mut();
1142 resources
1143 .surface
1144 .configure(&resources.device, &surface_config);
1145 return false;
1146 }
1147 wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded => {
1148 return false;
1149 }
1150 wgpu::CurrentSurfaceTexture::Validation => {
1151 *self.last_error.lock().unwrap() =
1152 Some("Surface texture validation error".to_string());
1153 return false;
1154 }
1155 };
1156
1157 self.ensure_intermediate_textures();
1159
1160 let frame_view = frame
1161 .texture
1162 .create_view(&wgpu::TextureViewDescriptor::default());
1163
1164 let gamma_params = GammaParams {
1165 gamma_ratios: self.rendering_params.gamma_ratios,
1166 grayscale_enhanced_contrast: self.rendering_params.grayscale_enhanced_contrast,
1167 subpixel_enhanced_contrast: self.rendering_params.subpixel_enhanced_contrast,
1168 is_bgr: self.is_bgr as u32,
1169 _pad: 0,
1170 };
1171
1172 let globals = GlobalParams {
1173 viewport_size: [
1174 self.surface_config.width as f32,
1175 self.surface_config.height as f32,
1176 ],
1177 premultiplied_alpha: if self.surface_config.alpha_mode
1178 == wgpu::CompositeAlphaMode::PreMultiplied
1179 {
1180 1
1181 } else {
1182 0
1183 },
1184 pad: 0,
1185 };
1186
1187 let path_globals = GlobalParams {
1188 premultiplied_alpha: 0,
1189 ..globals
1190 };
1191
1192 {
1193 let resources = self.resources();
1194 resources.queue.write_buffer(
1195 &resources.globals_buffer,
1196 0,
1197 bytemuck::bytes_of(&globals),
1198 );
1199 resources.queue.write_buffer(
1200 &resources.globals_buffer,
1201 self.path_globals_offset,
1202 bytemuck::bytes_of(&path_globals),
1203 );
1204 resources.queue.write_buffer(
1205 &resources.globals_buffer,
1206 self.gamma_offset,
1207 bytemuck::bytes_of(&gamma_params),
1208 );
1209 }
1210
1211 loop {
1212 let mut instance_offset: u64 = 0;
1213 let mut overflow = false;
1214
1215 let mut encoder =
1216 self.resources()
1217 .device
1218 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
1219 label: Some("main_encoder"),
1220 });
1221
1222 {
1223 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1224 label: Some("main_pass"),
1225 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1226 view: &frame_view,
1227 resolve_target: None,
1228 ops: wgpu::Operations {
1229 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
1230 store: wgpu::StoreOp::Store,
1231 },
1232 depth_slice: None,
1233 })],
1234 depth_stencil_attachment: None,
1235 ..Default::default()
1236 });
1237
1238 for batch in scene.batches() {
1239 let ok = match batch {
1240 PrimitiveBatch::Quads(range) => {
1241 self.draw_quads(&scene.quads[range], &mut instance_offset, &mut pass)
1242 }
1243 PrimitiveBatch::Shadows(range) => self.draw_shadows(
1244 &scene.shadows[range],
1245 &mut instance_offset,
1246 &mut pass,
1247 ),
1248 PrimitiveBatch::Paths(range) => {
1249 let paths = &scene.paths[range];
1250 if paths.is_empty() {
1251 continue;
1252 }
1253
1254 drop(pass);
1255
1256 let did_draw = self.draw_paths_to_intermediate(
1257 &mut encoder,
1258 paths,
1259 &mut instance_offset,
1260 );
1261
1262 pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1263 label: Some("main_pass_continued"),
1264 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1265 view: &frame_view,
1266 resolve_target: None,
1267 ops: wgpu::Operations {
1268 load: wgpu::LoadOp::Load,
1269 store: wgpu::StoreOp::Store,
1270 },
1271 depth_slice: None,
1272 })],
1273 depth_stencil_attachment: None,
1274 ..Default::default()
1275 });
1276
1277 if did_draw {
1278 self.draw_paths_from_intermediate(
1279 paths,
1280 &mut instance_offset,
1281 &mut pass,
1282 )
1283 } else {
1284 false
1285 }
1286 }
1287 PrimitiveBatch::Underlines(range) => self.draw_underlines(
1288 &scene.underlines[range],
1289 &mut instance_offset,
1290 &mut pass,
1291 ),
1292 PrimitiveBatch::MonochromeSprites { texture_id, range } => self
1293 .draw_monochrome_sprites(
1294 &scene.monochrome_sprites[range],
1295 texture_id,
1296 &mut instance_offset,
1297 &mut pass,
1298 ),
1299 PrimitiveBatch::SubpixelSprites { texture_id, range } => self
1300 .draw_subpixel_sprites(
1301 &scene.subpixel_sprites[range],
1302 texture_id,
1303 &mut instance_offset,
1304 &mut pass,
1305 ),
1306 PrimitiveBatch::PolychromeSprites { texture_id, range } => self
1307 .draw_polychrome_sprites(
1308 &scene.polychrome_sprites[range],
1309 texture_id,
1310 &mut instance_offset,
1311 &mut pass,
1312 ),
1313 PrimitiveBatch::Surfaces(_surfaces) => {
1314 true
1317 }
1318 };
1319 if !ok {
1320 overflow = true;
1321 break;
1322 }
1323 }
1324 }
1325
1326 if overflow {
1327 drop(encoder);
1328 if self.instance_buffer_capacity >= self.max_buffer_size {
1329 log::error!(
1330 "instance buffer size grew too large: {}",
1331 self.instance_buffer_capacity
1332 );
1333 frame.present();
1334 return true;
1335 }
1336 self.grow_instance_buffer();
1337 continue;
1338 }
1339
1340 self.resources()
1341 .queue
1342 .submit(std::iter::once(encoder.finish()));
1343 frame.present();
1344 return true;
1345 }
1346 }
1347
1348 fn draw_quads(
1349 &self,
1350 quads: &[Quad],
1351 instance_offset: &mut u64,
1352 pass: &mut wgpu::RenderPass<'_>,
1353 ) -> bool {
1354 let data = unsafe { Self::instance_bytes(quads) };
1355 self.draw_instances(
1356 data,
1357 quads.len() as u32,
1358 &self.resources().pipelines.quads,
1359 instance_offset,
1360 pass,
1361 )
1362 }
1363
1364 fn draw_shadows(
1365 &self,
1366 shadows: &[Shadow],
1367 instance_offset: &mut u64,
1368 pass: &mut wgpu::RenderPass<'_>,
1369 ) -> bool {
1370 let data = unsafe { Self::instance_bytes(shadows) };
1371 self.draw_instances(
1372 data,
1373 shadows.len() as u32,
1374 &self.resources().pipelines.shadows,
1375 instance_offset,
1376 pass,
1377 )
1378 }
1379
1380 fn draw_underlines(
1381 &self,
1382 underlines: &[Underline],
1383 instance_offset: &mut u64,
1384 pass: &mut wgpu::RenderPass<'_>,
1385 ) -> bool {
1386 let data = unsafe { Self::instance_bytes(underlines) };
1387 self.draw_instances(
1388 data,
1389 underlines.len() as u32,
1390 &self.resources().pipelines.underlines,
1391 instance_offset,
1392 pass,
1393 )
1394 }
1395
1396 fn draw_monochrome_sprites(
1397 &self,
1398 sprites: &[MonochromeSprite],
1399 texture_id: AtlasTextureId,
1400 instance_offset: &mut u64,
1401 pass: &mut wgpu::RenderPass<'_>,
1402 ) -> bool {
1403 let tex_info = self.atlas.get_texture_info(texture_id);
1404 let data = unsafe { Self::instance_bytes(sprites) };
1405 self.draw_instances_with_texture(
1406 data,
1407 sprites.len() as u32,
1408 &tex_info.view,
1409 &self.resources().pipelines.mono_sprites,
1410 instance_offset,
1411 pass,
1412 )
1413 }
1414
1415 fn draw_subpixel_sprites(
1416 &self,
1417 sprites: &[SubpixelSprite],
1418 texture_id: AtlasTextureId,
1419 instance_offset: &mut u64,
1420 pass: &mut wgpu::RenderPass<'_>,
1421 ) -> bool {
1422 let tex_info = self.atlas.get_texture_info(texture_id);
1423 let data = unsafe { Self::instance_bytes(sprites) };
1424 let resources = self.resources();
1425 let pipeline = resources
1426 .pipelines
1427 .subpixel_sprites
1428 .as_ref()
1429 .unwrap_or(&resources.pipelines.mono_sprites);
1430 self.draw_instances_with_texture(
1431 data,
1432 sprites.len() as u32,
1433 &tex_info.view,
1434 pipeline,
1435 instance_offset,
1436 pass,
1437 )
1438 }
1439
1440 fn draw_polychrome_sprites(
1441 &self,
1442 sprites: &[PolychromeSprite],
1443 texture_id: AtlasTextureId,
1444 instance_offset: &mut u64,
1445 pass: &mut wgpu::RenderPass<'_>,
1446 ) -> bool {
1447 let tex_info = self.atlas.get_texture_info(texture_id);
1448 let data = unsafe { Self::instance_bytes(sprites) };
1449 self.draw_instances_with_texture(
1450 data,
1451 sprites.len() as u32,
1452 &tex_info.view,
1453 &self.resources().pipelines.poly_sprites,
1454 instance_offset,
1455 pass,
1456 )
1457 }
1458
1459 fn draw_instances(
1460 &self,
1461 data: &[u8],
1462 instance_count: u32,
1463 pipeline: &wgpu::RenderPipeline,
1464 instance_offset: &mut u64,
1465 pass: &mut wgpu::RenderPass<'_>,
1466 ) -> bool {
1467 if instance_count == 0 {
1468 return true;
1469 }
1470 let Some((offset, size)) = self.write_to_instance_buffer(instance_offset, data) else {
1471 return false;
1472 };
1473 let resources = self.resources();
1474 let bind_group = resources
1475 .device
1476 .create_bind_group(&wgpu::BindGroupDescriptor {
1477 label: None,
1478 layout: &resources.bind_group_layouts.instances,
1479 entries: &[wgpu::BindGroupEntry {
1480 binding: 0,
1481 resource: self.instance_binding(offset, size),
1482 }],
1483 });
1484 pass.set_pipeline(pipeline);
1485 pass.set_bind_group(0, &resources.globals_bind_group, &[]);
1486 pass.set_bind_group(1, &bind_group, &[]);
1487 pass.draw(0..4, 0..instance_count);
1488 true
1489 }
1490
1491 fn draw_instances_with_texture(
1492 &self,
1493 data: &[u8],
1494 instance_count: u32,
1495 texture_view: &wgpu::TextureView,
1496 pipeline: &wgpu::RenderPipeline,
1497 instance_offset: &mut u64,
1498 pass: &mut wgpu::RenderPass<'_>,
1499 ) -> bool {
1500 if instance_count == 0 {
1501 return true;
1502 }
1503 let Some((offset, size)) = self.write_to_instance_buffer(instance_offset, data) else {
1504 return false;
1505 };
1506 let resources = self.resources();
1507 let bind_group = resources
1508 .device
1509 .create_bind_group(&wgpu::BindGroupDescriptor {
1510 label: None,
1511 layout: &resources.bind_group_layouts.instances_with_texture,
1512 entries: &[
1513 wgpu::BindGroupEntry {
1514 binding: 0,
1515 resource: self.instance_binding(offset, size),
1516 },
1517 wgpu::BindGroupEntry {
1518 binding: 1,
1519 resource: wgpu::BindingResource::TextureView(texture_view),
1520 },
1521 wgpu::BindGroupEntry {
1522 binding: 2,
1523 resource: wgpu::BindingResource::Sampler(&resources.atlas_sampler),
1524 },
1525 ],
1526 });
1527 pass.set_pipeline(pipeline);
1528 pass.set_bind_group(0, &resources.globals_bind_group, &[]);
1529 pass.set_bind_group(1, &bind_group, &[]);
1530 pass.draw(0..4, 0..instance_count);
1531 true
1532 }
1533
1534 unsafe fn instance_bytes<T>(instances: &[T]) -> &[u8] {
1535 unsafe {
1536 std::slice::from_raw_parts(
1537 instances.as_ptr() as *const u8,
1538 std::mem::size_of_val(instances),
1539 )
1540 }
1541 }
1542
1543 fn draw_paths_from_intermediate(
1544 &self,
1545 paths: &[Path<ScaledPixels>],
1546 instance_offset: &mut u64,
1547 pass: &mut wgpu::RenderPass<'_>,
1548 ) -> bool {
1549 let first_path = &paths[0];
1550 let sprites: Vec<PathSprite> = if paths.last().map(|p| &p.order) == Some(&first_path.order)
1551 {
1552 paths
1553 .iter()
1554 .map(|p| PathSprite {
1555 bounds: p.clipped_bounds(),
1556 })
1557 .collect()
1558 } else {
1559 let mut bounds = first_path.clipped_bounds();
1560 for path in paths.iter().skip(1) {
1561 bounds = bounds.union(&path.clipped_bounds());
1562 }
1563 vec![PathSprite { bounds }]
1564 };
1565
1566 let resources = self.resources();
1567 let Some(path_intermediate_view) = resources.path_intermediate_view.as_ref() else {
1568 return true;
1569 };
1570
1571 let sprite_data = unsafe { Self::instance_bytes(&sprites) };
1572 self.draw_instances_with_texture(
1573 sprite_data,
1574 sprites.len() as u32,
1575 path_intermediate_view,
1576 &resources.pipelines.paths,
1577 instance_offset,
1578 pass,
1579 )
1580 }
1581
1582 fn draw_paths_to_intermediate(
1583 &self,
1584 encoder: &mut wgpu::CommandEncoder,
1585 paths: &[Path<ScaledPixels>],
1586 instance_offset: &mut u64,
1587 ) -> bool {
1588 let mut vertices = Vec::new();
1589 for path in paths {
1590 let bounds = path.clipped_bounds();
1591 vertices.extend(path.vertices.iter().map(|v| PathRasterizationVertex {
1592 xy_position: v.xy_position,
1593 st_position: v.st_position,
1594 color: path.color,
1595 bounds,
1596 }));
1597 }
1598
1599 if vertices.is_empty() {
1600 return true;
1601 }
1602
1603 let vertex_data = unsafe { Self::instance_bytes(&vertices) };
1604 let Some((vertex_offset, vertex_size)) =
1605 self.write_to_instance_buffer(instance_offset, vertex_data)
1606 else {
1607 return false;
1608 };
1609
1610 let resources = self.resources();
1611 let data_bind_group = resources
1612 .device
1613 .create_bind_group(&wgpu::BindGroupDescriptor {
1614 label: Some("path_rasterization_bind_group"),
1615 layout: &resources.bind_group_layouts.instances,
1616 entries: &[wgpu::BindGroupEntry {
1617 binding: 0,
1618 resource: self.instance_binding(vertex_offset, vertex_size),
1619 }],
1620 });
1621
1622 let Some(path_intermediate_view) = resources.path_intermediate_view.as_ref() else {
1623 return true;
1624 };
1625
1626 let (target_view, resolve_target) = if let Some(ref msaa_view) = resources.path_msaa_view {
1627 (msaa_view, Some(path_intermediate_view))
1628 } else {
1629 (path_intermediate_view, None)
1630 };
1631
1632 {
1633 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1634 label: Some("path_rasterization_pass"),
1635 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1636 view: target_view,
1637 resolve_target,
1638 ops: wgpu::Operations {
1639 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
1640 store: wgpu::StoreOp::Store,
1641 },
1642 depth_slice: None,
1643 })],
1644 depth_stencil_attachment: None,
1645 ..Default::default()
1646 });
1647
1648 pass.set_pipeline(&resources.pipelines.path_rasterization);
1649 pass.set_bind_group(0, &resources.path_globals_bind_group, &[]);
1650 pass.set_bind_group(1, &data_bind_group, &[]);
1651 pass.draw(0..vertices.len() as u32, 0..1);
1652 }
1653
1654 true
1655 }
1656
1657 fn grow_instance_buffer(&mut self) {
1658 let new_capacity = (self.instance_buffer_capacity * 2).min(self.max_buffer_size);
1659 log::info!("increased instance buffer size to {}", new_capacity);
1660 let resources = self.resources_mut();
1661 resources.instance_buffer = resources.device.create_buffer(&wgpu::BufferDescriptor {
1662 label: Some("instance_buffer"),
1663 size: new_capacity,
1664 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
1665 mapped_at_creation: false,
1666 });
1667 self.instance_buffer_capacity = new_capacity;
1668 }
1669
1670 fn write_to_instance_buffer(
1671 &self,
1672 instance_offset: &mut u64,
1673 data: &[u8],
1674 ) -> Option<(u64, NonZeroU64)> {
1675 let offset = (*instance_offset).next_multiple_of(self.storage_buffer_alignment);
1676 let size = (data.len() as u64).max(16);
1677 if offset + size > self.instance_buffer_capacity {
1678 return None;
1679 }
1680 let resources = self.resources();
1681 resources
1682 .queue
1683 .write_buffer(&resources.instance_buffer, offset, data);
1684 *instance_offset = offset + size;
1685 Some((offset, NonZeroU64::new(size).expect("size is at least 16")))
1686 }
1687
1688 fn instance_binding(&self, offset: u64, size: NonZeroU64) -> wgpu::BindingResource<'_> {
1689 wgpu::BindingResource::Buffer(wgpu::BufferBinding {
1690 buffer: &self.resources().instance_buffer,
1691 offset,
1692 size: Some(size),
1693 })
1694 }
1695
1696 pub fn unconfigure_surface(&mut self) {
1704 self.surface_configured = false;
1705 if let Some(res) = self.resources.as_mut() {
1707 res.invalidate_intermediate_textures();
1708 }
1709 }
1710
1711 #[cfg(not(target_family = "wasm"))]
1720 pub fn replace_surface<W: HasWindowHandle>(
1721 &mut self,
1722 window: &W,
1723 config: WgpuSurfaceConfig,
1724 instance: &wgpu::Instance,
1725 ) -> anyhow::Result<()> {
1726 let window_handle = window
1727 .window_handle()
1728 .map_err(|e| anyhow::anyhow!("Failed to get window handle: {e}"))?;
1729
1730 let surface = create_surface(instance, window_handle.as_raw())?;
1731
1732 let width = (config.size.width.0 as u32).max(1);
1733 let height = (config.size.height.0 as u32).max(1);
1734
1735 let alpha_mode = if config.transparent {
1736 self.transparent_alpha_mode
1737 } else {
1738 self.opaque_alpha_mode
1739 };
1740
1741 self.surface_config.width = width;
1742 self.surface_config.height = height;
1743 self.surface_config.alpha_mode = alpha_mode;
1744 if let Some(mode) = config.preferred_present_mode {
1745 self.surface_config.present_mode = mode;
1746 }
1747
1748 {
1749 let res = self
1750 .resources
1751 .as_mut()
1752 .expect("GPU resources not available");
1753 surface.configure(&res.device, &self.surface_config);
1754 res.surface = surface;
1755
1756 res.invalidate_intermediate_textures();
1758 }
1759
1760 self.surface_configured = true;
1761
1762 Ok(())
1763 }
1764
1765 pub fn destroy(&mut self) {
1766 self.resources.take();
1769 }
1770
1771 pub fn device_lost(&self) -> bool {
1773 self.device_lost.load(std::sync::atomic::Ordering::SeqCst)
1774 }
1775
1776 pub fn needs_redraw(&mut self) -> bool {
1779 std::mem::take(&mut self.needs_redraw)
1780 }
1781
1782 #[cfg(not(target_family = "wasm"))]
1790 pub fn recover<W>(&mut self, window: &W) -> anyhow::Result<()>
1791 where
1792 W: HasWindowHandle + HasDisplayHandle + std::fmt::Debug + Send + Sync + Clone + 'static,
1793 {
1794 let gpu_context = self.context.as_ref().expect("recover 需要 gpu_context");
1795
1796 let needs_new_context = gpu_context
1798 .borrow()
1799 .as_ref()
1800 .is_none_or(|ctx| ctx.device_lost());
1801
1802 let window_handle = window
1803 .window_handle()
1804 .map_err(|e| anyhow::anyhow!("Failed to get window handle: {e}"))?;
1805
1806 let surface = if needs_new_context {
1807 log::warn!("GPU 设备丢失,正在重新创建上下文...");
1808
1809 self.resources = None;
1811 *gpu_context.borrow_mut() = None;
1812
1813 std::thread::sleep(std::time::Duration::from_millis(350));
1818
1819 let instance = WgpuContext::instance(Box::new(window.clone()));
1820 let surface = create_surface(&instance, window_handle.as_raw())?;
1821 let new_context =
1822 WgpuContext::new_rejecting_software(instance, &surface, self.compositor_gpu)?;
1823 *gpu_context.borrow_mut() = Some(new_context);
1824 surface
1825 } else {
1826 let ctx_ref = gpu_context.borrow();
1827 let instance = &ctx_ref.as_ref().unwrap().instance;
1828 create_surface(instance, window_handle.as_raw())?
1829 };
1830
1831 let config = WgpuSurfaceConfig {
1832 size: rgpui::Size {
1833 width: rgpui::DevicePixels(self.surface_config.width as i32),
1834 height: rgpui::DevicePixels(self.surface_config.height as i32),
1835 },
1836 transparent: self.surface_config.alpha_mode != wgpu::CompositeAlphaMode::Opaque,
1837 preferred_present_mode: Some(self.surface_config.present_mode),
1838 };
1839 let gpu_context = Rc::clone(gpu_context);
1840 let ctx_ref = gpu_context.borrow();
1841 let context = ctx_ref.as_ref().expect("context should exist");
1842
1843 self.resources = None;
1844 self.atlas.handle_device_lost(context);
1845
1846 *self = Self::new_internal(
1847 Some(gpu_context.clone()),
1848 context,
1849 surface,
1850 config,
1851 self.compositor_gpu,
1852 self.atlas.clone(),
1853 )?;
1854
1855 log::info!("GPU recovery complete");
1856 Ok(())
1857 }
1858}
1859
1860#[cfg(not(target_family = "wasm"))]
1861fn create_surface(
1862 instance: &wgpu::Instance,
1863 raw_window_handle: raw_window_handle::RawWindowHandle,
1864) -> anyhow::Result<wgpu::Surface<'static>> {
1865 unsafe {
1866 instance
1867 .create_surface_unsafe(wgpu::SurfaceTargetUnsafe::RawHandle {
1868 raw_display_handle: None,
1870 raw_window_handle,
1871 })
1872 .map_err(|e| anyhow::anyhow!("{e}"))
1873 }
1874}
1875
1876struct RenderingParameters {
1877 path_sample_count: u32,
1878 gamma_ratios: [f32; 4],
1879 grayscale_enhanced_contrast: f32,
1880 subpixel_enhanced_contrast: f32,
1881}
1882
1883impl RenderingParameters {
1884 fn new(adapter: &wgpu::Adapter, surface_format: wgpu::TextureFormat) -> Self {
1885 use std::env;
1886
1887 let format_features = adapter.get_texture_format_features(surface_format);
1888 let path_sample_count = [4, 2, 1]
1889 .into_iter()
1890 .find(|&n| format_features.flags.sample_count_supported(n))
1891 .unwrap_or(1);
1892
1893 let gamma = env::var("ZED_FONTS_GAMMA")
1894 .ok()
1895 .and_then(|v| v.parse().ok())
1896 .unwrap_or(1.8_f32)
1897 .clamp(1.0, 2.2);
1898 let gamma_ratios = get_gamma_correction_ratios(gamma);
1899
1900 let grayscale_enhanced_contrast = env::var("ZED_FONTS_GRAYSCALE_ENHANCED_CONTRAST")
1901 .ok()
1902 .and_then(|v| v.parse().ok())
1903 .unwrap_or(1.0_f32)
1904 .max(0.0);
1905
1906 let subpixel_enhanced_contrast = env::var("ZED_FONTS_SUBPIXEL_ENHANCED_CONTRAST")
1907 .ok()
1908 .and_then(|v| v.parse().ok())
1909 .unwrap_or(0.5_f32)
1910 .max(0.0);
1911
1912 Self {
1913 path_sample_count,
1914 gamma_ratios,
1915 grayscale_enhanced_contrast,
1916 subpixel_enhanced_contrast,
1917 }
1918 }
1919}