Skip to repository content

tenant.openagents/omega

No repository description is available.

OpenAgents Git authority 2026-07-28T04:37:34.815Z Public web read
NIP-34 coordinate30617:7649603503856e5148d571eac2766b288a8ff1e9e35d380337a1d2b0015b4f92:omega
MaintainersHidden in public view
References2 branches · 1 tag
Read-only clonegit clone https://openagents.com/git/tenant.openagents/omega.git
Browse files

wgpu_renderer.rs

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