wgpu_renderer.rs

   1use crate::{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};
   8#[cfg(not(target_family = "wasm"))]
   9use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
  10use std::num::NonZeroU64;
  11use std::sync::Arc;
  12
  13#[repr(C)]
  14#[derive(Clone, Copy, Pod, Zeroable)]
  15struct GlobalParams {
  16    viewport_size: [f32; 2],
  17    premultiplied_alpha: u32,
  18    pad: u32,
  19}
  20
  21#[repr(C)]
  22#[derive(Clone, Copy, Pod, Zeroable)]
  23struct PodBounds {
  24    origin: [f32; 2],
  25    size: [f32; 2],
  26}
  27
  28impl From<Bounds<ScaledPixels>> for PodBounds {
  29    fn from(bounds: Bounds<ScaledPixels>) -> Self {
  30        Self {
  31            origin: [bounds.origin.x.0, bounds.origin.y.0],
  32            size: [bounds.size.width.0, bounds.size.height.0],
  33        }
  34    }
  35}
  36
  37#[repr(C)]
  38#[derive(Clone, Copy, Pod, Zeroable)]
  39struct SurfaceParams {
  40    bounds: PodBounds,
  41    content_mask: PodBounds,
  42}
  43
  44#[repr(C)]
  45#[derive(Clone, Copy, Pod, Zeroable)]
  46struct GammaParams {
  47    gamma_ratios: [f32; 4],
  48    grayscale_enhanced_contrast: f32,
  49    subpixel_enhanced_contrast: f32,
  50    _pad: [f32; 2],
  51}
  52
  53#[derive(Clone, Debug)]
  54#[repr(C)]
  55struct PathSprite {
  56    bounds: Bounds<ScaledPixels>,
  57}
  58
  59#[derive(Clone, Debug)]
  60#[repr(C)]
  61struct PathRasterizationVertex {
  62    xy_position: Point<ScaledPixels>,
  63    st_position: Point<f32>,
  64    color: Background,
  65    bounds: Bounds<ScaledPixels>,
  66}
  67
  68pub struct WgpuSurfaceConfig {
  69    pub size: Size<DevicePixels>,
  70    pub transparent: bool,
  71}
  72
  73struct WgpuPipelines {
  74    quads: wgpu::RenderPipeline,
  75    shadows: wgpu::RenderPipeline,
  76    path_rasterization: wgpu::RenderPipeline,
  77    paths: wgpu::RenderPipeline,
  78    underlines: wgpu::RenderPipeline,
  79    mono_sprites: wgpu::RenderPipeline,
  80    subpixel_sprites: Option<wgpu::RenderPipeline>,
  81    poly_sprites: wgpu::RenderPipeline,
  82    #[allow(dead_code)]
  83    surfaces: wgpu::RenderPipeline,
  84}
  85
  86struct WgpuBindGroupLayouts {
  87    globals: wgpu::BindGroupLayout,
  88    instances: wgpu::BindGroupLayout,
  89    instances_with_texture: wgpu::BindGroupLayout,
  90    surfaces: wgpu::BindGroupLayout,
  91}
  92
  93pub struct WgpuRenderer {
  94    device: Arc<wgpu::Device>,
  95    queue: Arc<wgpu::Queue>,
  96    surface: wgpu::Surface<'static>,
  97    surface_config: wgpu::SurfaceConfiguration,
  98    pipelines: WgpuPipelines,
  99    bind_group_layouts: WgpuBindGroupLayouts,
 100    atlas: Arc<WgpuAtlas>,
 101    atlas_sampler: wgpu::Sampler,
 102    globals_buffer: wgpu::Buffer,
 103    path_globals_offset: u64,
 104    gamma_offset: u64,
 105    globals_bind_group: wgpu::BindGroup,
 106    path_globals_bind_group: wgpu::BindGroup,
 107    instance_buffer: wgpu::Buffer,
 108    instance_buffer_capacity: u64,
 109    max_buffer_size: u64,
 110    storage_buffer_alignment: u64,
 111    path_intermediate_texture: wgpu::Texture,
 112    path_intermediate_view: wgpu::TextureView,
 113    path_msaa_texture: Option<wgpu::Texture>,
 114    path_msaa_view: Option<wgpu::TextureView>,
 115    rendering_params: RenderingParameters,
 116    dual_source_blending: bool,
 117    adapter_info: wgpu::AdapterInfo,
 118    transparent_alpha_mode: wgpu::CompositeAlphaMode,
 119    opaque_alpha_mode: wgpu::CompositeAlphaMode,
 120}
 121
 122impl WgpuRenderer {
 123    /// Creates a new WgpuRenderer from raw window handles.
 124    ///
 125    /// # Safety
 126    /// The caller must ensure that the window handle remains valid for the lifetime
 127    /// of the returned renderer.
 128    #[cfg(not(target_family = "wasm"))]
 129    pub fn new<W: HasWindowHandle + HasDisplayHandle>(
 130        gpu_context: &mut Option<WgpuContext>,
 131        window: &W,
 132        config: WgpuSurfaceConfig,
 133    ) -> anyhow::Result<Self> {
 134        let window_handle = window
 135            .window_handle()
 136            .map_err(|e| anyhow::anyhow!("Failed to get window handle: {e}"))?;
 137        let display_handle = window
 138            .display_handle()
 139            .map_err(|e| anyhow::anyhow!("Failed to get display handle: {e}"))?;
 140
 141        let target = wgpu::SurfaceTargetUnsafe::RawHandle {
 142            raw_display_handle: display_handle.as_raw(),
 143            raw_window_handle: window_handle.as_raw(),
 144        };
 145
 146        // Use the existing context's instance if available, otherwise create a new one.
 147        // The surface must be created with the same instance that will be used for
 148        // adapter selection, otherwise wgpu will panic.
 149        let instance = gpu_context
 150            .as_ref()
 151            .map(|ctx| ctx.instance.clone())
 152            .unwrap_or_else(WgpuContext::instance);
 153
 154        // Safety: The caller guarantees that the window handle is valid for the
 155        // lifetime of this renderer. In practice, the RawWindow struct is created
 156        // from the native window handles and the surface is dropped before the window.
 157        let surface = unsafe {
 158            instance
 159                .create_surface_unsafe(target)
 160                .map_err(|e| anyhow::anyhow!("Failed to create surface: {e}"))?
 161        };
 162
 163        let context = match gpu_context {
 164            Some(context) => {
 165                context.check_compatible_with_surface(&surface)?;
 166                context
 167            }
 168            None => gpu_context.insert(WgpuContext::new(instance, &surface)?),
 169        };
 170
 171        Self::new_with_surface(context, surface, config)
 172    }
 173
 174    #[cfg(target_family = "wasm")]
 175    pub fn new_from_canvas(
 176        context: &WgpuContext,
 177        canvas: &web_sys::HtmlCanvasElement,
 178        config: WgpuSurfaceConfig,
 179    ) -> anyhow::Result<Self> {
 180        let surface = context
 181            .instance
 182            .create_surface(wgpu::SurfaceTarget::Canvas(canvas.clone()))
 183            .map_err(|e| anyhow::anyhow!("Failed to create surface: {e}"))?;
 184        Self::new_with_surface(context, surface, config)
 185    }
 186
 187    pub fn new_with_surface(
 188        context: &WgpuContext,
 189        surface: wgpu::Surface<'static>,
 190        config: WgpuSurfaceConfig,
 191    ) -> anyhow::Result<Self> {
 192        let surface_caps = surface.get_capabilities(&context.adapter);
 193        let preferred_formats = [
 194            wgpu::TextureFormat::Bgra8Unorm,
 195            wgpu::TextureFormat::Rgba8Unorm,
 196        ];
 197        let surface_format = preferred_formats
 198            .iter()
 199            .find(|f| surface_caps.formats.contains(f))
 200            .copied()
 201            .or_else(|| surface_caps.formats.iter().find(|f| !f.is_srgb()).copied())
 202            .or_else(|| surface_caps.formats.first().copied())
 203            .ok_or_else(|| {
 204                anyhow::anyhow!(
 205                    "Surface reports no supported texture formats for adapter {:?}",
 206                    context.adapter.get_info().name
 207                )
 208            })?;
 209
 210        let pick_alpha_mode =
 211            |preferences: &[wgpu::CompositeAlphaMode]| -> anyhow::Result<wgpu::CompositeAlphaMode> {
 212                preferences
 213                    .iter()
 214                    .find(|p| surface_caps.alpha_modes.contains(p))
 215                    .copied()
 216                    .or_else(|| surface_caps.alpha_modes.first().copied())
 217                    .ok_or_else(|| {
 218                        anyhow::anyhow!(
 219                            "Surface reports no supported alpha modes for adapter {:?}",
 220                            context.adapter.get_info().name
 221                        )
 222                    })
 223            };
 224
 225        let transparent_alpha_mode = pick_alpha_mode(&[
 226            wgpu::CompositeAlphaMode::PreMultiplied,
 227            wgpu::CompositeAlphaMode::Inherit,
 228        ])?;
 229
 230        let opaque_alpha_mode = pick_alpha_mode(&[
 231            wgpu::CompositeAlphaMode::Opaque,
 232            wgpu::CompositeAlphaMode::Inherit,
 233        ])?;
 234
 235        let alpha_mode = if config.transparent {
 236            transparent_alpha_mode
 237        } else {
 238            opaque_alpha_mode
 239        };
 240
 241        let surface_config = wgpu::SurfaceConfiguration {
 242            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
 243            format: surface_format,
 244            width: config.size.width.0 as u32,
 245            height: config.size.height.0 as u32,
 246            present_mode: wgpu::PresentMode::Fifo,
 247            desired_maximum_frame_latency: 2,
 248            alpha_mode,
 249            view_formats: vec![],
 250        };
 251        surface.configure(&context.device, &surface_config);
 252
 253        let device = Arc::clone(&context.device);
 254        let queue = Arc::clone(&context.queue);
 255        let dual_source_blending = context.supports_dual_source_blending();
 256
 257        let rendering_params = RenderingParameters::new(&context.adapter, surface_format);
 258        let bind_group_layouts = Self::create_bind_group_layouts(&device);
 259        let pipelines = Self::create_pipelines(
 260            &device,
 261            &bind_group_layouts,
 262            surface_format,
 263            alpha_mode,
 264            rendering_params.path_sample_count,
 265            dual_source_blending,
 266        );
 267
 268        let atlas = Arc::new(WgpuAtlas::new(Arc::clone(&device), Arc::clone(&queue)));
 269        let atlas_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
 270            label: Some("atlas_sampler"),
 271            mag_filter: wgpu::FilterMode::Linear,
 272            min_filter: wgpu::FilterMode::Linear,
 273            ..Default::default()
 274        });
 275
 276        let uniform_alignment = device.limits().min_uniform_buffer_offset_alignment as u64;
 277        let globals_size = std::mem::size_of::<GlobalParams>() as u64;
 278        let gamma_size = std::mem::size_of::<GammaParams>() as u64;
 279        let path_globals_offset = globals_size.next_multiple_of(uniform_alignment);
 280        let gamma_offset = (path_globals_offset + globals_size).next_multiple_of(uniform_alignment);
 281
 282        let globals_buffer = device.create_buffer(&wgpu::BufferDescriptor {
 283            label: Some("globals_buffer"),
 284            size: gamma_offset + gamma_size,
 285            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
 286            mapped_at_creation: false,
 287        });
 288
 289        let max_buffer_size = device.limits().max_buffer_size;
 290        let storage_buffer_alignment = device.limits().min_storage_buffer_offset_alignment as u64;
 291        let initial_instance_buffer_capacity = 2 * 1024 * 1024;
 292        let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor {
 293            label: Some("instance_buffer"),
 294            size: initial_instance_buffer_capacity,
 295            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
 296            mapped_at_creation: false,
 297        });
 298
 299        let (path_intermediate_texture, path_intermediate_view) = Self::create_path_intermediate(
 300            &device,
 301            surface_format,
 302            config.size.width.0 as u32,
 303            config.size.height.0 as u32,
 304        );
 305
 306        let (path_msaa_texture, path_msaa_view) = Self::create_msaa_if_needed(
 307            &device,
 308            surface_format,
 309            config.size.width.0 as u32,
 310            config.size.height.0 as u32,
 311            rendering_params.path_sample_count,
 312        )
 313        .map(|(t, v)| (Some(t), Some(v)))
 314        .unwrap_or((None, None));
 315
 316        let globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
 317            label: Some("globals_bind_group"),
 318            layout: &bind_group_layouts.globals,
 319            entries: &[
 320                wgpu::BindGroupEntry {
 321                    binding: 0,
 322                    resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
 323                        buffer: &globals_buffer,
 324                        offset: 0,
 325                        size: Some(NonZeroU64::new(globals_size).unwrap()),
 326                    }),
 327                },
 328                wgpu::BindGroupEntry {
 329                    binding: 1,
 330                    resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
 331                        buffer: &globals_buffer,
 332                        offset: gamma_offset,
 333                        size: Some(NonZeroU64::new(gamma_size).unwrap()),
 334                    }),
 335                },
 336            ],
 337        });
 338
 339        let path_globals_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
 340            label: Some("path_globals_bind_group"),
 341            layout: &bind_group_layouts.globals,
 342            entries: &[
 343                wgpu::BindGroupEntry {
 344                    binding: 0,
 345                    resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
 346                        buffer: &globals_buffer,
 347                        offset: path_globals_offset,
 348                        size: Some(NonZeroU64::new(globals_size).unwrap()),
 349                    }),
 350                },
 351                wgpu::BindGroupEntry {
 352                    binding: 1,
 353                    resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
 354                        buffer: &globals_buffer,
 355                        offset: gamma_offset,
 356                        size: Some(NonZeroU64::new(gamma_size).unwrap()),
 357                    }),
 358                },
 359            ],
 360        });
 361
 362        let adapter_info = context.adapter.get_info();
 363
 364        Ok(Self {
 365            device,
 366            queue,
 367            surface,
 368            surface_config,
 369            pipelines,
 370            bind_group_layouts,
 371            atlas,
 372            atlas_sampler,
 373            globals_buffer,
 374            path_globals_offset,
 375            gamma_offset,
 376            globals_bind_group,
 377            path_globals_bind_group,
 378            instance_buffer,
 379            instance_buffer_capacity: initial_instance_buffer_capacity,
 380            max_buffer_size,
 381            storage_buffer_alignment,
 382            path_intermediate_texture,
 383            path_intermediate_view,
 384            path_msaa_texture,
 385            path_msaa_view,
 386            rendering_params,
 387            dual_source_blending,
 388            adapter_info,
 389            transparent_alpha_mode,
 390            opaque_alpha_mode,
 391        })
 392    }
 393
 394    fn create_bind_group_layouts(device: &wgpu::Device) -> WgpuBindGroupLayouts {
 395        let globals =
 396            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
 397                label: Some("globals_layout"),
 398                entries: &[
 399                    wgpu::BindGroupLayoutEntry {
 400                        binding: 0,
 401                        visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
 402                        ty: wgpu::BindingType::Buffer {
 403                            ty: wgpu::BufferBindingType::Uniform,
 404                            has_dynamic_offset: false,
 405                            min_binding_size: NonZeroU64::new(
 406                                std::mem::size_of::<GlobalParams>() as u64
 407                            ),
 408                        },
 409                        count: None,
 410                    },
 411                    wgpu::BindGroupLayoutEntry {
 412                        binding: 1,
 413                        visibility: wgpu::ShaderStages::FRAGMENT,
 414                        ty: wgpu::BindingType::Buffer {
 415                            ty: wgpu::BufferBindingType::Uniform,
 416                            has_dynamic_offset: false,
 417                            min_binding_size: NonZeroU64::new(
 418                                std::mem::size_of::<GammaParams>() as u64
 419                            ),
 420                        },
 421                        count: None,
 422                    },
 423                ],
 424            });
 425
 426        let storage_buffer_entry = |binding: u32| wgpu::BindGroupLayoutEntry {
 427            binding,
 428            visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
 429            ty: wgpu::BindingType::Buffer {
 430                ty: wgpu::BufferBindingType::Storage { read_only: true },
 431                has_dynamic_offset: false,
 432                min_binding_size: None,
 433            },
 434            count: None,
 435        };
 436
 437        let instances = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
 438            label: Some("instances_layout"),
 439            entries: &[storage_buffer_entry(0)],
 440        });
 441
 442        let instances_with_texture =
 443            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
 444                label: Some("instances_with_texture_layout"),
 445                entries: &[
 446                    storage_buffer_entry(0),
 447                    wgpu::BindGroupLayoutEntry {
 448                        binding: 1,
 449                        visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
 450                        ty: wgpu::BindingType::Texture {
 451                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
 452                            view_dimension: wgpu::TextureViewDimension::D2,
 453                            multisampled: false,
 454                        },
 455                        count: None,
 456                    },
 457                    wgpu::BindGroupLayoutEntry {
 458                        binding: 2,
 459                        visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
 460                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
 461                        count: None,
 462                    },
 463                ],
 464            });
 465
 466        let surfaces = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
 467            label: Some("surfaces_layout"),
 468            entries: &[
 469                wgpu::BindGroupLayoutEntry {
 470                    binding: 0,
 471                    visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
 472                    ty: wgpu::BindingType::Buffer {
 473                        ty: wgpu::BufferBindingType::Uniform,
 474                        has_dynamic_offset: false,
 475                        min_binding_size: NonZeroU64::new(
 476                            std::mem::size_of::<SurfaceParams>() as u64
 477                        ),
 478                    },
 479                    count: None,
 480                },
 481                wgpu::BindGroupLayoutEntry {
 482                    binding: 1,
 483                    visibility: wgpu::ShaderStages::FRAGMENT,
 484                    ty: wgpu::BindingType::Texture {
 485                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
 486                        view_dimension: wgpu::TextureViewDimension::D2,
 487                        multisampled: false,
 488                    },
 489                    count: None,
 490                },
 491                wgpu::BindGroupLayoutEntry {
 492                    binding: 2,
 493                    visibility: wgpu::ShaderStages::FRAGMENT,
 494                    ty: wgpu::BindingType::Texture {
 495                        sample_type: wgpu::TextureSampleType::Float { filterable: true },
 496                        view_dimension: wgpu::TextureViewDimension::D2,
 497                        multisampled: false,
 498                    },
 499                    count: None,
 500                },
 501                wgpu::BindGroupLayoutEntry {
 502                    binding: 3,
 503                    visibility: wgpu::ShaderStages::FRAGMENT,
 504                    ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
 505                    count: None,
 506                },
 507            ],
 508        });
 509
 510        WgpuBindGroupLayouts {
 511            globals,
 512            instances,
 513            instances_with_texture,
 514            surfaces,
 515        }
 516    }
 517
 518    fn create_pipelines(
 519        device: &wgpu::Device,
 520        layouts: &WgpuBindGroupLayouts,
 521        surface_format: wgpu::TextureFormat,
 522        alpha_mode: wgpu::CompositeAlphaMode,
 523        path_sample_count: u32,
 524        dual_source_blending: bool,
 525    ) -> WgpuPipelines {
 526        let base_shader_source = include_str!("shaders.wgsl");
 527        let shader_module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
 528            label: Some("gpui_shaders"),
 529            source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(base_shader_source)),
 530        });
 531
 532        let subpixel_shader_source = include_str!("shaders_subpixel.wgsl");
 533        let subpixel_shader_module = if dual_source_blending {
 534            let combined = format!(
 535                "enable dual_source_blending;\n{base_shader_source}\n{subpixel_shader_source}"
 536            );
 537            Some(device.create_shader_module(wgpu::ShaderModuleDescriptor {
 538                label: Some("gpui_subpixel_shaders"),
 539                source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Owned(combined)),
 540            }))
 541        } else {
 542            None
 543        };
 544
 545        let blend_mode = match alpha_mode {
 546            wgpu::CompositeAlphaMode::PreMultiplied => {
 547                wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING
 548            }
 549            _ => wgpu::BlendState::ALPHA_BLENDING,
 550        };
 551
 552        let color_target = wgpu::ColorTargetState {
 553            format: surface_format,
 554            blend: Some(blend_mode),
 555            write_mask: wgpu::ColorWrites::ALL,
 556        };
 557
 558        let create_pipeline = |name: &str,
 559                               vs_entry: &str,
 560                               fs_entry: &str,
 561                               globals_layout: &wgpu::BindGroupLayout,
 562                               data_layout: &wgpu::BindGroupLayout,
 563                               topology: wgpu::PrimitiveTopology,
 564                               color_targets: &[Option<wgpu::ColorTargetState>],
 565                               sample_count: u32,
 566                               module: &wgpu::ShaderModule| {
 567            let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
 568                label: Some(&format!("{name}_layout")),
 569                bind_group_layouts: &[globals_layout, data_layout],
 570                immediate_size: 0,
 571            });
 572
 573            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
 574                label: Some(name),
 575                layout: Some(&pipeline_layout),
 576                vertex: wgpu::VertexState {
 577                    module,
 578                    entry_point: Some(vs_entry),
 579                    buffers: &[],
 580                    compilation_options: wgpu::PipelineCompilationOptions::default(),
 581                },
 582                fragment: Some(wgpu::FragmentState {
 583                    module,
 584                    entry_point: Some(fs_entry),
 585                    targets: color_targets,
 586                    compilation_options: wgpu::PipelineCompilationOptions::default(),
 587                }),
 588                primitive: wgpu::PrimitiveState {
 589                    topology,
 590                    strip_index_format: None,
 591                    front_face: wgpu::FrontFace::Ccw,
 592                    cull_mode: None,
 593                    polygon_mode: wgpu::PolygonMode::Fill,
 594                    unclipped_depth: false,
 595                    conservative: false,
 596                },
 597                depth_stencil: None,
 598                multisample: wgpu::MultisampleState {
 599                    count: sample_count,
 600                    mask: !0,
 601                    alpha_to_coverage_enabled: false,
 602                },
 603                multiview_mask: None,
 604                cache: None,
 605            })
 606        };
 607
 608        let quads = create_pipeline(
 609            "quads",
 610            "vs_quad",
 611            "fs_quad",
 612            &layouts.globals,
 613            &layouts.instances,
 614            wgpu::PrimitiveTopology::TriangleStrip,
 615            &[Some(color_target.clone())],
 616            1,
 617            &shader_module,
 618        );
 619
 620        let shadows = create_pipeline(
 621            "shadows",
 622            "vs_shadow",
 623            "fs_shadow",
 624            &layouts.globals,
 625            &layouts.instances,
 626            wgpu::PrimitiveTopology::TriangleStrip,
 627            &[Some(color_target.clone())],
 628            1,
 629            &shader_module,
 630        );
 631
 632        let path_rasterization = create_pipeline(
 633            "path_rasterization",
 634            "vs_path_rasterization",
 635            "fs_path_rasterization",
 636            &layouts.globals,
 637            &layouts.instances,
 638            wgpu::PrimitiveTopology::TriangleList,
 639            &[Some(wgpu::ColorTargetState {
 640                format: surface_format,
 641                blend: Some(wgpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
 642                write_mask: wgpu::ColorWrites::ALL,
 643            })],
 644            path_sample_count,
 645            &shader_module,
 646        );
 647
 648        let paths_blend = wgpu::BlendState {
 649            color: wgpu::BlendComponent {
 650                src_factor: wgpu::BlendFactor::One,
 651                dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
 652                operation: wgpu::BlendOperation::Add,
 653            },
 654            alpha: wgpu::BlendComponent {
 655                src_factor: wgpu::BlendFactor::One,
 656                dst_factor: wgpu::BlendFactor::One,
 657                operation: wgpu::BlendOperation::Add,
 658            },
 659        };
 660
 661        let paths = create_pipeline(
 662            "paths",
 663            "vs_path",
 664            "fs_path",
 665            &layouts.globals,
 666            &layouts.instances_with_texture,
 667            wgpu::PrimitiveTopology::TriangleStrip,
 668            &[Some(wgpu::ColorTargetState {
 669                format: surface_format,
 670                blend: Some(paths_blend),
 671                write_mask: wgpu::ColorWrites::ALL,
 672            })],
 673            1,
 674            &shader_module,
 675        );
 676
 677        let underlines = create_pipeline(
 678            "underlines",
 679            "vs_underline",
 680            "fs_underline",
 681            &layouts.globals,
 682            &layouts.instances,
 683            wgpu::PrimitiveTopology::TriangleStrip,
 684            &[Some(color_target.clone())],
 685            1,
 686            &shader_module,
 687        );
 688
 689        let mono_sprites = create_pipeline(
 690            "mono_sprites",
 691            "vs_mono_sprite",
 692            "fs_mono_sprite",
 693            &layouts.globals,
 694            &layouts.instances_with_texture,
 695            wgpu::PrimitiveTopology::TriangleStrip,
 696            &[Some(color_target.clone())],
 697            1,
 698            &shader_module,
 699        );
 700
 701        let subpixel_sprites = if let Some(subpixel_module) = &subpixel_shader_module {
 702            let subpixel_blend = wgpu::BlendState {
 703                color: wgpu::BlendComponent {
 704                    src_factor: wgpu::BlendFactor::Src1,
 705                    dst_factor: wgpu::BlendFactor::OneMinusSrc1,
 706                    operation: wgpu::BlendOperation::Add,
 707                },
 708                alpha: wgpu::BlendComponent {
 709                    src_factor: wgpu::BlendFactor::One,
 710                    dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
 711                    operation: wgpu::BlendOperation::Add,
 712                },
 713            };
 714
 715            Some(create_pipeline(
 716                "subpixel_sprites",
 717                "vs_subpixel_sprite",
 718                "fs_subpixel_sprite",
 719                &layouts.globals,
 720                &layouts.instances_with_texture,
 721                wgpu::PrimitiveTopology::TriangleStrip,
 722                &[Some(wgpu::ColorTargetState {
 723                    format: surface_format,
 724                    blend: Some(subpixel_blend),
 725                    write_mask: wgpu::ColorWrites::COLOR,
 726                })],
 727                1,
 728                subpixel_module,
 729            ))
 730        } else {
 731            None
 732        };
 733
 734        let poly_sprites = create_pipeline(
 735            "poly_sprites",
 736            "vs_poly_sprite",
 737            "fs_poly_sprite",
 738            &layouts.globals,
 739            &layouts.instances_with_texture,
 740            wgpu::PrimitiveTopology::TriangleStrip,
 741            &[Some(color_target.clone())],
 742            1,
 743            &shader_module,
 744        );
 745
 746        let surfaces = create_pipeline(
 747            "surfaces",
 748            "vs_surface",
 749            "fs_surface",
 750            &layouts.globals,
 751            &layouts.surfaces,
 752            wgpu::PrimitiveTopology::TriangleStrip,
 753            &[Some(color_target)],
 754            1,
 755            &shader_module,
 756        );
 757
 758        WgpuPipelines {
 759            quads,
 760            shadows,
 761            path_rasterization,
 762            paths,
 763            underlines,
 764            mono_sprites,
 765            subpixel_sprites,
 766            poly_sprites,
 767            surfaces,
 768        }
 769    }
 770
 771    fn create_path_intermediate(
 772        device: &wgpu::Device,
 773        format: wgpu::TextureFormat,
 774        width: u32,
 775        height: u32,
 776    ) -> (wgpu::Texture, wgpu::TextureView) {
 777        let texture = device.create_texture(&wgpu::TextureDescriptor {
 778            label: Some("path_intermediate"),
 779            size: wgpu::Extent3d {
 780                width: width.max(1),
 781                height: height.max(1),
 782                depth_or_array_layers: 1,
 783            },
 784            mip_level_count: 1,
 785            sample_count: 1,
 786            dimension: wgpu::TextureDimension::D2,
 787            format,
 788            usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
 789            view_formats: &[],
 790        });
 791        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
 792        (texture, view)
 793    }
 794
 795    fn create_msaa_if_needed(
 796        device: &wgpu::Device,
 797        format: wgpu::TextureFormat,
 798        width: u32,
 799        height: u32,
 800        sample_count: u32,
 801    ) -> Option<(wgpu::Texture, wgpu::TextureView)> {
 802        if sample_count <= 1 {
 803            return None;
 804        }
 805        let texture = device.create_texture(&wgpu::TextureDescriptor {
 806            label: Some("path_msaa"),
 807            size: wgpu::Extent3d {
 808                width: width.max(1),
 809                height: height.max(1),
 810                depth_or_array_layers: 1,
 811            },
 812            mip_level_count: 1,
 813            sample_count,
 814            dimension: wgpu::TextureDimension::D2,
 815            format,
 816            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
 817            view_formats: &[],
 818        });
 819        let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
 820        Some((texture, view))
 821    }
 822
 823    pub fn update_drawable_size(&mut self, size: Size<DevicePixels>) {
 824        let width = size.width.0 as u32;
 825        let height = size.height.0 as u32;
 826
 827        if width != self.surface_config.width || height != self.surface_config.height {
 828            self.surface_config.width = width.max(1);
 829            self.surface_config.height = height.max(1);
 830            self.surface.configure(&self.device, &self.surface_config);
 831
 832            let (path_intermediate_texture, path_intermediate_view) =
 833                Self::create_path_intermediate(
 834                    &self.device,
 835                    self.surface_config.format,
 836                    self.surface_config.width,
 837                    self.surface_config.height,
 838                );
 839            self.path_intermediate_texture = path_intermediate_texture;
 840            self.path_intermediate_view = path_intermediate_view;
 841
 842            let (path_msaa_texture, path_msaa_view) = Self::create_msaa_if_needed(
 843                &self.device,
 844                self.surface_config.format,
 845                self.surface_config.width,
 846                self.surface_config.height,
 847                self.rendering_params.path_sample_count,
 848            )
 849            .map(|(t, v)| (Some(t), Some(v)))
 850            .unwrap_or((None, None));
 851            self.path_msaa_texture = path_msaa_texture;
 852            self.path_msaa_view = path_msaa_view;
 853        }
 854    }
 855
 856    pub fn update_transparency(&mut self, transparent: bool) {
 857        let new_alpha_mode = if transparent {
 858            self.transparent_alpha_mode
 859        } else {
 860            self.opaque_alpha_mode
 861        };
 862
 863        if new_alpha_mode != self.surface_config.alpha_mode {
 864            self.surface_config.alpha_mode = new_alpha_mode;
 865            self.surface.configure(&self.device, &self.surface_config);
 866            self.pipelines = Self::create_pipelines(
 867                &self.device,
 868                &self.bind_group_layouts,
 869                self.surface_config.format,
 870                self.surface_config.alpha_mode,
 871                self.rendering_params.path_sample_count,
 872                self.dual_source_blending,
 873            );
 874        }
 875    }
 876
 877    #[allow(dead_code)]
 878    pub fn viewport_size(&self) -> Size<DevicePixels> {
 879        Size {
 880            width: DevicePixels(self.surface_config.width as i32),
 881            height: DevicePixels(self.surface_config.height as i32),
 882        }
 883    }
 884
 885    pub fn sprite_atlas(&self) -> &Arc<WgpuAtlas> {
 886        &self.atlas
 887    }
 888
 889    pub fn supports_dual_source_blending(&self) -> bool {
 890        self.dual_source_blending
 891    }
 892
 893    pub fn gpu_specs(&self) -> GpuSpecs {
 894        GpuSpecs {
 895            is_software_emulated: self.adapter_info.device_type == wgpu::DeviceType::Cpu,
 896            device_name: self.adapter_info.name.clone(),
 897            driver_name: self.adapter_info.driver.clone(),
 898            driver_info: self.adapter_info.driver_info.clone(),
 899        }
 900    }
 901
 902    pub fn draw(&mut self, scene: &Scene) {
 903        self.atlas.before_frame();
 904
 905        let frame = match self.surface.get_current_texture() {
 906            Ok(frame) => frame,
 907            Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => {
 908                self.surface.configure(&self.device, &self.surface_config);
 909                return;
 910            }
 911            Err(e) => {
 912                log::error!("Failed to acquire surface texture: {e}");
 913                return;
 914            }
 915        };
 916        let frame_view = frame
 917            .texture
 918            .create_view(&wgpu::TextureViewDescriptor::default());
 919
 920        let gamma_params = GammaParams {
 921            gamma_ratios: self.rendering_params.gamma_ratios,
 922            grayscale_enhanced_contrast: self.rendering_params.grayscale_enhanced_contrast,
 923            subpixel_enhanced_contrast: self.rendering_params.subpixel_enhanced_contrast,
 924            _pad: [0.0; 2],
 925        };
 926
 927        let globals = GlobalParams {
 928            viewport_size: [
 929                self.surface_config.width as f32,
 930                self.surface_config.height as f32,
 931            ],
 932            premultiplied_alpha: if self.surface_config.alpha_mode
 933                == wgpu::CompositeAlphaMode::PreMultiplied
 934            {
 935                1
 936            } else {
 937                0
 938            },
 939            pad: 0,
 940        };
 941
 942        let path_globals = GlobalParams {
 943            premultiplied_alpha: 0,
 944            ..globals
 945        };
 946
 947        self.queue
 948            .write_buffer(&self.globals_buffer, 0, bytemuck::bytes_of(&globals));
 949        self.queue.write_buffer(
 950            &self.globals_buffer,
 951            self.path_globals_offset,
 952            bytemuck::bytes_of(&path_globals),
 953        );
 954        self.queue.write_buffer(
 955            &self.globals_buffer,
 956            self.gamma_offset,
 957            bytemuck::bytes_of(&gamma_params),
 958        );
 959
 960        loop {
 961            let mut instance_offset: u64 = 0;
 962            let mut overflow = false;
 963
 964            let mut encoder = self
 965                .device
 966                .create_command_encoder(&wgpu::CommandEncoderDescriptor {
 967                    label: Some("main_encoder"),
 968                });
 969
 970            {
 971                let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
 972                    label: Some("main_pass"),
 973                    color_attachments: &[Some(wgpu::RenderPassColorAttachment {
 974                        view: &frame_view,
 975                        resolve_target: None,
 976                        ops: wgpu::Operations {
 977                            load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
 978                            store: wgpu::StoreOp::Store,
 979                        },
 980                        depth_slice: None,
 981                    })],
 982                    depth_stencil_attachment: None,
 983                    ..Default::default()
 984                });
 985
 986                for batch in scene.batches() {
 987                    let ok = match batch {
 988                        PrimitiveBatch::Quads(range) => {
 989                            self.draw_quads(&scene.quads[range], &mut instance_offset, &mut pass)
 990                        }
 991                        PrimitiveBatch::Shadows(range) => self.draw_shadows(
 992                            &scene.shadows[range],
 993                            &mut instance_offset,
 994                            &mut pass,
 995                        ),
 996                        PrimitiveBatch::Paths(range) => {
 997                            let paths = &scene.paths[range];
 998                            if paths.is_empty() {
 999                                continue;
1000                            }
1001
1002                            drop(pass);
1003
1004                            let did_draw = self.draw_paths_to_intermediate(
1005                                &mut encoder,
1006                                paths,
1007                                &mut instance_offset,
1008                            );
1009
1010                            pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1011                                label: Some("main_pass_continued"),
1012                                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1013                                    view: &frame_view,
1014                                    resolve_target: None,
1015                                    ops: wgpu::Operations {
1016                                        load: wgpu::LoadOp::Load,
1017                                        store: wgpu::StoreOp::Store,
1018                                    },
1019                                    depth_slice: None,
1020                                })],
1021                                depth_stencil_attachment: None,
1022                                ..Default::default()
1023                            });
1024
1025                            if did_draw {
1026                                self.draw_paths_from_intermediate(
1027                                    paths,
1028                                    &mut instance_offset,
1029                                    &mut pass,
1030                                )
1031                            } else {
1032                                false
1033                            }
1034                        }
1035                        PrimitiveBatch::Underlines(range) => self.draw_underlines(
1036                            &scene.underlines[range],
1037                            &mut instance_offset,
1038                            &mut pass,
1039                        ),
1040                        PrimitiveBatch::MonochromeSprites { texture_id, range } => self
1041                            .draw_monochrome_sprites(
1042                                &scene.monochrome_sprites[range],
1043                                texture_id,
1044                                &mut instance_offset,
1045                                &mut pass,
1046                            ),
1047                        PrimitiveBatch::SubpixelSprites { texture_id, range } => self
1048                            .draw_subpixel_sprites(
1049                                &scene.subpixel_sprites[range],
1050                                texture_id,
1051                                &mut instance_offset,
1052                                &mut pass,
1053                            ),
1054                        PrimitiveBatch::PolychromeSprites { texture_id, range } => self
1055                            .draw_polychrome_sprites(
1056                                &scene.polychrome_sprites[range],
1057                                texture_id,
1058                                &mut instance_offset,
1059                                &mut pass,
1060                            ),
1061                        PrimitiveBatch::Surfaces(_surfaces) => {
1062                            // Surfaces are macOS-only for video playback
1063                            // Not implemented for Linux/wgpu
1064                            true
1065                        }
1066                    };
1067                    if !ok {
1068                        overflow = true;
1069                        break;
1070                    }
1071                }
1072            }
1073
1074            if overflow {
1075                drop(encoder);
1076                if self.instance_buffer_capacity >= self.max_buffer_size {
1077                    log::error!(
1078                        "instance buffer size grew too large: {}",
1079                        self.instance_buffer_capacity
1080                    );
1081                    frame.present();
1082                    return;
1083                }
1084                self.grow_instance_buffer();
1085                continue;
1086            }
1087
1088            self.queue.submit(std::iter::once(encoder.finish()));
1089            frame.present();
1090            return;
1091        }
1092    }
1093
1094    fn draw_quads(
1095        &self,
1096        quads: &[Quad],
1097        instance_offset: &mut u64,
1098        pass: &mut wgpu::RenderPass<'_>,
1099    ) -> bool {
1100        let data = unsafe { Self::instance_bytes(quads) };
1101        self.draw_instances(
1102            data,
1103            quads.len() as u32,
1104            &self.pipelines.quads,
1105            instance_offset,
1106            pass,
1107        )
1108    }
1109
1110    fn draw_shadows(
1111        &self,
1112        shadows: &[Shadow],
1113        instance_offset: &mut u64,
1114        pass: &mut wgpu::RenderPass<'_>,
1115    ) -> bool {
1116        let data = unsafe { Self::instance_bytes(shadows) };
1117        self.draw_instances(
1118            data,
1119            shadows.len() as u32,
1120            &self.pipelines.shadows,
1121            instance_offset,
1122            pass,
1123        )
1124    }
1125
1126    fn draw_underlines(
1127        &self,
1128        underlines: &[Underline],
1129        instance_offset: &mut u64,
1130        pass: &mut wgpu::RenderPass<'_>,
1131    ) -> bool {
1132        let data = unsafe { Self::instance_bytes(underlines) };
1133        self.draw_instances(
1134            data,
1135            underlines.len() as u32,
1136            &self.pipelines.underlines,
1137            instance_offset,
1138            pass,
1139        )
1140    }
1141
1142    fn draw_monochrome_sprites(
1143        &self,
1144        sprites: &[MonochromeSprite],
1145        texture_id: AtlasTextureId,
1146        instance_offset: &mut u64,
1147        pass: &mut wgpu::RenderPass<'_>,
1148    ) -> bool {
1149        let tex_info = self.atlas.get_texture_info(texture_id);
1150        let data = unsafe { Self::instance_bytes(sprites) };
1151        self.draw_instances_with_texture(
1152            data,
1153            sprites.len() as u32,
1154            &tex_info.view,
1155            &self.pipelines.mono_sprites,
1156            instance_offset,
1157            pass,
1158        )
1159    }
1160
1161    fn draw_subpixel_sprites(
1162        &self,
1163        sprites: &[SubpixelSprite],
1164        texture_id: AtlasTextureId,
1165        instance_offset: &mut u64,
1166        pass: &mut wgpu::RenderPass<'_>,
1167    ) -> bool {
1168        let tex_info = self.atlas.get_texture_info(texture_id);
1169        let data = unsafe { Self::instance_bytes(sprites) };
1170        let pipeline = self
1171            .pipelines
1172            .subpixel_sprites
1173            .as_ref()
1174            .unwrap_or(&self.pipelines.mono_sprites);
1175        self.draw_instances_with_texture(
1176            data,
1177            sprites.len() as u32,
1178            &tex_info.view,
1179            pipeline,
1180            instance_offset,
1181            pass,
1182        )
1183    }
1184
1185    fn draw_polychrome_sprites(
1186        &self,
1187        sprites: &[PolychromeSprite],
1188        texture_id: AtlasTextureId,
1189        instance_offset: &mut u64,
1190        pass: &mut wgpu::RenderPass<'_>,
1191    ) -> bool {
1192        let tex_info = self.atlas.get_texture_info(texture_id);
1193        let data = unsafe { Self::instance_bytes(sprites) };
1194        self.draw_instances_with_texture(
1195            data,
1196            sprites.len() as u32,
1197            &tex_info.view,
1198            &self.pipelines.poly_sprites,
1199            instance_offset,
1200            pass,
1201        )
1202    }
1203
1204    fn draw_instances(
1205        &self,
1206        data: &[u8],
1207        instance_count: u32,
1208        pipeline: &wgpu::RenderPipeline,
1209        instance_offset: &mut u64,
1210        pass: &mut wgpu::RenderPass<'_>,
1211    ) -> bool {
1212        if instance_count == 0 {
1213            return true;
1214        }
1215        let Some((offset, size)) = self.write_to_instance_buffer(instance_offset, data) else {
1216            return false;
1217        };
1218        let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1219            label: None,
1220            layout: &self.bind_group_layouts.instances,
1221            entries: &[wgpu::BindGroupEntry {
1222                binding: 0,
1223                resource: self.instance_binding(offset, size),
1224            }],
1225        });
1226        pass.set_pipeline(pipeline);
1227        pass.set_bind_group(0, &self.globals_bind_group, &[]);
1228        pass.set_bind_group(1, &bind_group, &[]);
1229        pass.draw(0..4, 0..instance_count);
1230        true
1231    }
1232
1233    fn draw_instances_with_texture(
1234        &self,
1235        data: &[u8],
1236        instance_count: u32,
1237        texture_view: &wgpu::TextureView,
1238        pipeline: &wgpu::RenderPipeline,
1239        instance_offset: &mut u64,
1240        pass: &mut wgpu::RenderPass<'_>,
1241    ) -> bool {
1242        if instance_count == 0 {
1243            return true;
1244        }
1245        let Some((offset, size)) = self.write_to_instance_buffer(instance_offset, data) else {
1246            return false;
1247        };
1248        let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1249            label: None,
1250            layout: &self.bind_group_layouts.instances_with_texture,
1251            entries: &[
1252                wgpu::BindGroupEntry {
1253                    binding: 0,
1254                    resource: self.instance_binding(offset, size),
1255                },
1256                wgpu::BindGroupEntry {
1257                    binding: 1,
1258                    resource: wgpu::BindingResource::TextureView(texture_view),
1259                },
1260                wgpu::BindGroupEntry {
1261                    binding: 2,
1262                    resource: wgpu::BindingResource::Sampler(&self.atlas_sampler),
1263                },
1264            ],
1265        });
1266        pass.set_pipeline(pipeline);
1267        pass.set_bind_group(0, &self.globals_bind_group, &[]);
1268        pass.set_bind_group(1, &bind_group, &[]);
1269        pass.draw(0..4, 0..instance_count);
1270        true
1271    }
1272
1273    unsafe fn instance_bytes<T>(instances: &[T]) -> &[u8] {
1274        unsafe {
1275            std::slice::from_raw_parts(
1276                instances.as_ptr() as *const u8,
1277                std::mem::size_of_val(instances),
1278            )
1279        }
1280    }
1281
1282    fn draw_paths_from_intermediate(
1283        &self,
1284        paths: &[Path<ScaledPixels>],
1285        instance_offset: &mut u64,
1286        pass: &mut wgpu::RenderPass<'_>,
1287    ) -> bool {
1288        let first_path = &paths[0];
1289        let sprites: Vec<PathSprite> = if paths.last().map(|p| &p.order) == Some(&first_path.order)
1290        {
1291            paths
1292                .iter()
1293                .map(|p| PathSprite {
1294                    bounds: p.clipped_bounds(),
1295                })
1296                .collect()
1297        } else {
1298            let mut bounds = first_path.clipped_bounds();
1299            for path in paths.iter().skip(1) {
1300                bounds = bounds.union(&path.clipped_bounds());
1301            }
1302            vec![PathSprite { bounds }]
1303        };
1304
1305        let sprite_data = unsafe { Self::instance_bytes(&sprites) };
1306        self.draw_instances_with_texture(
1307            sprite_data,
1308            sprites.len() as u32,
1309            &self.path_intermediate_view,
1310            &self.pipelines.paths,
1311            instance_offset,
1312            pass,
1313        )
1314    }
1315
1316    fn draw_paths_to_intermediate(
1317        &self,
1318        encoder: &mut wgpu::CommandEncoder,
1319        paths: &[Path<ScaledPixels>],
1320        instance_offset: &mut u64,
1321    ) -> bool {
1322        let mut vertices = Vec::new();
1323        for path in paths {
1324            let bounds = path.clipped_bounds();
1325            vertices.extend(path.vertices.iter().map(|v| PathRasterizationVertex {
1326                xy_position: v.xy_position,
1327                st_position: v.st_position,
1328                color: path.color,
1329                bounds,
1330            }));
1331        }
1332
1333        if vertices.is_empty() {
1334            return true;
1335        }
1336
1337        let vertex_data = unsafe { Self::instance_bytes(&vertices) };
1338        let Some((vertex_offset, vertex_size)) =
1339            self.write_to_instance_buffer(instance_offset, vertex_data)
1340        else {
1341            return false;
1342        };
1343
1344        let data_bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
1345            label: Some("path_rasterization_bind_group"),
1346            layout: &self.bind_group_layouts.instances,
1347            entries: &[wgpu::BindGroupEntry {
1348                binding: 0,
1349                resource: self.instance_binding(vertex_offset, vertex_size),
1350            }],
1351        });
1352
1353        let (target_view, resolve_target) = if let Some(ref msaa_view) = self.path_msaa_view {
1354            (msaa_view, Some(&self.path_intermediate_view))
1355        } else {
1356            (&self.path_intermediate_view, None)
1357        };
1358
1359        {
1360            let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
1361                label: Some("path_rasterization_pass"),
1362                color_attachments: &[Some(wgpu::RenderPassColorAttachment {
1363                    view: target_view,
1364                    resolve_target,
1365                    ops: wgpu::Operations {
1366                        load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
1367                        store: wgpu::StoreOp::Store,
1368                    },
1369                    depth_slice: None,
1370                })],
1371                depth_stencil_attachment: None,
1372                ..Default::default()
1373            });
1374
1375            pass.set_pipeline(&self.pipelines.path_rasterization);
1376            pass.set_bind_group(0, &self.path_globals_bind_group, &[]);
1377            pass.set_bind_group(1, &data_bind_group, &[]);
1378            pass.draw(0..vertices.len() as u32, 0..1);
1379        }
1380
1381        true
1382    }
1383
1384    fn grow_instance_buffer(&mut self) {
1385        let new_capacity = (self.instance_buffer_capacity * 2).min(self.max_buffer_size);
1386        log::info!("increased instance buffer size to {}", new_capacity);
1387        self.instance_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
1388            label: Some("instance_buffer"),
1389            size: new_capacity,
1390            usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
1391            mapped_at_creation: false,
1392        });
1393        self.instance_buffer_capacity = new_capacity;
1394    }
1395
1396    fn write_to_instance_buffer(
1397        &self,
1398        instance_offset: &mut u64,
1399        data: &[u8],
1400    ) -> Option<(u64, NonZeroU64)> {
1401        let offset = (*instance_offset).next_multiple_of(self.storage_buffer_alignment);
1402        let size = (data.len() as u64).max(16);
1403        if offset + size > self.instance_buffer_capacity {
1404            return None;
1405        }
1406        self.queue.write_buffer(&self.instance_buffer, offset, data);
1407        *instance_offset = offset + size;
1408        Some((offset, NonZeroU64::new(size).expect("size is at least 16")))
1409    }
1410
1411    fn instance_binding(&self, offset: u64, size: NonZeroU64) -> wgpu::BindingResource<'_> {
1412        wgpu::BindingResource::Buffer(wgpu::BufferBinding {
1413            buffer: &self.instance_buffer,
1414            offset,
1415            size: Some(size),
1416        })
1417    }
1418
1419    pub fn destroy(&mut self) {
1420        // wgpu resources are automatically cleaned up when dropped
1421    }
1422}
1423
1424struct RenderingParameters {
1425    path_sample_count: u32,
1426    gamma_ratios: [f32; 4],
1427    grayscale_enhanced_contrast: f32,
1428    subpixel_enhanced_contrast: f32,
1429}
1430
1431impl RenderingParameters {
1432    fn new(adapter: &wgpu::Adapter, surface_format: wgpu::TextureFormat) -> Self {
1433        use std::env;
1434
1435        let format_features = adapter.get_texture_format_features(surface_format);
1436        let path_sample_count = [4, 2, 1]
1437            .into_iter()
1438            .find(|&n| format_features.flags.sample_count_supported(n))
1439            .unwrap_or(1);
1440
1441        let gamma = env::var("ZED_FONTS_GAMMA")
1442            .ok()
1443            .and_then(|v| v.parse().ok())
1444            .unwrap_or(1.8_f32)
1445            .clamp(1.0, 2.2);
1446        let gamma_ratios = get_gamma_correction_ratios(gamma);
1447
1448        let grayscale_enhanced_contrast = env::var("ZED_FONTS_GRAYSCALE_ENHANCED_CONTRAST")
1449            .ok()
1450            .and_then(|v| v.parse().ok())
1451            .unwrap_or(1.0_f32)
1452            .max(0.0);
1453
1454        let subpixel_enhanced_contrast = env::var("ZED_FONTS_SUBPIXEL_ENHANCED_CONTRAST")
1455            .ok()
1456            .and_then(|v| v.parse().ok())
1457            .unwrap_or(0.5_f32)
1458            .max(0.0);
1459
1460        Self {
1461            path_sample_count,
1462            gamma_ratios,
1463            grayscale_enhanced_contrast,
1464            subpixel_enhanced_contrast,
1465        }
1466    }
1467}