blade_renderer.rs

  1// Doing `if let` gives you nice scoping with passes/encoders
  2#![allow(irrefutable_let_patterns)]
  3
  4use super::{BladeAtlas, BladeContext};
  5use crate::{
  6    Background, Bounds, DevicePixels, GpuSpecs, MonochromeSprite, Path, Point, PolychromeSprite,
  7    PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, Underline,
  8};
  9use blade_graphics as gpu;
 10use blade_util::{BufferBelt, BufferBeltDescriptor};
 11use bytemuck::{Pod, Zeroable};
 12#[cfg(target_os = "macos")]
 13use media::core_video::CVMetalTextureCache;
 14use std::sync::Arc;
 15
 16const MAX_FRAME_TIME_MS: u32 = 10000;
 17
 18#[repr(C)]
 19#[derive(Clone, Copy, Pod, Zeroable)]
 20struct GlobalParams {
 21    viewport_size: [f32; 2],
 22    premultiplied_alpha: u32,
 23    pad: u32,
 24}
 25
 26//Note: we can't use `Bounds` directly here because
 27// it doesn't implement Pod + Zeroable
 28#[repr(C)]
 29#[derive(Clone, Copy, Pod, Zeroable)]
 30struct PodBounds {
 31    origin: [f32; 2],
 32    size: [f32; 2],
 33}
 34
 35impl From<Bounds<ScaledPixels>> for PodBounds {
 36    fn from(bounds: Bounds<ScaledPixels>) -> Self {
 37        Self {
 38            origin: [bounds.origin.x.0, bounds.origin.y.0],
 39            size: [bounds.size.width.0, bounds.size.height.0],
 40        }
 41    }
 42}
 43
 44#[repr(C)]
 45#[derive(Clone, Copy, Pod, Zeroable)]
 46struct SurfaceParams {
 47    bounds: PodBounds,
 48    content_mask: PodBounds,
 49}
 50
 51#[derive(blade_macros::ShaderData)]
 52struct ShaderQuadsData {
 53    globals: GlobalParams,
 54    b_quads: gpu::BufferPiece,
 55}
 56
 57#[derive(blade_macros::ShaderData)]
 58struct ShaderShadowsData {
 59    globals: GlobalParams,
 60    b_shadows: gpu::BufferPiece,
 61}
 62
 63#[derive(blade_macros::ShaderData)]
 64struct ShaderPathRasterizationData {
 65    globals: GlobalParams,
 66    b_path_vertices: gpu::BufferPiece,
 67}
 68
 69#[derive(blade_macros::ShaderData)]
 70struct ShaderPathsData {
 71    globals: GlobalParams,
 72    t_sprite: gpu::TextureView,
 73    s_sprite: gpu::Sampler,
 74    b_path_sprites: gpu::BufferPiece,
 75}
 76
 77#[derive(blade_macros::ShaderData)]
 78struct ShaderUnderlinesData {
 79    globals: GlobalParams,
 80    b_underlines: gpu::BufferPiece,
 81}
 82
 83#[derive(blade_macros::ShaderData)]
 84struct ShaderMonoSpritesData {
 85    globals: GlobalParams,
 86    t_sprite: gpu::TextureView,
 87    s_sprite: gpu::Sampler,
 88    b_mono_sprites: gpu::BufferPiece,
 89}
 90
 91#[derive(blade_macros::ShaderData)]
 92struct ShaderPolySpritesData {
 93    globals: GlobalParams,
 94    t_sprite: gpu::TextureView,
 95    s_sprite: gpu::Sampler,
 96    b_poly_sprites: gpu::BufferPiece,
 97}
 98
 99#[derive(blade_macros::ShaderData)]
100struct ShaderSurfacesData {
101    globals: GlobalParams,
102    surface_locals: SurfaceParams,
103    t_y: gpu::TextureView,
104    t_cb_cr: gpu::TextureView,
105    s_surface: gpu::Sampler,
106}
107
108#[derive(Clone, Debug, Eq, PartialEq)]
109#[repr(C)]
110struct PathSprite {
111    bounds: Bounds<ScaledPixels>,
112}
113
114#[derive(Clone, Debug)]
115#[repr(C)]
116struct PathRasterizationVertex {
117    xy_position: Point<ScaledPixels>,
118    st_position: Point<f32>,
119    color: Background,
120    bounds: Bounds<ScaledPixels>,
121}
122
123struct BladePipelines {
124    quads: gpu::RenderPipeline,
125    shadows: gpu::RenderPipeline,
126    path_rasterization: gpu::RenderPipeline,
127    paths: gpu::RenderPipeline,
128    underlines: gpu::RenderPipeline,
129    mono_sprites: gpu::RenderPipeline,
130    poly_sprites: gpu::RenderPipeline,
131    surfaces: gpu::RenderPipeline,
132}
133
134impl BladePipelines {
135    fn new(gpu: &gpu::Context, surface_info: gpu::SurfaceInfo, path_sample_count: u32) -> Self {
136        use gpu::ShaderData as _;
137
138        log::info!(
139            "Initializing Blade pipelines for surface {:?}",
140            surface_info
141        );
142        let shader = gpu.create_shader(gpu::ShaderDesc {
143            source: include_str!("shaders.wgsl"),
144        });
145        shader.check_struct_size::<GlobalParams>();
146        shader.check_struct_size::<SurfaceParams>();
147        shader.check_struct_size::<Quad>();
148        shader.check_struct_size::<Shadow>();
149        shader.check_struct_size::<PathRasterizationVertex>();
150        shader.check_struct_size::<PathSprite>();
151        shader.check_struct_size::<Underline>();
152        shader.check_struct_size::<MonochromeSprite>();
153        shader.check_struct_size::<PolychromeSprite>();
154
155        // See https://apoorvaj.io/alpha-compositing-opengl-blending-and-premultiplied-alpha/
156        let blend_mode = match surface_info.alpha {
157            gpu::AlphaMode::Ignored => gpu::BlendState::ALPHA_BLENDING,
158            gpu::AlphaMode::PreMultiplied => gpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING,
159            gpu::AlphaMode::PostMultiplied => gpu::BlendState::ALPHA_BLENDING,
160        };
161        let color_targets = &[gpu::ColorTargetState {
162            format: surface_info.format,
163            blend: Some(blend_mode),
164            write_mask: gpu::ColorWrites::default(),
165        }];
166
167        Self {
168            quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
169                name: "quads",
170                data_layouts: &[&ShaderQuadsData::layout()],
171                vertex: shader.at("vs_quad"),
172                vertex_fetches: &[],
173                primitive: gpu::PrimitiveState {
174                    topology: gpu::PrimitiveTopology::TriangleStrip,
175                    ..Default::default()
176                },
177                depth_stencil: None,
178                fragment: Some(shader.at("fs_quad")),
179                color_targets,
180                multisample_state: gpu::MultisampleState::default(),
181            }),
182            shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
183                name: "shadows",
184                data_layouts: &[&ShaderShadowsData::layout()],
185                vertex: shader.at("vs_shadow"),
186                vertex_fetches: &[],
187                primitive: gpu::PrimitiveState {
188                    topology: gpu::PrimitiveTopology::TriangleStrip,
189                    ..Default::default()
190                },
191                depth_stencil: None,
192                fragment: Some(shader.at("fs_shadow")),
193                color_targets,
194                multisample_state: gpu::MultisampleState::default(),
195            }),
196            path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
197                name: "path_rasterization",
198                data_layouts: &[&ShaderPathRasterizationData::layout()],
199                vertex: shader.at("vs_path_rasterization"),
200                vertex_fetches: &[],
201                primitive: gpu::PrimitiveState {
202                    topology: gpu::PrimitiveTopology::TriangleList,
203                    ..Default::default()
204                },
205                depth_stencil: None,
206                fragment: Some(shader.at("fs_path_rasterization")),
207                // The original implementation was using ADDITIVE blende mode,
208                // I don't know why
209                // color_targets: &[gpu::ColorTargetState {
210                //     format: PATH_TEXTURE_FORMAT,
211                //     blend: Some(gpu::BlendState::ADDITIVE),
212                //     write_mask: gpu::ColorWrites::default(),
213                // }],
214                color_targets: &[gpu::ColorTargetState {
215                    format: surface_info.format,
216                    blend: Some(gpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING),
217                    write_mask: gpu::ColorWrites::default(),
218                }],
219                multisample_state: gpu::MultisampleState {
220                    sample_count: path_sample_count,
221                    ..Default::default()
222                },
223            }),
224            paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
225                name: "paths",
226                data_layouts: &[&ShaderPathsData::layout()],
227                vertex: shader.at("vs_path"),
228                vertex_fetches: &[],
229                primitive: gpu::PrimitiveState {
230                    topology: gpu::PrimitiveTopology::TriangleStrip,
231                    ..Default::default()
232                },
233                depth_stencil: None,
234                fragment: Some(shader.at("fs_path")),
235                color_targets: &[gpu::ColorTargetState {
236                    format: surface_info.format,
237                    blend: Some(gpu::BlendState {
238                        color: gpu::BlendComponent::OVER,
239                        alpha: gpu::BlendComponent::ADDITIVE,
240                    }),
241                    write_mask: gpu::ColorWrites::default(),
242                }],
243                multisample_state: gpu::MultisampleState::default(),
244            }),
245            underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
246                name: "underlines",
247                data_layouts: &[&ShaderUnderlinesData::layout()],
248                vertex: shader.at("vs_underline"),
249                vertex_fetches: &[],
250                primitive: gpu::PrimitiveState {
251                    topology: gpu::PrimitiveTopology::TriangleStrip,
252                    ..Default::default()
253                },
254                depth_stencil: None,
255                fragment: Some(shader.at("fs_underline")),
256                color_targets,
257                multisample_state: gpu::MultisampleState::default(),
258            }),
259            mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
260                name: "mono-sprites",
261                data_layouts: &[&ShaderMonoSpritesData::layout()],
262                vertex: shader.at("vs_mono_sprite"),
263                vertex_fetches: &[],
264                primitive: gpu::PrimitiveState {
265                    topology: gpu::PrimitiveTopology::TriangleStrip,
266                    ..Default::default()
267                },
268                depth_stencil: None,
269                fragment: Some(shader.at("fs_mono_sprite")),
270                color_targets,
271                multisample_state: gpu::MultisampleState::default(),
272            }),
273            poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
274                name: "poly-sprites",
275                data_layouts: &[&ShaderPolySpritesData::layout()],
276                vertex: shader.at("vs_poly_sprite"),
277                vertex_fetches: &[],
278                primitive: gpu::PrimitiveState {
279                    topology: gpu::PrimitiveTopology::TriangleStrip,
280                    ..Default::default()
281                },
282                depth_stencil: None,
283                fragment: Some(shader.at("fs_poly_sprite")),
284                color_targets,
285                multisample_state: gpu::MultisampleState::default(),
286            }),
287            surfaces: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
288                name: "surfaces",
289                data_layouts: &[&ShaderSurfacesData::layout()],
290                vertex: shader.at("vs_surface"),
291                vertex_fetches: &[],
292                primitive: gpu::PrimitiveState {
293                    topology: gpu::PrimitiveTopology::TriangleStrip,
294                    ..Default::default()
295                },
296                depth_stencil: None,
297                fragment: Some(shader.at("fs_surface")),
298                color_targets,
299                multisample_state: gpu::MultisampleState::default(),
300            }),
301        }
302    }
303
304    fn destroy(&mut self, gpu: &gpu::Context) {
305        gpu.destroy_render_pipeline(&mut self.quads);
306        gpu.destroy_render_pipeline(&mut self.shadows);
307        gpu.destroy_render_pipeline(&mut self.path_rasterization);
308        gpu.destroy_render_pipeline(&mut self.paths);
309        gpu.destroy_render_pipeline(&mut self.underlines);
310        gpu.destroy_render_pipeline(&mut self.mono_sprites);
311        gpu.destroy_render_pipeline(&mut self.poly_sprites);
312        gpu.destroy_render_pipeline(&mut self.surfaces);
313    }
314}
315
316pub struct BladeSurfaceConfig {
317    pub size: gpu::Extent,
318    pub transparent: bool,
319}
320
321//Note: we could see some of these fields moved into `BladeContext`
322// so that they are shared between windows. E.g. `pipelines`.
323// But that is complicated by the fact that pipelines depend on
324// the format and alpha mode.
325pub struct BladeRenderer {
326    gpu: Arc<gpu::Context>,
327    surface: gpu::Surface,
328    surface_config: gpu::SurfaceConfig,
329    command_encoder: gpu::CommandEncoder,
330    last_sync_point: Option<gpu::SyncPoint>,
331    pipelines: BladePipelines,
332    instance_belt: BufferBelt,
333    atlas: Arc<BladeAtlas>,
334    atlas_sampler: gpu::Sampler,
335    #[cfg(target_os = "macos")]
336    core_video_texture_cache: CVMetalTextureCache,
337    path_sample_count: u32,
338    path_intermediate_texture: gpu::Texture,
339    path_intermediate_texture_view: gpu::TextureView,
340    path_intermediate_msaa_texture: Option<gpu::Texture>,
341    path_intermediate_msaa_texture_view: Option<gpu::TextureView>,
342}
343
344impl BladeRenderer {
345    pub fn new<I: raw_window_handle::HasWindowHandle + raw_window_handle::HasDisplayHandle>(
346        context: &BladeContext,
347        window: &I,
348        config: BladeSurfaceConfig,
349    ) -> anyhow::Result<Self> {
350        let surface_config = gpu::SurfaceConfig {
351            size: config.size,
352            usage: gpu::TextureUsage::TARGET,
353            display_sync: gpu::DisplaySync::Recent,
354            color_space: gpu::ColorSpace::Linear,
355            allow_exclusive_full_screen: false,
356            transparent: config.transparent,
357        };
358        let surface = context
359            .gpu
360            .create_surface_configured(window, surface_config)
361            .map_err(|err| anyhow::anyhow!("Failed to create surface: {err:?}"))?;
362
363        let command_encoder = context.gpu.create_command_encoder(gpu::CommandEncoderDesc {
364            name: "main",
365            buffer_count: 2,
366        });
367        // workaround for https://github.com/zed-industries/zed/issues/26143
368        let path_sample_count = std::env::var("ZED_PATH_SAMPLE_COUNT")
369            .ok()
370            .and_then(|v| v.parse().ok())
371            .or_else(|| {
372                [4, 2, 1]
373                    .into_iter()
374                    .find(|count| context.gpu.supports_texture_sample_count(*count))
375            })
376            .unwrap_or(1);
377        let pipelines = BladePipelines::new(&context.gpu, surface.info(), path_sample_count);
378        let instance_belt = BufferBelt::new(BufferBeltDescriptor {
379            memory: gpu::Memory::Shared,
380            min_chunk_size: 0x1000,
381            alignment: 0x40, // Vulkan `minStorageBufferOffsetAlignment` on Intel Xe
382        });
383        let atlas = Arc::new(BladeAtlas::new(&context.gpu));
384        let atlas_sampler = context.gpu.create_sampler(gpu::SamplerDesc {
385            name: "path rasterization sampler",
386            mag_filter: gpu::FilterMode::Linear,
387            min_filter: gpu::FilterMode::Linear,
388            ..Default::default()
389        });
390
391        let (path_intermediate_texture, path_intermediate_texture_view) =
392            create_path_intermediate_texture(
393                &context.gpu,
394                surface.info().format,
395                config.size.width,
396                config.size.height,
397            );
398        let (path_intermediate_msaa_texture, path_intermediate_msaa_texture_view) =
399            create_msaa_texture_if_needed(
400                &context.gpu,
401                surface.info().format,
402                config.size.width,
403                config.size.height,
404                path_sample_count,
405            )
406            .unzip();
407
408        #[cfg(target_os = "macos")]
409        let core_video_texture_cache = unsafe {
410            CVMetalTextureCache::new(
411                objc2::rc::Retained::as_ptr(&context.gpu.metal_device()) as *mut _
412            )
413            .unwrap()
414        };
415
416        Ok(Self {
417            gpu: Arc::clone(&context.gpu),
418            surface,
419            surface_config,
420            command_encoder,
421            last_sync_point: None,
422            pipelines,
423            instance_belt,
424            atlas,
425            atlas_sampler,
426            #[cfg(target_os = "macos")]
427            core_video_texture_cache,
428            path_sample_count,
429            path_intermediate_texture,
430            path_intermediate_texture_view,
431            path_intermediate_msaa_texture,
432            path_intermediate_msaa_texture_view,
433        })
434    }
435
436    fn wait_for_gpu(&mut self) {
437        if let Some(last_sp) = self.last_sync_point.take()
438            && !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {
439                log::error!("GPU hung");
440                #[cfg(target_os = "linux")]
441                if self.gpu.device_information().driver_name == "radv" {
442                    log::error!(
443                        "there's a known bug with amdgpu/radv, try setting ZED_PATH_SAMPLE_COUNT=0 as a workaround"
444                    );
445                    log::error!(
446                        "if that helps you're running into https://github.com/zed-industries/zed/issues/26143"
447                    );
448                }
449                log::error!(
450                    "your device information is: {:?}",
451                    self.gpu.device_information()
452                );
453                while !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {}
454            }
455    }
456
457    pub fn update_drawable_size(&mut self, size: Size<DevicePixels>) {
458        self.update_drawable_size_impl(size, false);
459    }
460
461    /// Like `update_drawable_size` but skips the check that the size has changed. This is useful in
462    /// cases like restoring a window from minimization where the size is the same but the
463    /// renderer's swap chain needs to be recreated.
464    #[cfg_attr(
465        any(target_os = "macos", target_os = "linux", target_os = "freebsd"),
466        allow(dead_code)
467    )]
468    pub fn update_drawable_size_even_if_unchanged(&mut self, size: Size<DevicePixels>) {
469        self.update_drawable_size_impl(size, true);
470    }
471
472    fn update_drawable_size_impl(&mut self, size: Size<DevicePixels>, always_resize: bool) {
473        let gpu_size = gpu::Extent {
474            width: size.width.0 as u32,
475            height: size.height.0 as u32,
476            depth: 1,
477        };
478
479        if always_resize || gpu_size != self.surface_config.size {
480            self.wait_for_gpu();
481            self.surface_config.size = gpu_size;
482            self.gpu
483                .reconfigure_surface(&mut self.surface, self.surface_config);
484            self.gpu.destroy_texture(self.path_intermediate_texture);
485            self.gpu
486                .destroy_texture_view(self.path_intermediate_texture_view);
487            if let Some(msaa_texture) = self.path_intermediate_msaa_texture {
488                self.gpu.destroy_texture(msaa_texture);
489            }
490            if let Some(msaa_view) = self.path_intermediate_msaa_texture_view {
491                self.gpu.destroy_texture_view(msaa_view);
492            }
493            let (path_intermediate_texture, path_intermediate_texture_view) =
494                create_path_intermediate_texture(
495                    &self.gpu,
496                    self.surface.info().format,
497                    gpu_size.width,
498                    gpu_size.height,
499                );
500            self.path_intermediate_texture = path_intermediate_texture;
501            self.path_intermediate_texture_view = path_intermediate_texture_view;
502            let (path_intermediate_msaa_texture, path_intermediate_msaa_texture_view) =
503                create_msaa_texture_if_needed(
504                    &self.gpu,
505                    self.surface.info().format,
506                    gpu_size.width,
507                    gpu_size.height,
508                    self.path_sample_count,
509                )
510                .unzip();
511            self.path_intermediate_msaa_texture = path_intermediate_msaa_texture;
512            self.path_intermediate_msaa_texture_view = path_intermediate_msaa_texture_view;
513        }
514    }
515
516    pub fn update_transparency(&mut self, transparent: bool) {
517        if transparent != self.surface_config.transparent {
518            self.wait_for_gpu();
519            self.surface_config.transparent = transparent;
520            self.gpu
521                .reconfigure_surface(&mut self.surface, self.surface_config);
522            self.pipelines.destroy(&self.gpu);
523            self.pipelines =
524                BladePipelines::new(&self.gpu, self.surface.info(), self.path_sample_count);
525        }
526    }
527
528    #[cfg_attr(
529        any(target_os = "macos", feature = "wayland", target_os = "windows"),
530        allow(dead_code)
531    )]
532    pub fn viewport_size(&self) -> gpu::Extent {
533        self.surface_config.size
534    }
535
536    pub fn sprite_atlas(&self) -> &Arc<BladeAtlas> {
537        &self.atlas
538    }
539
540    #[cfg_attr(target_os = "macos", allow(dead_code))]
541    pub fn gpu_specs(&self) -> GpuSpecs {
542        let info = self.gpu.device_information();
543
544        GpuSpecs {
545            is_software_emulated: info.is_software_emulated,
546            device_name: info.device_name.clone(),
547            driver_name: info.driver_name.clone(),
548            driver_info: info.driver_info.clone(),
549        }
550    }
551
552    #[cfg(target_os = "macos")]
553    pub fn layer(&self) -> metal::MetalLayer {
554        unsafe { foreign_types::ForeignType::from_ptr(self.layer_ptr()) }
555    }
556
557    #[cfg(target_os = "macos")]
558    pub fn layer_ptr(&self) -> *mut metal::CAMetalLayer {
559        objc2::rc::Retained::as_ptr(&self.surface.metal_layer()) as *mut _
560    }
561
562    #[profiling::function]
563    fn draw_paths_to_intermediate(
564        &mut self,
565        paths: &[Path<ScaledPixels>],
566        width: f32,
567        height: f32,
568    ) {
569        self.command_encoder
570            .init_texture(self.path_intermediate_texture);
571        if let Some(msaa_texture) = self.path_intermediate_msaa_texture {
572            self.command_encoder.init_texture(msaa_texture);
573        }
574
575        let target = if let Some(msaa_view) = self.path_intermediate_msaa_texture_view {
576            gpu::RenderTarget {
577                view: msaa_view,
578                init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
579                finish_op: gpu::FinishOp::ResolveTo(self.path_intermediate_texture_view),
580            }
581        } else {
582            gpu::RenderTarget {
583                view: self.path_intermediate_texture_view,
584                init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
585                finish_op: gpu::FinishOp::Store,
586            }
587        };
588        if let mut pass = self.command_encoder.render(
589            "rasterize paths",
590            gpu::RenderTargetSet {
591                colors: &[target],
592                depth_stencil: None,
593            },
594        ) {
595            let globals = GlobalParams {
596                viewport_size: [width, height],
597                premultiplied_alpha: 0,
598                pad: 0,
599            };
600            let mut encoder = pass.with(&self.pipelines.path_rasterization);
601
602            let mut vertices = Vec::new();
603            for path in paths {
604                vertices.extend(path.vertices.iter().map(|v| PathRasterizationVertex {
605                    xy_position: v.xy_position,
606                    st_position: v.st_position,
607                    color: path.color,
608                    bounds: path.clipped_bounds(),
609                }));
610            }
611            let vertex_buf = unsafe { self.instance_belt.alloc_typed(&vertices, &self.gpu) };
612            encoder.bind(
613                0,
614                &ShaderPathRasterizationData {
615                    globals,
616                    b_path_vertices: vertex_buf,
617                },
618            );
619            encoder.draw(0, vertices.len() as u32, 0, 1);
620        }
621    }
622
623    pub fn destroy(&mut self) {
624        self.wait_for_gpu();
625        self.atlas.destroy();
626        self.gpu.destroy_sampler(self.atlas_sampler);
627        self.instance_belt.destroy(&self.gpu);
628        self.gpu.destroy_command_encoder(&mut self.command_encoder);
629        self.pipelines.destroy(&self.gpu);
630        self.gpu.destroy_surface(&mut self.surface);
631        self.gpu.destroy_texture(self.path_intermediate_texture);
632        self.gpu
633            .destroy_texture_view(self.path_intermediate_texture_view);
634        if let Some(msaa_texture) = self.path_intermediate_msaa_texture {
635            self.gpu.destroy_texture(msaa_texture);
636        }
637        if let Some(msaa_view) = self.path_intermediate_msaa_texture_view {
638            self.gpu.destroy_texture_view(msaa_view);
639        }
640    }
641
642    pub fn draw(&mut self, scene: &Scene) {
643        self.command_encoder.start();
644        self.atlas.before_frame(&mut self.command_encoder);
645
646        let frame = {
647            profiling::scope!("acquire frame");
648            self.surface.acquire_frame()
649        };
650        self.command_encoder.init_texture(frame.texture());
651
652        let globals = GlobalParams {
653            viewport_size: [
654                self.surface_config.size.width as f32,
655                self.surface_config.size.height as f32,
656            ],
657            premultiplied_alpha: match self.surface.info().alpha {
658                gpu::AlphaMode::Ignored | gpu::AlphaMode::PostMultiplied => 0,
659                gpu::AlphaMode::PreMultiplied => 1,
660            },
661            pad: 0,
662        };
663
664        let mut pass = self.command_encoder.render(
665            "main",
666            gpu::RenderTargetSet {
667                colors: &[gpu::RenderTarget {
668                    view: frame.texture_view(),
669                    init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
670                    finish_op: gpu::FinishOp::Store,
671                }],
672                depth_stencil: None,
673            },
674        );
675
676        profiling::scope!("render pass");
677        for batch in scene.batches() {
678            match batch {
679                PrimitiveBatch::Quads(quads) => {
680                    let instance_buf = unsafe { self.instance_belt.alloc_typed(quads, &self.gpu) };
681                    let mut encoder = pass.with(&self.pipelines.quads);
682                    encoder.bind(
683                        0,
684                        &ShaderQuadsData {
685                            globals,
686                            b_quads: instance_buf,
687                        },
688                    );
689                    encoder.draw(0, 4, 0, quads.len() as u32);
690                }
691                PrimitiveBatch::Shadows(shadows) => {
692                    let instance_buf =
693                        unsafe { self.instance_belt.alloc_typed(shadows, &self.gpu) };
694                    let mut encoder = pass.with(&self.pipelines.shadows);
695                    encoder.bind(
696                        0,
697                        &ShaderShadowsData {
698                            globals,
699                            b_shadows: instance_buf,
700                        },
701                    );
702                    encoder.draw(0, 4, 0, shadows.len() as u32);
703                }
704                PrimitiveBatch::Paths(paths) => {
705                    let Some(first_path) = paths.first() else {
706                        continue;
707                    };
708                    drop(pass);
709                    self.draw_paths_to_intermediate(
710                        paths,
711                        self.surface_config.size.width as f32,
712                        self.surface_config.size.height as f32,
713                    );
714                    pass = self.command_encoder.render(
715                        "main",
716                        gpu::RenderTargetSet {
717                            colors: &[gpu::RenderTarget {
718                                view: frame.texture_view(),
719                                init_op: gpu::InitOp::Load,
720                                finish_op: gpu::FinishOp::Store,
721                            }],
722                            depth_stencil: None,
723                        },
724                    );
725                    let mut encoder = pass.with(&self.pipelines.paths);
726                    // When copying paths from the intermediate texture to the drawable,
727                    // each pixel must only be copied once, in case of transparent paths.
728                    //
729                    // If all paths have the same draw order, then their bounds are all
730                    // disjoint, so we can copy each path's bounds individually. If this
731                    // batch combines different draw orders, we perform a single copy
732                    // for a minimal spanning rect.
733                    let sprites = if paths.last().unwrap().order == first_path.order {
734                        paths
735                            .iter()
736                            .map(|path| PathSprite {
737                                bounds: path.clipped_bounds(),
738                            })
739                            .collect()
740                    } else {
741                        let mut bounds = first_path.clipped_bounds();
742                        for path in paths.iter().skip(1) {
743                            bounds = bounds.union(&path.clipped_bounds());
744                        }
745                        vec![PathSprite { bounds }]
746                    };
747                    let instance_buf =
748                        unsafe { self.instance_belt.alloc_typed(&sprites, &self.gpu) };
749                    encoder.bind(
750                        0,
751                        &ShaderPathsData {
752                            globals,
753                            t_sprite: self.path_intermediate_texture_view,
754                            s_sprite: self.atlas_sampler,
755                            b_path_sprites: instance_buf,
756                        },
757                    );
758                    encoder.draw(0, 4, 0, sprites.len() as u32);
759                }
760                PrimitiveBatch::Underlines(underlines) => {
761                    let instance_buf =
762                        unsafe { self.instance_belt.alloc_typed(underlines, &self.gpu) };
763                    let mut encoder = pass.with(&self.pipelines.underlines);
764                    encoder.bind(
765                        0,
766                        &ShaderUnderlinesData {
767                            globals,
768                            b_underlines: instance_buf,
769                        },
770                    );
771                    encoder.draw(0, 4, 0, underlines.len() as u32);
772                }
773                PrimitiveBatch::MonochromeSprites {
774                    texture_id,
775                    sprites,
776                } => {
777                    let tex_info = self.atlas.get_texture_info(texture_id);
778                    let instance_buf =
779                        unsafe { self.instance_belt.alloc_typed(sprites, &self.gpu) };
780                    let mut encoder = pass.with(&self.pipelines.mono_sprites);
781                    encoder.bind(
782                        0,
783                        &ShaderMonoSpritesData {
784                            globals,
785                            t_sprite: tex_info.raw_view,
786                            s_sprite: self.atlas_sampler,
787                            b_mono_sprites: instance_buf,
788                        },
789                    );
790                    encoder.draw(0, 4, 0, sprites.len() as u32);
791                }
792                PrimitiveBatch::PolychromeSprites {
793                    texture_id,
794                    sprites,
795                } => {
796                    let tex_info = self.atlas.get_texture_info(texture_id);
797                    let instance_buf =
798                        unsafe { self.instance_belt.alloc_typed(sprites, &self.gpu) };
799                    let mut encoder = pass.with(&self.pipelines.poly_sprites);
800                    encoder.bind(
801                        0,
802                        &ShaderPolySpritesData {
803                            globals,
804                            t_sprite: tex_info.raw_view,
805                            s_sprite: self.atlas_sampler,
806                            b_poly_sprites: instance_buf,
807                        },
808                    );
809                    encoder.draw(0, 4, 0, sprites.len() as u32);
810                }
811                PrimitiveBatch::Surfaces(surfaces) => {
812                    let mut _encoder = pass.with(&self.pipelines.surfaces);
813
814                    for surface in surfaces {
815                        #[cfg(not(target_os = "macos"))]
816                        {
817                            let _ = surface;
818                            continue;
819                        };
820
821                        #[cfg(target_os = "macos")]
822                        {
823                            let (t_y, t_cb_cr) = unsafe {
824                                use core_foundation::base::TCFType as _;
825                                use std::ptr;
826
827                                assert_eq!(
828                                        surface.image_buffer.get_pixel_format(),
829                                        core_video::pixel_buffer::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
830                                    );
831
832                                let y_texture = self
833                                    .core_video_texture_cache
834                                    .create_texture_from_image(
835                                        surface.image_buffer.as_concrete_TypeRef(),
836                                        ptr::null(),
837                                        metal::MTLPixelFormat::R8Unorm,
838                                        surface.image_buffer.get_width_of_plane(0),
839                                        surface.image_buffer.get_height_of_plane(0),
840                                        0,
841                                    )
842                                    .unwrap();
843                                let cb_cr_texture = self
844                                    .core_video_texture_cache
845                                    .create_texture_from_image(
846                                        surface.image_buffer.as_concrete_TypeRef(),
847                                        ptr::null(),
848                                        metal::MTLPixelFormat::RG8Unorm,
849                                        surface.image_buffer.get_width_of_plane(1),
850                                        surface.image_buffer.get_height_of_plane(1),
851                                        1,
852                                    )
853                                    .unwrap();
854                                (
855                                    gpu::TextureView::from_metal_texture(
856                                        &objc2::rc::Retained::retain(
857                                            foreign_types::ForeignTypeRef::as_ptr(
858                                                y_texture.as_texture_ref(),
859                                            )
860                                                as *mut objc2::runtime::ProtocolObject<
861                                                    dyn objc2_metal::MTLTexture,
862                                                >,
863                                        )
864                                        .unwrap(),
865                                        gpu::TexelAspects::COLOR,
866                                    ),
867                                    gpu::TextureView::from_metal_texture(
868                                        &objc2::rc::Retained::retain(
869                                            foreign_types::ForeignTypeRef::as_ptr(
870                                                cb_cr_texture.as_texture_ref(),
871                                            )
872                                                as *mut objc2::runtime::ProtocolObject<
873                                                    dyn objc2_metal::MTLTexture,
874                                                >,
875                                        )
876                                        .unwrap(),
877                                        gpu::TexelAspects::COLOR,
878                                    ),
879                                )
880                            };
881
882                            _encoder.bind(
883                                0,
884                                &ShaderSurfacesData {
885                                    globals,
886                                    surface_locals: SurfaceParams {
887                                        bounds: surface.bounds.into(),
888                                        content_mask: surface.content_mask.bounds.into(),
889                                    },
890                                    t_y,
891                                    t_cb_cr,
892                                    s_surface: self.atlas_sampler,
893                                },
894                            );
895
896                            _encoder.draw(0, 4, 0, 1);
897                        }
898                    }
899                }
900            }
901        }
902        drop(pass);
903
904        self.command_encoder.present(frame);
905        let sync_point = self.gpu.submit(&mut self.command_encoder);
906
907        profiling::scope!("finish");
908        self.instance_belt.flush(&sync_point);
909        self.atlas.after_frame(&sync_point);
910
911        self.wait_for_gpu();
912        self.last_sync_point = Some(sync_point);
913    }
914}
915
916fn create_path_intermediate_texture(
917    gpu: &gpu::Context,
918    format: gpu::TextureFormat,
919    width: u32,
920    height: u32,
921) -> (gpu::Texture, gpu::TextureView) {
922    let texture = gpu.create_texture(gpu::TextureDesc {
923        name: "path intermediate",
924        format,
925        size: gpu::Extent {
926            width,
927            height,
928            depth: 1,
929        },
930        array_layer_count: 1,
931        mip_level_count: 1,
932        sample_count: 1,
933        dimension: gpu::TextureDimension::D2,
934        usage: gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE | gpu::TextureUsage::TARGET,
935        external: None,
936    });
937    let texture_view = gpu.create_texture_view(
938        texture,
939        gpu::TextureViewDesc {
940            name: "path intermediate view",
941            format,
942            dimension: gpu::ViewDimension::D2,
943            subresources: &Default::default(),
944        },
945    );
946    (texture, texture_view)
947}
948
949fn create_msaa_texture_if_needed(
950    gpu: &gpu::Context,
951    format: gpu::TextureFormat,
952    width: u32,
953    height: u32,
954    sample_count: u32,
955) -> Option<(gpu::Texture, gpu::TextureView)> {
956    if sample_count <= 1 {
957        return None;
958    }
959    let texture_msaa = gpu.create_texture(gpu::TextureDesc {
960        name: "path intermediate msaa",
961        format,
962        size: gpu::Extent {
963            width,
964            height,
965            depth: 1,
966        },
967        array_layer_count: 1,
968        mip_level_count: 1,
969        sample_count,
970        dimension: gpu::TextureDimension::D2,
971        usage: gpu::TextureUsage::TARGET,
972        external: None,
973    });
974    let texture_view_msaa = gpu.create_texture_view(
975        texture_msaa,
976        gpu::TextureViewDesc {
977            name: "path intermediate msaa view",
978            format,
979            dimension: gpu::ViewDimension::D2,
980            subresources: &Default::default(),
981        },
982    );
983
984    Some((texture_msaa, texture_view_msaa))
985}