use crate::{CompositorGpuHint, WgpuAtlas, WgpuContext};use bytemuck::{Pod, Zeroable};use gpui::{ AtlasTextureId, Background, Bounds, DevicePixels, GpuSpecs, MonochromeSprite, Path, Point, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, SubpixelSprite, Underline, get_gamma_correction_ratios,};use log::warn;#[cfg(not(target_family = "wasm"))]use raw_window_handle::{HasDisplayHandle, HasWindowHandle};use std::cell::RefCell;use std::num::NonZeroU64;use std::rc::Rc;use std::sync::{Arc, Mutex}; #[repr(C)]#[derive(Clone, Copy, Pod, Zeroable)]struct GlobalParams { viewport_size: [f32; 2], premultiplied_alpha: u32, pad: u32,} #[repr(C)]#[derive(Clone, Copy, Pod, Zeroable)]struct PodBounds { origin: [f32; 2], size: [f32; 2],} impl From<Bounds<ScaledPixels>> for PodBounds { fn from(bounds: Bounds<ScaledPixels>) -> Self { Self { origin: [bounds.origin.x.0, bounds.origin.y.0], size: [bounds.size.width.0, bounds.size.height.0], } }} #[repr(C)]#[derive(Clone, Copy, Pod, Zeroable)]struct SurfaceParams { bounds: PodBounds, content_mask: PodBounds,} #[repr(C)]#[derive(Clone, Copy, Pod, Zeroable)]struct GammaParams { gamma_ratios: [f32; 4], grayscale_enhanced_contrast: f32, subpixel_enhanced_contrast: f32, is_bgr: u32, _pad: u32,} #[derive(Clone, Debug)]#[repr(C)]struct PathSprite { bounds: Bounds<ScaledPixels>,} #[derive(Clone, Debug)]#[repr(C)]struct PathRasterizationVertex { xy_position: Point<ScaledPixels>, st_position: Point<f32>, color: Background, bounds: Bounds<ScaledPixels>,} pub struct WgpuSurfaceConfig { pub size: Size<DevicePixels>, pub transparent: bool, /// Preferred presentation mode. When `Some`, the renderer will use this /// mode if supported by the surface, falling back to `Fifo`. /// When `None`, defaults to `Fifo` (VSync). /// /// Mobile platforms may prefer `Mailbox` (triple-buffering) to avoid /// blocking in `get_current_texture()` during lifecycle transitions. pub preferred_present_mode: Option<wgpu::PresentMode>,} struct WgpuPipelines { quads: wgpu::RenderPipeline, shadows: wgpu::RenderPipeline, path_rasterization: wgpu::RenderPipeline, paths: wgpu::RenderPipeline, underlines: wgpu::RenderPipeline, mono_sprites: wgpu::RenderPipeline, subpixel_sprites: Option<wgpu::RenderPipeline>, poly_sprites: wgpu::RenderPipeline, #[allow(dead_code)] surfaces: wgpu::RenderPipeline,} struct WgpuBindGroupLayouts { globals: wgpu::BindGroupLayout, instances: wgpu::BindGroupLayout, instances_with_texture: wgpu::BindGroupLayout, surfaces: wgpu::BindGroupLayout,} /// Shared GPU context reference, used to coordinate device recovery across multiple windows.pub type GpuContext = Rc<RefCell<Option<WgpuContext>>>; /// GPU resources that must be dropped together during device recovery.struct WgpuResources { device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>, surface: wgpu::Surface<'static>, pipelines: WgpuPipelines, bind_group_layouts: WgpuBindGroupLayouts, atlas_sampler: wgpu::Sampler, globals_buffer: wgpu::Buffer, globals_bind_group: wgpu::BindGroup, path_globals_bind_group: wgpu::BindGroup, instance_buffer: wgpu::Buffer, path_intermediate_texture: Option<wgpu::Texture>, path_intermediate_view: Option<wgpu::TextureView>, path_msaa_texture: Option<wgpu::Texture>, path_msaa_view: Option<wgpu::TextureView>,} impl WgpuResources { fn invalidate_intermediate_textures(&mut self) { self.path_intermediate_texture = None; self.path_intermediate_view = None; self.path_msaa_texture = None; self.path_msaa_view = None; }} pub struct WgpuRenderer { /// Shared GPU context for device recovery coordination (unused on WASM). #[allow(dead_code)] context: Option<GpuContext>, /// Compositor GPU hint for adapter selection (unused on WASM). #[allow(dead_code)] compositor_gpu: Option<CompositorGpuHint>, resources: Option<WgpuResources>, surface_config: wgpu::SurfaceConfiguration, atlas: Arc<WgpuAtlas>, path_globals_offset: u64, gamma_offset: u64, instance_buffer_capacity: u64, max_buffer_size: u64, storage_buffer_alignment: u64, rendering_params: RenderingParameters, is_bgr: bool, dual_source_blending: bool, adapter_info: wgpu::AdapterInfo, transparent_alpha_mode: wgpu::CompositeAlphaMode, opaque_alpha_mode: wgpu::CompositeAlphaMode, max_texture_size: u32, last_error: Arc<Mutex<Option<String>>>, failed_frame_count: u32, device_lost: std::sync::Arc<std::sync::atomic::AtomicBool>, surface_configured: bool, needs_redraw: bool,} impl WgpuRenderer { fn resources(&self) -> &WgpuResources { self.resources .as_ref() .expect("GPU resources not available") } fn resources_mut(&mut self) -> &mut WgpuResources { self.resources .as_mut() .expect("GPU resources not available") } /// Creates a new WgpuRenderer from raw window handles. /// /// The `gpu_context` is a shared reference that coordinates GPU context across /// multiple windows. The first window to create a renderer will initialize the /// context; subsequent windows will share it. /// /// # Safety /// The caller must ensure that the window handle remains valid for the lifetime /// of the returned renderer. #[cfg(not(target_family = "wasm"))] pub fn new<W>( gpu_context: GpuContext, window: &W, config: WgpuSurfaceConfig, compositor_gpu: Option<CompositorGpuHint>, ) -> anyhow::Result<Self> where W: HasWindowHandle + HasDisplayHandle + std::fmt::Debug + Send + Sync + Clone + 'static, { let window_handle = window .window_handle() .map_err(|e| anyhow::anyhow!("Failed to get window handle: {e}"))?; let target = wgpu::SurfaceTargetUnsafe::RawHandle { // Fall back to the display handle already provided via InstanceDescriptor::display. raw_display_handle: None, raw_window_handle: window_handle.as_raw(), }; // Use the existing context's instance if available, otherwise create a new one. // The surface must be created with the same instance that will be used for // adapter selection, otherwise wgpu will panic. let instance = gpu_context .borrow() .as_ref() .map(|ctx| ctx.instance.clone()) .unwrap_or_else(|| WgpuContext::instance(Box::new(window.clone()))); // Safety: The caller guarantees that the window handle is valid for the // lifetime of this renderer. In practice, the RawWindow struct is created // from the native window handles and the surface is dropped before the window. let surface = unsafe { instance .create_surface_unsafe(target) .map_err(|e| anyhow::anyhow!("Failed to create surface: {e}"))? }; let mut ctx_ref = gpu_context.borrow_mut(); let context = match ctx_ref.as_mut() { Some(context) => { context.check_compatible_with_surface(&surface)?; context } None => ctx_ref.insert(WgpuContext::new(instance, &surface, compositor_gpu)?), }; let atlas = Arc::new(WgpuAtlas::from_context(context)); Self::new_internal( Some(Rc::clone(&gpu_context)), context, surface, config, compositor_gpu, atlas, ) } #[cfg(target_family = "wasm")] pub fn new_from_canvas( context: &WgpuContext, canvas: &web_sys::HtmlCanvasElement, config: WgpuSurfaceConfig, ) -> anyhow::Result<Self> { let surface = context .instance .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone())) .map_err(|e| anyhow::anyhow!("Failed to create surface: {e}"))?; let atlas = Arc::new(WgpuAtlas::from_context(context)); Self::new_internal(None, context, surface, config, None, atlas) } fn new_internal( gpu_context: Option<GpuContext>, context: &WgpuContext, surface: wgpu::Surface<'static>, config: WgpuSurfaceConfig, compositor_gpu: Option<CompositorGpuHint>, atlas: Arc<WgpuAtlas>, ) -> anyhow::Result<Self> { let surface_caps = surface.get_capabilities(&context.adapter); let preferred_formats = [ wgpu::TextureFormat::Bgra8Unorm, wgpu::TextureFormat::Rgba8Unorm, ]; let surface_format = preferred_formats .iter() .find(|f| surface_caps.formats.contains(f)) .copied() .or_else(|| surface_caps.formats.iter().find(|f| !f.is_srgb()).copied()) .or_else(|| surface_caps.formats.first().copied()) .ok_or_else(|| { anyhow::anyhow!( "Surface reports no supported texture formats for adapter {:?}", context.adapter.get_info().name ) })?; let pick_alpha_mode = |preferences: &[wgpu::CompositeAlphaMode]| -> anyhow::Result<wgpu::CompositeAlphaMode> { preferences .iter() .find(|p| surface_caps.alpha_modes.contains(p)) .copied() .or_else(|| surface_caps.alpha_modes.first().copied()) .ok_or_else(|| { anyhow::anyhow!( "Surface reports no supported alpha modes for adapter {:?}", context.adapter.get_info().name ) }) }; let transparent_alpha_mode = pick_alpha_mode(&[ wgpu::CompositeAlphaMode::PreMultiplied, wgpu::CompositeAlphaMode::Inherit, ])?; let opaque_alpha_mode = pick_alpha_mode(&[ wgpu::CompositeAlphaMode::Opaque, wgpu::CompositeAlphaMode::Inherit, ])?; let alpha_mode = if config.transparent { transparent_alpha_mode } else { opaque_alpha_mode }; let device = Arc::clone(&context.device); let max_texture_size = device.limits().max_texture_dimension_2d; let requested_width = config.size.width.0 as u32; let requested_height = config.size.height.0 as u32; let clamped_width = requested_width.min(max_texture_size); let clamped_height = requested_height.min(max_texture_size); if clamped_width != requested_width || clamped_height != requested_height { warn!( "Requested surface size ({}, {}) exceeds maximum texture dimension {}. \ Clamping to ({}, {}). Window content may not fill the entire window.", requested_width, requested_height, max_texture_size, clamped_width, clamped_height ); } let surface_config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface_format, width: clamped_width.max(1), height: clamped_height.max(1), present_mode: config .preferred_present_mode .filter(|mode| surface_caps.present_modes.contains(mode)) .unwrap_or(wgpu::PresentMode::Fifo), desired_maximum_frame_latency: 2, alpha_mode, view_formats: vec![], }; // Configure the surface immediately. The adapter selection process already validated // that this adapter can successfully configure this surface. surface.configure(&context.device, &surface_config); let queue = Arc::clone(&context.queue); let dual_source_blending = context.supports_dual_source_blending(); let rendering_params = RenderingParameters::new(&context.adapter, surface_format); let bind_group_layouts = Self::create_bind_group_layouts(&device); let pipelines = Self::create_pipelines( &device, &bind_group_layouts, surface_format, alpha_mode, rendering_params.path_sample_count, dual_source_blending, ); let atlas_sampler = device.create_sampler(&wgpu::SamplerDescriptor { label: Some("atlas_sampler"), mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, ..Default::default() }); let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment as u64; let globals_size = std::mem::size_of::<GlobalParams>() as u64; let gamma_size = std::mem::size_of::<GammaParams>() as u64; let path_globals_offset = globals_size.next_multiple_of(uniform_alignment); let gamma_offset = (path_globals_offset + globals_size).next_multiple_of(uniform_alignment); let globals_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("globals_buffer"), size: gamma_offset + gamma_size, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); let max_buffer_size = device.limits().max_buffer_size; let storage_buffer_alignment = device.limits().min_storage_buffer_offset_alignment as u64; let initial_instance_buffer_capacity = 2 * 1024 * 1024; let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("instance_buffer"), size: initial_instance_buffer_capacity, usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); let globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("globals_bind_group"), layout: &bind_group_layouts.globals, entries: &[ wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &globals_buffer, offset: 0, size: Some(NonZeroU64::new(globals_size).unwrap()), }), }, wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &globals_buffer, offset: gamma_offset, size: Some(NonZeroU64::new(gamma_size).unwrap()), }), }, ], }); let path_globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("path_globals_bind_group"), layout: &bind_group_layouts.globals, entries: &[ wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &globals_buffer, offset: path_globals_offset, size: Some(NonZeroU64::new(globals_size).unwrap()), }), }, wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &globals_buffer, offset: gamma_offset, size: Some(NonZeroU64::new(gamma_size).unwrap()), }), }, ], }); let adapter_info = context.adapter.get_info(); let last_error: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None)); let last_error_clone = Arc::clone(&last_error); device.on_uncaptured_error(Arc::new(move |error| { let mut guard = last_error_clone.lock().unwrap(); *guard = Some(error.to_string()); })); let resources = WgpuResources { device, queue, surface, pipelines, bind_group_layouts, atlas_sampler, globals_buffer, globals_bind_group, path_globals_bind_group, instance_buffer, // Defer intermediate texture creation to first draw call via ensure_intermediate_textures(). // This avoids panics when the device/surface is in an invalid state during initialization. path_intermediate_texture: None, path_intermediate_view: None, path_msaa_texture: None, path_msaa_view: None, }; Ok(Self { context: gpu_context, compositor_gpu, resources: Some(resources), surface_config, atlas, path_globals_offset, gamma_offset, instance_buffer_capacity: initial_instance_buffer_capacity, max_buffer_size, storage_buffer_alignment, rendering_params, is_bgr: false, dual_source_blending, adapter_info, transparent_alpha_mode, opaque_alpha_mode, max_texture_size, last_error, failed_frame_count: 0, device_lost: context.device_lost_flag(), surface_configured: true, needs_redraw: false, }) } fn create_bind_group_layouts(device: &wgpu::Device) -> WgpuBindGroupLayouts { let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("globals_layout"), entries: &[ wgpu::BindGroupLayoutEntry { binding: 0,Lines 1–500 of 1913. Load more
use crate::{CompositorGpuHint, WgpuAtlas, WgpuContext}; use bytemuck::{Pod, Zeroable}; use gpui::{ AtlasTextureId, Background, Bounds, DevicePixels, GpuSpecs, MonochromeSprite, Path, Point, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, SubpixelSprite, Underline, get_gamma_correction_ratios, }; use log::warn; #[cfg(not(target_family = "wasm"))] use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use std::cell::RefCell; use std::num::NonZeroU64; use std::rc::Rc; use std::sync::{Arc, Mutex}; #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct GlobalParams { viewport_size: [f32; 2], premultiplied_alpha: u32, pad: u32, } #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct PodBounds { origin: [f32; 2], size: [f32; 2], } impl From<Bounds<ScaledPixels>> for PodBounds { fn from(bounds: Bounds<ScaledPixels>) -> Self { Self { origin: [bounds.origin.x.0, bounds.origin.y.0], size: [bounds.size.width.0, bounds.size.height.0], } } } #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct SurfaceParams { bounds: PodBounds, content_mask: PodBounds, } #[repr(C)] #[derive(Clone, Copy, Pod, Zeroable)] struct GammaParams { gamma_ratios: [f32; 4], grayscale_enhanced_contrast: f32, subpixel_enhanced_contrast: f32, is_bgr: u32, _pad: u32, } #[derive(Clone, Debug)] #[repr(C)] struct PathSprite { bounds: Bounds<ScaledPixels>, } #[derive(Clone, Debug)] #[repr(C)] struct PathRasterizationVertex { xy_position: Point<ScaledPixels>, st_position: Point<f32>, color: Background, bounds: Bounds<ScaledPixels>, } pub struct WgpuSurfaceConfig { pub size: Size<DevicePixels>, pub transparent: bool, /// Preferred presentation mode. When `Some`, the renderer will use this /// mode if supported by the surface, falling back to `Fifo`. /// When `None`, defaults to `Fifo` (VSync). /// /// Mobile platforms may prefer `Mailbox` (triple-buffering) to avoid /// blocking in `get_current_texture()` during lifecycle transitions. pub preferred_present_mode: Option<wgpu::PresentMode>, } struct WgpuPipelines { quads: wgpu::RenderPipeline, shadows: wgpu::RenderPipeline, path_rasterization: wgpu::RenderPipeline, paths: wgpu::RenderPipeline, underlines: wgpu::RenderPipeline, mono_sprites: wgpu::RenderPipeline, subpixel_sprites: Option<wgpu::RenderPipeline>, poly_sprites: wgpu::RenderPipeline, #[allow(dead_code)] surfaces: wgpu::RenderPipeline, } struct WgpuBindGroupLayouts { globals: wgpu::BindGroupLayout, instances: wgpu::BindGroupLayout, instances_with_texture: wgpu::BindGroupLayout, surfaces: wgpu::BindGroupLayout, } /// Shared GPU context reference, used to coordinate device recovery across multiple windows. pub type GpuContext = Rc<RefCell<Option<WgpuContext>>>; /// GPU resources that must be dropped together during device recovery. struct WgpuResources { device: Arc<wgpu::Device>, queue: Arc<wgpu::Queue>, surface: wgpu::Surface<'static>, pipelines: WgpuPipelines, bind_group_layouts: WgpuBindGroupLayouts, atlas_sampler: wgpu::Sampler, globals_buffer: wgpu::Buffer, globals_bind_group: wgpu::BindGroup, path_globals_bind_group: wgpu::BindGroup, instance_buffer: wgpu::Buffer, path_intermediate_texture: Option<wgpu::Texture>, path_intermediate_view: Option<wgpu::TextureView>, path_msaa_texture: Option<wgpu::Texture>, path_msaa_view: Option<wgpu::TextureView>, } impl WgpuResources { fn invalidate_intermediate_textures(&mut self) { self.path_intermediate_texture = None; self.path_intermediate_view = None; self.path_msaa_texture = None; self.path_msaa_view = None; } } pub struct WgpuRenderer { /// Shared GPU context for device recovery coordination (unused on WASM). #[allow(dead_code)] context: Option<GpuContext>, /// Compositor GPU hint for adapter selection (unused on WASM). #[allow(dead_code)] compositor_gpu: Option<CompositorGpuHint>, resources: Option<WgpuResources>, surface_config: wgpu::SurfaceConfiguration, atlas: Arc<WgpuAtlas>, path_globals_offset: u64, gamma_offset: u64, instance_buffer_capacity: u64, max_buffer_size: u64, storage_buffer_alignment: u64, rendering_params: RenderingParameters, is_bgr: bool, dual_source_blending: bool, adapter_info: wgpu::AdapterInfo, transparent_alpha_mode: wgpu::CompositeAlphaMode, opaque_alpha_mode: wgpu::CompositeAlphaMode, max_texture_size: u32, last_error: Arc<Mutex<Option<String>>>, failed_frame_count: u32, device_lost: std::sync::Arc<std::sync::atomic::AtomicBool>, surface_configured: bool, needs_redraw: bool, } impl WgpuRenderer { fn resources(&self) -> &WgpuResources { self.resources .as_ref() .expect("GPU resources not available") } fn resources_mut(&mut self) -> &mut WgpuResources { self.resources .as_mut() .expect("GPU resources not available") } /// Creates a new WgpuRenderer from raw window handles. /// /// The `gpu_context` is a shared reference that coordinates GPU context across /// multiple windows. The first window to create a renderer will initialize the /// context; subsequent windows will share it. /// /// # Safety /// The caller must ensure that the window handle remains valid for the lifetime /// of the returned renderer. #[cfg(not(target_family = "wasm"))] pub fn new<W>( gpu_context: GpuContext, window: &W, config: WgpuSurfaceConfig, compositor_gpu: Option<CompositorGpuHint>, ) -> anyhow::Result<Self> where W: HasWindowHandle + HasDisplayHandle + std::fmt::Debug + Send + Sync + Clone + 'static, { let window_handle = window .window_handle() .map_err(|e| anyhow::anyhow!("Failed to get window handle: {e}"))?; let target = wgpu::SurfaceTargetUnsafe::RawHandle { // Fall back to the display handle already provided via InstanceDescriptor::display. raw_display_handle: None, raw_window_handle: window_handle.as_raw(), }; // Use the existing context's instance if available, otherwise create a new one. // The surface must be created with the same instance that will be used for // adapter selection, otherwise wgpu will panic. let instance = gpu_context .borrow() .as_ref() .map(|ctx| ctx.instance.clone()) .unwrap_or_else(|| WgpuContext::instance(Box::new(window.clone()))); // Safety: The caller guarantees that the window handle is valid for the // lifetime of this renderer. In practice, the RawWindow struct is created // from the native window handles and the surface is dropped before the window. let surface = unsafe { instance .create_surface_unsafe(target) .map_err(|e| anyhow::anyhow!("Failed to create surface: {e}"))? }; let mut ctx_ref = gpu_context.borrow_mut(); let context = match ctx_ref.as_mut() { Some(context) => { context.check_compatible_with_surface(&surface)?; context } None => ctx_ref.insert(WgpuContext::new(instance, &surface, compositor_gpu)?), }; let atlas = Arc::new(WgpuAtlas::from_context(context)); Self::new_internal( Some(Rc::clone(&gpu_context)), context, surface, config, compositor_gpu, atlas, ) } #[cfg(target_family = "wasm")] pub fn new_from_canvas( context: &WgpuContext, canvas: &web_sys::HtmlCanvasElement, config: WgpuSurfaceConfig, ) -> anyhow::Result<Self> { let surface = context .instance .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone())) .map_err(|e| anyhow::anyhow!("Failed to create surface: {e}"))?; let atlas = Arc::new(WgpuAtlas::from_context(context)); Self::new_internal(None, context, surface, config, None, atlas) } fn new_internal( gpu_context: Option<GpuContext>, context: &WgpuContext, surface: wgpu::Surface<'static>, config: WgpuSurfaceConfig, compositor_gpu: Option<CompositorGpuHint>, atlas: Arc<WgpuAtlas>, ) -> anyhow::Result<Self> { let surface_caps = surface.get_capabilities(&context.adapter); let preferred_formats = [ wgpu::TextureFormat::Bgra8Unorm, wgpu::TextureFormat::Rgba8Unorm, ]; let surface_format = preferred_formats .iter() .find(|f| surface_caps.formats.contains(f)) .copied() .or_else(|| surface_caps.formats.iter().find(|f| !f.is_srgb()).copied()) .or_else(|| surface_caps.formats.first().copied()) .ok_or_else(|| { anyhow::anyhow!( "Surface reports no supported texture formats for adapter {:?}", context.adapter.get_info().name ) })?; let pick_alpha_mode = |preferences: &[wgpu::CompositeAlphaMode]| -> anyhow::Result<wgpu::CompositeAlphaMode> { preferences .iter() .find(|p| surface_caps.alpha_modes.contains(p)) .copied() .or_else(|| surface_caps.alpha_modes.first().copied()) .ok_or_else(|| { anyhow::anyhow!( "Surface reports no supported alpha modes for adapter {:?}", context.adapter.get_info().name ) }) }; let transparent_alpha_mode = pick_alpha_mode(&[ wgpu::CompositeAlphaMode::PreMultiplied, wgpu::CompositeAlphaMode::Inherit, ])?; let opaque_alpha_mode = pick_alpha_mode(&[ wgpu::CompositeAlphaMode::Opaque, wgpu::CompositeAlphaMode::Inherit, ])?; let alpha_mode = if config.transparent { transparent_alpha_mode } else { opaque_alpha_mode }; let device = Arc::clone(&context.device); let max_texture_size = device.limits().max_texture_dimension_2d; let requested_width = config.size.width.0 as u32; let requested_height = config.size.height.0 as u32; let clamped_width = requested_width.min(max_texture_size); let clamped_height = requested_height.min(max_texture_size); if clamped_width != requested_width || clamped_height != requested_height { warn!( "Requested surface size ({}, {}) exceeds maximum texture dimension {}. \ Clamping to ({}, {}). Window content may not fill the entire window.", requested_width, requested_height, max_texture_size, clamped_width, clamped_height ); } let surface_config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface_format, width: clamped_width.max(1), height: clamped_height.max(1), present_mode: config .preferred_present_mode .filter(|mode| surface_caps.present_modes.contains(mode)) .unwrap_or(wgpu::PresentMode::Fifo), desired_maximum_frame_latency: 2, alpha_mode, view_formats: vec![], }; // Configure the surface immediately. The adapter selection process already validated // that this adapter can successfully configure this surface. surface.configure(&context.device, &surface_config); let queue = Arc::clone(&context.queue); let dual_source_blending = context.supports_dual_source_blending(); let rendering_params = RenderingParameters::new(&context.adapter, surface_format); let bind_group_layouts = Self::create_bind_group_layouts(&device); let pipelines = Self::create_pipelines( &device, &bind_group_layouts, surface_format, alpha_mode, rendering_params.path_sample_count, dual_source_blending, ); let atlas_sampler = device.create_sampler(&wgpu::SamplerDescriptor { label: Some("atlas_sampler"), mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, ..Default::default() }); let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment as u64; let globals_size = std::mem::size_of::<GlobalParams>() as u64; let gamma_size = std::mem::size_of::<GammaParams>() as u64; let path_globals_offset = globals_size.next_multiple_of(uniform_alignment); let gamma_offset = (path_globals_offset + globals_size).next_multiple_of(uniform_alignment); let globals_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("globals_buffer"), size: gamma_offset + gamma_size, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); let max_buffer_size = device.limits().max_buffer_size; let storage_buffer_alignment = device.limits().min_storage_buffer_offset_alignment as u64; let initial_instance_buffer_capacity = 2 * 1024 * 1024; let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("instance_buffer"), size: initial_instance_buffer_capacity, usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); let globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("globals_bind_group"), layout: &bind_group_layouts.globals, entries: &[ wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &globals_buffer, offset: 0, size: Some(NonZeroU64::new(globals_size).unwrap()), }), }, wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &globals_buffer, offset: gamma_offset, size: Some(NonZeroU64::new(gamma_size).unwrap()), }), }, ], }); let path_globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("path_globals_bind_group"), layout: &bind_group_layouts.globals, entries: &[ wgpu::BindGroupEntry { binding: 0, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &globals_buffer, offset: path_globals_offset, size: Some(NonZeroU64::new(globals_size).unwrap()), }), }, wgpu::BindGroupEntry { binding: 1, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { buffer: &globals_buffer, offset: gamma_offset, size: Some(NonZeroU64::new(gamma_size).unwrap()), }), }, ], }); let adapter_info = context.adapter.get_info(); let last_error: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None)); let last_error_clone = Arc::clone(&last_error); device.on_uncaptured_error(Arc::new(move |error| { let mut guard = last_error_clone.lock().unwrap(); *guard = Some(error.to_string()); })); let resources = WgpuResources { device, queue, surface, pipelines, bind_group_layouts, atlas_sampler, globals_buffer, globals_bind_group, path_globals_bind_group, instance_buffer, // Defer intermediate texture creation to first draw call via ensure_intermediate_textures(). // This avoids panics when the device/surface is in an invalid state during initialization. path_intermediate_texture: None, path_intermediate_view: None, path_msaa_texture: None, path_msaa_view: None, }; Ok(Self { context: gpu_context, compositor_gpu, resources: Some(resources), surface_config, atlas, path_globals_offset, gamma_offset, instance_buffer_capacity: initial_instance_buffer_capacity, max_buffer_size, storage_buffer_alignment, rendering_params, is_bgr: false, dual_source_blending, adapter_info, transparent_alpha_mode, opaque_alpha_mode, max_texture_size, last_error, failed_frame_count: 0, device_lost: context.device_lost_flag(), surface_configured: true, needs_redraw: false, }) } fn create_bind_group_layouts(device: &wgpu::Device) -> WgpuBindGroupLayouts { let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("globals_layout"), entries: &[ wgpu::BindGroupLayoutEntry { binding: 0,