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, ContentMask, DevicePixels, GpuSpecs, MonochromeSprite, PathVertex,
  7    PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size, Underline,
  8};
  9use blade_graphics::{self as gpu};
 10use blade_util::{BufferBelt, BufferBeltDescriptor};
 11use bytemuck::{Pod, Zeroable};
 12#[cfg(target_os = "macos")]
 13use media::core_video::CVMetalTextureCache;
 14use std::{mem, 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 ShaderPathsData {
 65    globals: GlobalParams,
 66    b_path_vertices: gpu::BufferPiece,
 67    b_path_sprites: gpu::BufferPiece,
 68}
 69
 70#[derive(blade_macros::ShaderData)]
 71struct ShaderUnderlinesData {
 72    globals: GlobalParams,
 73    b_underlines: gpu::BufferPiece,
 74}
 75
 76#[derive(blade_macros::ShaderData)]
 77struct ShaderMonoSpritesData {
 78    globals: GlobalParams,
 79    t_sprite: gpu::TextureView,
 80    s_sprite: gpu::Sampler,
 81    b_mono_sprites: gpu::BufferPiece,
 82}
 83
 84#[derive(blade_macros::ShaderData)]
 85struct ShaderPolySpritesData {
 86    globals: GlobalParams,
 87    t_sprite: gpu::TextureView,
 88    s_sprite: gpu::Sampler,
 89    b_poly_sprites: gpu::BufferPiece,
 90}
 91
 92#[derive(blade_macros::ShaderData)]
 93struct ShaderSurfacesData {
 94    globals: GlobalParams,
 95    surface_locals: SurfaceParams,
 96    t_y: gpu::TextureView,
 97    t_cb_cr: gpu::TextureView,
 98    s_surface: gpu::Sampler,
 99}
100
101#[derive(Clone, Debug, Eq, PartialEq)]
102#[repr(C)]
103struct PathSprite {
104    bounds: Bounds<ScaledPixels>,
105    color: Background,
106}
107
108/// Argument buffer layout for `draw_indirect` commands.
109#[repr(C)]
110#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)]
111pub struct DrawIndirectArgs {
112    /// The number of vertices to draw.
113    pub vertex_count: u32,
114    /// The number of instances to draw.
115    pub instance_count: u32,
116    /// The Index of the first vertex to draw.
117    pub first_vertex: u32,
118    /// The instance ID of the first instance to draw.
119    ///
120    /// Has to be 0, unless [`Features::INDIRECT_FIRST_INSTANCE`](crate::Features::INDIRECT_FIRST_INSTANCE) is enabled.
121    pub first_instance: u32,
122}
123
124struct BladePipelines {
125    quads: gpu::RenderPipeline,
126    shadows: 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, 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        assert_eq!(
150            mem::size_of::<PathVertex<ScaledPixels>>(),
151            shader.get_struct_size("PathVertex") as usize,
152        );
153        shader.check_struct_size::<PathSprite>();
154        shader.check_struct_size::<Underline>();
155        shader.check_struct_size::<MonochromeSprite>();
156        shader.check_struct_size::<PolychromeSprite>();
157
158        // See https://apoorvaj.io/alpha-compositing-opengl-blending-and-premultiplied-alpha/
159        let blend_mode = match surface_info.alpha {
160            gpu::AlphaMode::Ignored => gpu::BlendState::ALPHA_BLENDING,
161            gpu::AlphaMode::PreMultiplied => gpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING,
162            gpu::AlphaMode::PostMultiplied => gpu::BlendState::ALPHA_BLENDING,
163        };
164        let color_targets = &[gpu::ColorTargetState {
165            format: surface_info.format,
166            blend: Some(blend_mode),
167            write_mask: gpu::ColorWrites::default(),
168        }];
169
170        Self {
171            quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
172                name: "quads",
173                data_layouts: &[&ShaderQuadsData::layout()],
174                vertex: shader.at("vs_quad"),
175                vertex_fetches: &[],
176                primitive: gpu::PrimitiveState {
177                    topology: gpu::PrimitiveTopology::TriangleStrip,
178                    ..Default::default()
179                },
180                depth_stencil: None,
181                fragment: Some(shader.at("fs_quad")),
182                color_targets,
183                multisample_state: gpu::MultisampleState {
184                    sample_count,
185                    ..Default::default()
186                },
187            }),
188            shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
189                name: "shadows",
190                data_layouts: &[&ShaderShadowsData::layout()],
191                vertex: shader.at("vs_shadow"),
192                vertex_fetches: &[],
193                primitive: gpu::PrimitiveState {
194                    topology: gpu::PrimitiveTopology::TriangleStrip,
195                    ..Default::default()
196                },
197                depth_stencil: None,
198                fragment: Some(shader.at("fs_shadow")),
199                color_targets,
200                multisample_state: gpu::MultisampleState {
201                    sample_count,
202                    ..Default::default()
203                },
204            }),
205            paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
206                name: "paths",
207                data_layouts: &[&ShaderPathsData::layout()],
208                vertex: shader.at("vs_path"),
209                vertex_fetches: &[],
210                primitive: gpu::PrimitiveState {
211                    topology: gpu::PrimitiveTopology::TriangleList,
212                    ..Default::default()
213                },
214                depth_stencil: None,
215                fragment: Some(shader.at("fs_path")),
216                color_targets,
217                multisample_state: gpu::MultisampleState {
218                    sample_count,
219                    ..Default::default()
220                },
221            }),
222            underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
223                name: "underlines",
224                data_layouts: &[&ShaderUnderlinesData::layout()],
225                vertex: shader.at("vs_underline"),
226                vertex_fetches: &[],
227                primitive: gpu::PrimitiveState {
228                    topology: gpu::PrimitiveTopology::TriangleStrip,
229                    ..Default::default()
230                },
231                depth_stencil: None,
232                fragment: Some(shader.at("fs_underline")),
233                color_targets,
234                multisample_state: gpu::MultisampleState {
235                    sample_count,
236                    ..Default::default()
237                },
238            }),
239            mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
240                name: "mono-sprites",
241                data_layouts: &[&ShaderMonoSpritesData::layout()],
242                vertex: shader.at("vs_mono_sprite"),
243                vertex_fetches: &[],
244                primitive: gpu::PrimitiveState {
245                    topology: gpu::PrimitiveTopology::TriangleStrip,
246                    ..Default::default()
247                },
248                depth_stencil: None,
249                fragment: Some(shader.at("fs_mono_sprite")),
250                color_targets,
251                multisample_state: gpu::MultisampleState {
252                    sample_count,
253                    ..Default::default()
254                },
255            }),
256            poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
257                name: "poly-sprites",
258                data_layouts: &[&ShaderPolySpritesData::layout()],
259                vertex: shader.at("vs_poly_sprite"),
260                vertex_fetches: &[],
261                primitive: gpu::PrimitiveState {
262                    topology: gpu::PrimitiveTopology::TriangleStrip,
263                    ..Default::default()
264                },
265                depth_stencil: None,
266                fragment: Some(shader.at("fs_poly_sprite")),
267                color_targets,
268                multisample_state: gpu::MultisampleState {
269                    sample_count,
270                    ..Default::default()
271                },
272            }),
273            surfaces: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
274                name: "surfaces",
275                data_layouts: &[&ShaderSurfacesData::layout()],
276                vertex: shader.at("vs_surface"),
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_surface")),
284                color_targets,
285                multisample_state: gpu::MultisampleState {
286                    sample_count,
287                    ..Default::default()
288                },
289            }),
290        }
291    }
292
293    fn destroy(&mut self, gpu: &gpu::Context) {
294        gpu.destroy_render_pipeline(&mut self.quads);
295        gpu.destroy_render_pipeline(&mut self.shadows);
296        gpu.destroy_render_pipeline(&mut self.paths);
297        gpu.destroy_render_pipeline(&mut self.underlines);
298        gpu.destroy_render_pipeline(&mut self.mono_sprites);
299        gpu.destroy_render_pipeline(&mut self.poly_sprites);
300        gpu.destroy_render_pipeline(&mut self.surfaces);
301    }
302}
303
304pub struct BladeSurfaceConfig {
305    pub size: gpu::Extent,
306    pub transparent: bool,
307}
308
309//Note: we could see some of these fields moved into `BladeContext`
310// so that they are shared between windows. E.g. `pipelines`.
311// But that is complicated by the fact that pipelines depend on
312// the format and alpha mode.
313pub struct BladeRenderer {
314    gpu: Arc<gpu::Context>,
315    surface: gpu::Surface,
316    surface_config: gpu::SurfaceConfig,
317    command_encoder: gpu::CommandEncoder,
318    last_sync_point: Option<gpu::SyncPoint>,
319    pipelines: BladePipelines,
320    instance_belt: BufferBelt,
321    atlas: Arc<BladeAtlas>,
322    atlas_sampler: gpu::Sampler,
323    #[cfg(target_os = "macos")]
324    core_video_texture_cache: CVMetalTextureCache,
325    sample_count: u32,
326    texture_msaa: Option<gpu::Texture>,
327    texture_view_msaa: Option<gpu::TextureView>,
328}
329
330impl BladeRenderer {
331    pub fn new<I: raw_window_handle::HasWindowHandle + raw_window_handle::HasDisplayHandle>(
332        context: &BladeContext,
333        window: &I,
334        config: BladeSurfaceConfig,
335    ) -> anyhow::Result<Self> {
336        // workaround for https://github.com/zed-industries/zed/issues/26143
337        let sample_count = std::env::var("ZED_SAMPLE_COUNT")
338            .ok()
339            .or_else(|| std::env::var("ZED_PATH_SAMPLE_COUNT").ok())
340            .and_then(|v| v.parse().ok())
341            .or_else(|| {
342                [4, 2, 1]
343                    .into_iter()
344                    .find(|count| context.gpu.supports_texture_sample_count(*count))
345            })
346            .unwrap_or(1);
347
348        let surface_config = gpu::SurfaceConfig {
349            size: config.size,
350            usage: gpu::TextureUsage::TARGET,
351            display_sync: gpu::DisplaySync::Recent,
352            color_space: gpu::ColorSpace::Linear,
353            allow_exclusive_full_screen: false,
354            transparent: config.transparent,
355        };
356        let surface = context
357            .gpu
358            .create_surface_configured(window, surface_config)
359            .map_err(|err| anyhow::anyhow!("Failed to create surface: {err:?}"))?;
360
361        let (texture_msaa, texture_view_msaa) = create_msaa_texture_if_needed(
362            &context.gpu,
363            surface.info().format,
364            config.size.width,
365            config.size.height,
366            sample_count,
367        )
368        .unzip();
369
370        let command_encoder = context.gpu.create_command_encoder(gpu::CommandEncoderDesc {
371            name: "main",
372            buffer_count: 2,
373        });
374
375        let pipelines = BladePipelines::new(&context.gpu, surface.info(), sample_count);
376        let instance_belt = BufferBelt::new(BufferBeltDescriptor {
377            memory: gpu::Memory::Shared,
378            min_chunk_size: 0x1000,
379            alignment: 0x40, // Vulkan `minStorageBufferOffsetAlignment` on Intel Xe
380        });
381        let atlas = Arc::new(BladeAtlas::new(&context.gpu));
382        let atlas_sampler = context.gpu.create_sampler(gpu::SamplerDesc {
383            name: "atlas",
384            mag_filter: gpu::FilterMode::Linear,
385            min_filter: gpu::FilterMode::Linear,
386            ..Default::default()
387        });
388
389        #[cfg(target_os = "macos")]
390        let core_video_texture_cache = unsafe {
391            CVMetalTextureCache::new(
392                objc2::rc::Retained::as_ptr(&context.gpu.metal_device()) as *mut _
393            )
394            .unwrap()
395        };
396
397        Ok(Self {
398            gpu: Arc::clone(&context.gpu),
399            surface,
400            surface_config,
401            command_encoder,
402            last_sync_point: None,
403            pipelines,
404            instance_belt,
405            atlas,
406            atlas_sampler,
407            #[cfg(target_os = "macos")]
408            core_video_texture_cache,
409            sample_count,
410            texture_msaa,
411            texture_view_msaa,
412        })
413    }
414
415    fn wait_for_gpu(&mut self) {
416        if let Some(last_sp) = self.last_sync_point.take() {
417            if !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {
418                log::error!("GPU hung");
419                #[cfg(target_os = "linux")]
420                if self.gpu.device_information().driver_name == "radv" {
421                    log::error!(
422                        "there's a known bug with amdgpu/radv, try setting ZED_PATH_SAMPLE_COUNT=0 as a workaround"
423                    );
424                    log::error!(
425                        "if that helps you're running into https://github.com/zed-industries/zed/issues/26143"
426                    );
427                }
428                log::error!(
429                    "your device information is: {:?}",
430                    self.gpu.device_information()
431                );
432                while !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {}
433            }
434        }
435    }
436
437    pub fn update_drawable_size(&mut self, size: Size<DevicePixels>) {
438        self.update_drawable_size_impl(size, false);
439    }
440
441    /// Like `update_drawable_size` but skips the check that the size has changed. This is useful in
442    /// cases like restoring a window from minimization where the size is the same but the
443    /// renderer's swap chain needs to be recreated.
444    #[cfg_attr(
445        any(target_os = "macos", target_os = "linux", target_os = "freebsd"),
446        allow(dead_code)
447    )]
448    pub fn update_drawable_size_even_if_unchanged(&mut self, size: Size<DevicePixels>) {
449        self.update_drawable_size_impl(size, true);
450    }
451
452    fn update_drawable_size_impl(&mut self, size: Size<DevicePixels>, always_resize: bool) {
453        let gpu_size = gpu::Extent {
454            width: size.width.0 as u32,
455            height: size.height.0 as u32,
456            depth: 1,
457        };
458
459        if always_resize || gpu_size != self.surface_config.size {
460            self.wait_for_gpu();
461            self.surface_config.size = gpu_size;
462            self.gpu
463                .reconfigure_surface(&mut self.surface, self.surface_config);
464
465            if let Some(texture_msaa) = self.texture_msaa {
466                self.gpu.destroy_texture(texture_msaa);
467            }
468            if let Some(texture_view_msaa) = self.texture_view_msaa {
469                self.gpu.destroy_texture_view(texture_view_msaa);
470            }
471
472            let (texture_msaa, texture_view_msaa) = create_msaa_texture_if_needed(
473                &self.gpu,
474                self.surface.info().format,
475                gpu_size.width,
476                gpu_size.height,
477                self.sample_count,
478            )
479            .unzip();
480            self.texture_msaa = texture_msaa;
481            self.texture_view_msaa = texture_view_msaa;
482        }
483    }
484
485    pub fn update_transparency(&mut self, transparent: bool) {
486        if transparent != self.surface_config.transparent {
487            self.wait_for_gpu();
488            self.surface_config.transparent = transparent;
489            self.gpu
490                .reconfigure_surface(&mut self.surface, self.surface_config);
491            self.pipelines.destroy(&self.gpu);
492            self.pipelines = BladePipelines::new(&self.gpu, self.surface.info(), self.sample_count);
493        }
494    }
495
496    #[cfg_attr(
497        any(target_os = "macos", feature = "wayland", target_os = "windows"),
498        allow(dead_code)
499    )]
500    pub fn viewport_size(&self) -> gpu::Extent {
501        self.surface_config.size
502    }
503
504    pub fn sprite_atlas(&self) -> &Arc<BladeAtlas> {
505        &self.atlas
506    }
507
508    #[cfg_attr(target_os = "macos", allow(dead_code))]
509    pub fn gpu_specs(&self) -> GpuSpecs {
510        let info = self.gpu.device_information();
511
512        GpuSpecs {
513            is_software_emulated: info.is_software_emulated,
514            device_name: info.device_name.clone(),
515            driver_name: info.driver_name.clone(),
516            driver_info: info.driver_info.clone(),
517        }
518    }
519
520    #[cfg(target_os = "macos")]
521    pub fn layer(&self) -> metal::MetalLayer {
522        unsafe { foreign_types::ForeignType::from_ptr(self.layer_ptr()) }
523    }
524
525    #[cfg(target_os = "macos")]
526    pub fn layer_ptr(&self) -> *mut metal::CAMetalLayer {
527        objc2::rc::Retained::as_ptr(&self.surface.metal_layer()) as *mut _
528    }
529
530    pub fn destroy(&mut self) {
531        self.wait_for_gpu();
532        self.atlas.destroy();
533        self.gpu.destroy_sampler(self.atlas_sampler);
534        self.instance_belt.destroy(&self.gpu);
535        self.gpu.destroy_command_encoder(&mut self.command_encoder);
536        self.pipelines.destroy(&self.gpu);
537        self.gpu.destroy_surface(&mut self.surface);
538        if let Some(texture_msaa) = self.texture_msaa {
539            self.gpu.destroy_texture(texture_msaa);
540        }
541        if let Some(texture_view_msaa) = self.texture_view_msaa {
542            self.gpu.destroy_texture_view(texture_view_msaa);
543        }
544    }
545
546    pub fn draw(&mut self, scene: &Scene) {
547        self.command_encoder.start();
548        self.atlas.before_frame(&mut self.command_encoder);
549
550        let frame = {
551            profiling::scope!("acquire frame");
552            self.surface.acquire_frame()
553        };
554        let frame_view = frame.texture_view();
555        if let Some(texture_msaa) = self.texture_msaa {
556            self.command_encoder.init_texture(texture_msaa);
557        }
558        self.command_encoder.init_texture(frame.texture());
559
560        let globals = GlobalParams {
561            viewport_size: [
562                self.surface_config.size.width as f32,
563                self.surface_config.size.height as f32,
564            ],
565            premultiplied_alpha: match self.surface.info().alpha {
566                gpu::AlphaMode::Ignored | gpu::AlphaMode::PostMultiplied => 0,
567                gpu::AlphaMode::PreMultiplied => 1,
568            },
569            pad: 0,
570        };
571
572        let target = if let Some(texture_view_msaa) = self.texture_view_msaa {
573            gpu::RenderTarget {
574                view: texture_view_msaa,
575                init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
576                finish_op: gpu::FinishOp::ResolveTo(frame_view),
577            }
578        } else {
579            gpu::RenderTarget {
580                view: frame_view,
581                init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
582                finish_op: gpu::FinishOp::Store,
583            }
584        };
585
586        // draw to the target texture
587        if let mut pass = self.command_encoder.render(
588            "main",
589            gpu::RenderTargetSet {
590                colors: &[target],
591                depth_stencil: None,
592            },
593        ) {
594            profiling::scope!("render pass");
595            for batch in scene.batches() {
596                match batch {
597                    PrimitiveBatch::Quads(quads) => {
598                        let instance_buf =
599                            unsafe { self.instance_belt.alloc_typed(quads, &self.gpu) };
600                        let mut encoder = pass.with(&self.pipelines.quads);
601                        encoder.bind(
602                            0,
603                            &ShaderQuadsData {
604                                globals,
605                                b_quads: instance_buf,
606                            },
607                        );
608                        encoder.draw(0, 4, 0, quads.len() as u32);
609                    }
610                    PrimitiveBatch::Shadows(shadows) => {
611                        let instance_buf =
612                            unsafe { self.instance_belt.alloc_typed(shadows, &self.gpu) };
613                        let mut encoder = pass.with(&self.pipelines.shadows);
614                        encoder.bind(
615                            0,
616                            &ShaderShadowsData {
617                                globals,
618                                b_shadows: instance_buf,
619                            },
620                        );
621                        encoder.draw(0, 4, 0, shadows.len() as u32);
622                    }
623                    PrimitiveBatch::Paths(paths) => {
624                        let mut encoder = pass.with(&self.pipelines.paths);
625
626                        let mut vertices = Vec::new();
627                        let mut sprites = Vec::with_capacity(paths.len());
628                        let mut draw_indirect_commands = Vec::with_capacity(paths.len());
629                        let mut first_vertex = 0;
630
631                        for (i, path) in paths.iter().enumerate() {
632                            draw_indirect_commands.push(DrawIndirectArgs {
633                                vertex_count: path.vertices.len() as u32,
634                                instance_count: 1,
635                                first_vertex,
636                                first_instance: i as u32,
637                            });
638                            first_vertex += path.vertices.len() as u32;
639
640                            vertices.extend(path.vertices.iter().map(|v| PathVertex {
641                                xy_position: v.xy_position,
642                                content_mask: ContentMask {
643                                    bounds: path.content_mask.bounds,
644                                },
645                            }));
646
647                            sprites.push(PathSprite {
648                                bounds: path.bounds,
649                                color: path.color,
650                            });
651                        }
652
653                        let b_path_vertices =
654                            unsafe { self.instance_belt.alloc_typed(&vertices, &self.gpu) };
655                        let instance_buf =
656                            unsafe { self.instance_belt.alloc_typed(&sprites, &self.gpu) };
657                        let indirect_buf = unsafe {
658                            self.instance_belt
659                                .alloc_typed(&draw_indirect_commands, &self.gpu)
660                        };
661
662                        encoder.bind(
663                            0,
664                            &ShaderPathsData {
665                                globals,
666                                b_path_vertices,
667                                b_path_sprites: instance_buf,
668                            },
669                        );
670
671                        for i in 0..paths.len() {
672                            encoder.draw_indirect(indirect_buf.buffer.at(indirect_buf.offset
673                                + (i * mem::size_of::<DrawIndirectArgs>()) as u64));
674                        }
675                    }
676                    PrimitiveBatch::Underlines(underlines) => {
677                        let instance_buf =
678                            unsafe { self.instance_belt.alloc_typed(underlines, &self.gpu) };
679                        let mut encoder = pass.with(&self.pipelines.underlines);
680                        encoder.bind(
681                            0,
682                            &ShaderUnderlinesData {
683                                globals,
684                                b_underlines: instance_buf,
685                            },
686                        );
687                        encoder.draw(0, 4, 0, underlines.len() as u32);
688                    }
689                    PrimitiveBatch::MonochromeSprites {
690                        texture_id,
691                        sprites,
692                    } => {
693                        let tex_info = self.atlas.get_texture_info(texture_id);
694                        let instance_buf =
695                            unsafe { self.instance_belt.alloc_typed(sprites, &self.gpu) };
696                        let mut encoder = pass.with(&self.pipelines.mono_sprites);
697                        encoder.bind(
698                            0,
699                            &ShaderMonoSpritesData {
700                                globals,
701                                t_sprite: tex_info.raw_view,
702                                s_sprite: self.atlas_sampler,
703                                b_mono_sprites: instance_buf,
704                            },
705                        );
706                        encoder.draw(0, 4, 0, sprites.len() as u32);
707                    }
708                    PrimitiveBatch::PolychromeSprites {
709                        texture_id,
710                        sprites,
711                    } => {
712                        let tex_info = self.atlas.get_texture_info(texture_id);
713                        let instance_buf =
714                            unsafe { self.instance_belt.alloc_typed(sprites, &self.gpu) };
715                        let mut encoder = pass.with(&self.pipelines.poly_sprites);
716                        encoder.bind(
717                            0,
718                            &ShaderPolySpritesData {
719                                globals,
720                                t_sprite: tex_info.raw_view,
721                                s_sprite: self.atlas_sampler,
722                                b_poly_sprites: instance_buf,
723                            },
724                        );
725                        encoder.draw(0, 4, 0, sprites.len() as u32);
726                    }
727                    PrimitiveBatch::Surfaces(surfaces) => {
728                        let mut _encoder = pass.with(&self.pipelines.surfaces);
729
730                        for surface in surfaces {
731                            #[cfg(not(target_os = "macos"))]
732                            {
733                                let _ = surface;
734                                continue;
735                            };
736
737                            #[cfg(target_os = "macos")]
738                            {
739                                let (t_y, t_cb_cr) = unsafe {
740                                    use core_foundation::base::TCFType as _;
741                                    use std::ptr;
742
743                                    assert_eq!(
744                                        surface.image_buffer.get_pixel_format(),
745                                        core_video::pixel_buffer::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
746                                    );
747
748                                    let y_texture = self
749                                        .core_video_texture_cache
750                                        .create_texture_from_image(
751                                            surface.image_buffer.as_concrete_TypeRef(),
752                                            ptr::null(),
753                                            metal::MTLPixelFormat::R8Unorm,
754                                            surface.image_buffer.get_width_of_plane(0),
755                                            surface.image_buffer.get_height_of_plane(0),
756                                            0,
757                                        )
758                                        .unwrap();
759                                    let cb_cr_texture = self
760                                        .core_video_texture_cache
761                                        .create_texture_from_image(
762                                            surface.image_buffer.as_concrete_TypeRef(),
763                                            ptr::null(),
764                                            metal::MTLPixelFormat::RG8Unorm,
765                                            surface.image_buffer.get_width_of_plane(1),
766                                            surface.image_buffer.get_height_of_plane(1),
767                                            1,
768                                        )
769                                        .unwrap();
770                                    (
771                                        gpu::TextureView::from_metal_texture(
772                                            &objc2::rc::Retained::retain(
773                                                foreign_types::ForeignTypeRef::as_ptr(
774                                                    y_texture.as_texture_ref(),
775                                                )
776                                                    as *mut objc2::runtime::ProtocolObject<
777                                                        dyn objc2_metal::MTLTexture,
778                                                    >,
779                                            )
780                                            .unwrap(),
781                                            gpu::TexelAspects::COLOR,
782                                        ),
783                                        gpu::TextureView::from_metal_texture(
784                                            &objc2::rc::Retained::retain(
785                                                foreign_types::ForeignTypeRef::as_ptr(
786                                                    cb_cr_texture.as_texture_ref(),
787                                                )
788                                                    as *mut objc2::runtime::ProtocolObject<
789                                                        dyn objc2_metal::MTLTexture,
790                                                    >,
791                                            )
792                                            .unwrap(),
793                                            gpu::TexelAspects::COLOR,
794                                        ),
795                                    )
796                                };
797
798                                _encoder.bind(
799                                    0,
800                                    &ShaderSurfacesData {
801                                        globals,
802                                        surface_locals: SurfaceParams {
803                                            bounds: surface.bounds.into(),
804                                            content_mask: surface.content_mask.bounds.into(),
805                                        },
806                                        t_y,
807                                        t_cb_cr,
808                                        s_surface: self.atlas_sampler,
809                                    },
810                                );
811
812                                _encoder.draw(0, 4, 0, 1);
813                            }
814                        }
815                    }
816                }
817            }
818        }
819
820        self.command_encoder.present(frame);
821        let sync_point = self.gpu.submit(&mut self.command_encoder);
822
823        profiling::scope!("finish");
824        self.instance_belt.flush(&sync_point);
825        self.atlas.after_frame(&sync_point);
826
827        self.wait_for_gpu();
828        self.last_sync_point = Some(sync_point);
829    }
830}
831
832fn create_msaa_texture_if_needed(
833    gpu: &gpu::Context,
834    format: gpu::TextureFormat,
835    width: u32,
836    height: u32,
837    sample_count: u32,
838) -> Option<(gpu::Texture, gpu::TextureView)> {
839    if sample_count <= 1 {
840        return None;
841    }
842
843    let texture_msaa = gpu.create_texture(gpu::TextureDesc {
844        name: "msaa",
845        format,
846        size: gpu::Extent {
847            width,
848            height,
849            depth: 1,
850        },
851        array_layer_count: 1,
852        mip_level_count: 1,
853        sample_count,
854        dimension: gpu::TextureDimension::D2,
855        usage: gpu::TextureUsage::TARGET,
856        external: None,
857    });
858    let texture_view_msaa = gpu.create_texture_view(
859        texture_msaa,
860        gpu::TextureViewDesc {
861            name: "msaa view",
862            format,
863            dimension: gpu::ViewDimension::D2,
864            subresources: &Default::default(),
865        },
866    );
867
868    Some((texture_msaa, texture_view_msaa))
869}