1750 lines · 62.0 KB · Rust
RawDownload
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,    _pad: [f32; 2],} #[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,} 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>,} 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,    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>,} 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: HasWindowHandle + HasDisplayHandle>(        gpu_context: GpuContext,        window: &W,        config: WgpuSurfaceConfig,        compositor_gpu: Option<CompositorGpuHint>,    ) -> anyhow::Result<Self> {        let window_handle = window            .window_handle()            .map_err(|e| anyhow::anyhow!("Failed to get window handle: {e}"))?;        let display_handle = window            .display_handle()            .map_err(|e| anyhow::anyhow!("Failed to get display handle: {e}"))?;         let target = wgpu::SurfaceTargetUnsafe::RawHandle {            raw_display_handle: display_handle.as_raw(),            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);         // 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::new(            Arc::clone(&context.device),            Arc::clone(&context.queue),        ));         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::new(            Arc::clone(&context.device),            Arc::clone(&context.queue),        ));         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: 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,            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(),        })    }     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,                        visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,                        ty: wgpu::BindingType::Buffer {                            ty: wgpu::BufferBindingType::Uniform,                            has_dynamic_offset: false,                            min_binding_size: NonZeroU64::new(                                std::mem::size_of::<GlobalParams>() as u64                            ),                        },                        count: None,                    },                    wgpu::BindGroupLayoutEntry {                        binding: 1,                        visibility: wgpu::ShaderStages::FRAGMENT,                        ty: wgpu::BindingType::Buffer {                            ty: wgpu::BufferBindingType::Uniform,                            has_dynamic_offset: false,                            min_binding_size: NonZeroU64::new(                                std::mem::size_of::<GammaParams>() as u64                            ),                        },                        count: None,

Lines 1–500 of 1750.