blade_renderer.rs

  1// Doing `if let` gives you nice scoping with passes/encoders
  2#![allow(irrefutable_let_patterns)]
  3
  4use super::{BladeAtlas, BladeBelt, BladeBeltDescriptor, PATH_TEXTURE_FORMAT};
  5use crate::{
  6    AtlasTextureKind, AtlasTile, Bounds, ContentMask, Hsla, MonochromeSprite, Path, PathId,
  7    PathVertex, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size,
  8    Underline,
  9};
 10use bytemuck::{Pod, Zeroable};
 11use collections::HashMap;
 12#[cfg(target_os = "macos")]
 13use media::core_video::CVMetalTextureCache;
 14#[cfg(target_os = "macos")]
 15use std::{ffi::c_void, ptr::NonNull};
 16
 17use blade_graphics as gpu;
 18use std::{mem, sync::Arc};
 19
 20const MAX_FRAME_TIME_MS: u32 = 1000;
 21
 22pub type Context = ();
 23pub type Renderer = BladeRenderer;
 24
 25#[cfg(target_os = "macos")]
 26pub unsafe fn new_renderer(
 27    _context: self::Context,
 28    _native_window: *mut c_void,
 29    native_view: *mut c_void,
 30    bounds: crate::Size<f32>,
 31) -> Renderer {
 32    use raw_window_handle as rwh;
 33    struct RawWindow {
 34        view: *mut c_void,
 35    }
 36
 37    impl rwh::HasWindowHandle for RawWindow {
 38        fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
 39            let view = NonNull::new(self.view).unwrap();
 40            let handle = rwh::AppKitWindowHandle::new(view);
 41            Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })
 42        }
 43    }
 44    impl rwh::HasDisplayHandle for RawWindow {
 45        fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
 46            let handle = rwh::AppKitDisplayHandle::new();
 47            Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })
 48        }
 49    }
 50
 51    let gpu = Arc::new(
 52        gpu::Context::init_windowed(
 53            &RawWindow {
 54                view: native_view as *mut _,
 55            },
 56            gpu::ContextDesc {
 57                validation: cfg!(debug_assertions),
 58                capture: false,
 59                overlay: false,
 60            },
 61        )
 62        .unwrap(),
 63    );
 64
 65    BladeRenderer::new(
 66        gpu,
 67        gpu::Extent {
 68            width: bounds.width as u32,
 69            height: bounds.height as u32,
 70            depth: 1,
 71        },
 72    )
 73}
 74
 75#[repr(C)]
 76#[derive(Clone, Copy, Pod, Zeroable)]
 77struct GlobalParams {
 78    viewport_size: [f32; 2],
 79    pad: [u32; 2],
 80}
 81
 82//Note: we can't use `Bounds` directly here because
 83// it doesn't implement Pod + Zeroable
 84#[repr(C)]
 85#[derive(Clone, Copy, Pod, Zeroable)]
 86struct PodBounds {
 87    origin: [f32; 2],
 88    size: [f32; 2],
 89}
 90
 91impl From<Bounds<ScaledPixels>> for PodBounds {
 92    fn from(bounds: Bounds<ScaledPixels>) -> Self {
 93        Self {
 94            origin: [bounds.origin.x.0, bounds.origin.y.0],
 95            size: [bounds.size.width.0, bounds.size.height.0],
 96        }
 97    }
 98}
 99
100#[repr(C)]
101#[derive(Clone, Copy, Pod, Zeroable)]
102struct SurfaceParams {
103    bounds: PodBounds,
104    content_mask: PodBounds,
105}
106
107#[derive(blade_macros::ShaderData)]
108struct ShaderQuadsData {
109    globals: GlobalParams,
110    b_quads: gpu::BufferPiece,
111}
112
113#[derive(blade_macros::ShaderData)]
114struct ShaderShadowsData {
115    globals: GlobalParams,
116    b_shadows: gpu::BufferPiece,
117}
118
119#[derive(blade_macros::ShaderData)]
120struct ShaderPathRasterizationData {
121    globals: GlobalParams,
122    b_path_vertices: gpu::BufferPiece,
123}
124
125#[derive(blade_macros::ShaderData)]
126struct ShaderPathsData {
127    globals: GlobalParams,
128    t_sprite: gpu::TextureView,
129    s_sprite: gpu::Sampler,
130    b_path_sprites: gpu::BufferPiece,
131}
132
133#[derive(blade_macros::ShaderData)]
134struct ShaderUnderlinesData {
135    globals: GlobalParams,
136    b_underlines: gpu::BufferPiece,
137}
138
139#[derive(blade_macros::ShaderData)]
140struct ShaderMonoSpritesData {
141    globals: GlobalParams,
142    t_sprite: gpu::TextureView,
143    s_sprite: gpu::Sampler,
144    b_mono_sprites: gpu::BufferPiece,
145}
146
147#[derive(blade_macros::ShaderData)]
148struct ShaderPolySpritesData {
149    globals: GlobalParams,
150    t_sprite: gpu::TextureView,
151    s_sprite: gpu::Sampler,
152    b_poly_sprites: gpu::BufferPiece,
153}
154
155#[derive(blade_macros::ShaderData)]
156struct ShaderSurfacesData {
157    globals: GlobalParams,
158    surface_locals: SurfaceParams,
159    t_y: gpu::TextureView,
160    t_cb_cr: gpu::TextureView,
161    s_surface: gpu::Sampler,
162}
163
164#[derive(Clone, Debug, Eq, PartialEq)]
165#[repr(C)]
166struct PathSprite {
167    bounds: Bounds<ScaledPixels>,
168    color: Hsla,
169    tile: AtlasTile,
170}
171
172struct BladePipelines {
173    quads: gpu::RenderPipeline,
174    shadows: gpu::RenderPipeline,
175    path_rasterization: gpu::RenderPipeline,
176    paths: gpu::RenderPipeline,
177    underlines: gpu::RenderPipeline,
178    mono_sprites: gpu::RenderPipeline,
179    poly_sprites: gpu::RenderPipeline,
180    surfaces: gpu::RenderPipeline,
181}
182
183impl BladePipelines {
184    fn new(gpu: &gpu::Context, surface_info: gpu::SurfaceInfo) -> Self {
185        use gpu::ShaderData as _;
186
187        let shader = gpu.create_shader(gpu::ShaderDesc {
188            source: include_str!("shaders.wgsl"),
189        });
190        shader.check_struct_size::<GlobalParams>();
191        shader.check_struct_size::<SurfaceParams>();
192        shader.check_struct_size::<Quad>();
193        shader.check_struct_size::<Shadow>();
194        assert_eq!(
195            mem::size_of::<PathVertex<ScaledPixels>>(),
196            shader.get_struct_size("PathVertex") as usize,
197        );
198        shader.check_struct_size::<PathSprite>();
199        shader.check_struct_size::<Underline>();
200        shader.check_struct_size::<MonochromeSprite>();
201        shader.check_struct_size::<PolychromeSprite>();
202
203        Self {
204            quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
205                name: "quads",
206                data_layouts: &[&ShaderQuadsData::layout()],
207                vertex: shader.at("vs_quad"),
208                vertex_fetches: &[],
209                primitive: gpu::PrimitiveState {
210                    topology: gpu::PrimitiveTopology::TriangleStrip,
211                    ..Default::default()
212                },
213                depth_stencil: None,
214                fragment: shader.at("fs_quad"),
215                color_targets: &[gpu::ColorTargetState {
216                    format: surface_info.format,
217                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
218                    write_mask: gpu::ColorWrites::default(),
219                }],
220            }),
221            shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
222                name: "shadows",
223                data_layouts: &[&ShaderShadowsData::layout()],
224                vertex: shader.at("vs_shadow"),
225                vertex_fetches: &[],
226                primitive: gpu::PrimitiveState {
227                    topology: gpu::PrimitiveTopology::TriangleStrip,
228                    ..Default::default()
229                },
230                depth_stencil: None,
231                fragment: shader.at("fs_shadow"),
232                color_targets: &[gpu::ColorTargetState {
233                    format: surface_info.format,
234                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
235                    write_mask: gpu::ColorWrites::default(),
236                }],
237            }),
238            path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
239                name: "path_rasterization",
240                data_layouts: &[&ShaderPathRasterizationData::layout()],
241                vertex: shader.at("vs_path_rasterization"),
242                vertex_fetches: &[],
243                primitive: gpu::PrimitiveState {
244                    topology: gpu::PrimitiveTopology::TriangleList,
245                    ..Default::default()
246                },
247                depth_stencil: None,
248                fragment: shader.at("fs_path_rasterization"),
249                color_targets: &[gpu::ColorTargetState {
250                    format: PATH_TEXTURE_FORMAT,
251                    blend: Some(gpu::BlendState::ADDITIVE),
252                    write_mask: gpu::ColorWrites::default(),
253                }],
254            }),
255            paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
256                name: "paths",
257                data_layouts: &[&ShaderPathsData::layout()],
258                vertex: shader.at("vs_path"),
259                vertex_fetches: &[],
260                primitive: gpu::PrimitiveState {
261                    topology: gpu::PrimitiveTopology::TriangleStrip,
262                    ..Default::default()
263                },
264                depth_stencil: None,
265                fragment: shader.at("fs_path"),
266                color_targets: &[gpu::ColorTargetState {
267                    format: surface_info.format,
268                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
269                    write_mask: gpu::ColorWrites::default(),
270                }],
271            }),
272            underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
273                name: "underlines",
274                data_layouts: &[&ShaderUnderlinesData::layout()],
275                vertex: shader.at("vs_underline"),
276                vertex_fetches: &[],
277                primitive: gpu::PrimitiveState {
278                    topology: gpu::PrimitiveTopology::TriangleStrip,
279                    ..Default::default()
280                },
281                depth_stencil: None,
282                fragment: shader.at("fs_underline"),
283                color_targets: &[gpu::ColorTargetState {
284                    format: surface_info.format,
285                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
286                    write_mask: gpu::ColorWrites::default(),
287                }],
288            }),
289            mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
290                name: "mono-sprites",
291                data_layouts: &[&ShaderMonoSpritesData::layout()],
292                vertex: shader.at("vs_mono_sprite"),
293                vertex_fetches: &[],
294                primitive: gpu::PrimitiveState {
295                    topology: gpu::PrimitiveTopology::TriangleStrip,
296                    ..Default::default()
297                },
298                depth_stencil: None,
299                fragment: shader.at("fs_mono_sprite"),
300                color_targets: &[gpu::ColorTargetState {
301                    format: surface_info.format,
302                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
303                    write_mask: gpu::ColorWrites::default(),
304                }],
305            }),
306            poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
307                name: "poly-sprites",
308                data_layouts: &[&ShaderPolySpritesData::layout()],
309                vertex: shader.at("vs_poly_sprite"),
310                vertex_fetches: &[],
311                primitive: gpu::PrimitiveState {
312                    topology: gpu::PrimitiveTopology::TriangleStrip,
313                    ..Default::default()
314                },
315                depth_stencil: None,
316                fragment: shader.at("fs_poly_sprite"),
317                color_targets: &[gpu::ColorTargetState {
318                    format: surface_info.format,
319                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
320                    write_mask: gpu::ColorWrites::default(),
321                }],
322            }),
323            surfaces: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
324                name: "surfaces",
325                data_layouts: &[&ShaderSurfacesData::layout()],
326                vertex: shader.at("vs_surface"),
327                vertex_fetches: &[],
328                primitive: gpu::PrimitiveState {
329                    topology: gpu::PrimitiveTopology::TriangleStrip,
330                    ..Default::default()
331                },
332                depth_stencil: None,
333                fragment: shader.at("fs_surface"),
334                color_targets: &[gpu::ColorTargetState {
335                    format: surface_info.format,
336                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
337                    write_mask: gpu::ColorWrites::default(),
338                }],
339            }),
340        }
341    }
342}
343
344pub struct BladeRenderer {
345    gpu: Arc<gpu::Context>,
346    command_encoder: gpu::CommandEncoder,
347    last_sync_point: Option<gpu::SyncPoint>,
348    pipelines: BladePipelines,
349    instance_belt: BladeBelt,
350    viewport_size: gpu::Extent,
351    path_tiles: HashMap<PathId, AtlasTile>,
352    atlas: Arc<BladeAtlas>,
353    atlas_sampler: gpu::Sampler,
354    #[cfg(target_os = "macos")]
355    core_video_texture_cache: CVMetalTextureCache,
356}
357
358impl BladeRenderer {
359    fn make_surface_config(size: gpu::Extent) -> gpu::SurfaceConfig {
360        gpu::SurfaceConfig {
361            size,
362            usage: gpu::TextureUsage::TARGET,
363            display_sync: gpu::DisplaySync::Recent,
364            //Note: this matches the original logic of the Metal backend,
365            // but ultimaterly we need to switch to `Linear`.
366            color_space: gpu::ColorSpace::Srgb,
367            allow_exclusive_full_screen: false,
368            transparent: false,
369        }
370    }
371
372    pub fn new(gpu: Arc<gpu::Context>, size: gpu::Extent) -> Self {
373        let surface_info = gpu.resize(Self::make_surface_config(size));
374        let command_encoder = gpu.create_command_encoder(gpu::CommandEncoderDesc {
375            name: "main",
376            buffer_count: 2,
377        });
378        let pipelines = BladePipelines::new(&gpu, surface_info);
379        let instance_belt = BladeBelt::new(BladeBeltDescriptor {
380            memory: gpu::Memory::Shared,
381            min_chunk_size: 0x1000,
382            alignment: 0x40, // Vulkan `minStorageBufferOffsetAlignment` on Intel Xe
383        });
384        let atlas = Arc::new(BladeAtlas::new(&gpu));
385        let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc {
386            name: "atlas",
387            mag_filter: gpu::FilterMode::Linear,
388            min_filter: gpu::FilterMode::Linear,
389            ..Default::default()
390        });
391
392        #[cfg(target_os = "macos")]
393        let core_video_texture_cache = unsafe {
394            use foreign_types::ForeignType as _;
395            CVMetalTextureCache::new(gpu.metal_device().as_ptr()).unwrap()
396        };
397
398        Self {
399            gpu,
400            command_encoder,
401            last_sync_point: None,
402            pipelines,
403            instance_belt,
404            viewport_size: size,
405            path_tiles: HashMap::default(),
406            atlas,
407            atlas_sampler,
408            #[cfg(target_os = "macos")]
409            core_video_texture_cache,
410        }
411    }
412
413    fn wait_for_gpu(&mut self) {
414        if let Some(last_sp) = self.last_sync_point.take() {
415            if !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {
416                panic!("GPU hung");
417            }
418        }
419    }
420
421    pub fn update_drawable_size(&mut self, size: Size<f64>) {
422        let gpu_size = gpu::Extent {
423            width: size.width as u32,
424            height: size.height as u32,
425            depth: 1,
426        };
427
428        if gpu_size != self.viewport_size() {
429            self.wait_for_gpu();
430            self.gpu.resize(Self::make_surface_config(gpu_size));
431            self.viewport_size = gpu_size;
432        }
433    }
434
435    pub fn viewport_size(&self) -> gpu::Extent {
436        self.viewport_size
437    }
438
439    pub fn sprite_atlas(&self) -> &Arc<BladeAtlas> {
440        &self.atlas
441    }
442
443    #[cfg(target_os = "macos")]
444    pub fn layer(&self) -> metal::MetalLayer {
445        self.gpu.metal_layer().unwrap()
446    }
447
448    #[cfg(target_os = "macos")]
449    pub fn layer_ptr(&self) -> *mut metal::CAMetalLayer {
450        use metal::foreign_types::ForeignType as _;
451        self.gpu.metal_layer().unwrap().as_ptr()
452    }
453
454    #[profiling::function]
455    fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
456        self.path_tiles.clear();
457        let mut vertices_by_texture_id = HashMap::default();
458
459        for path in paths {
460            let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
461            let tile = self.atlas.allocate_for_rendering(
462                clipped_bounds.size.map(Into::into),
463                AtlasTextureKind::Path,
464                &mut self.command_encoder,
465            );
466            vertices_by_texture_id
467                .entry(tile.texture_id)
468                .or_insert(Vec::new())
469                .extend(path.vertices.iter().map(|vertex| PathVertex {
470                    xy_position: vertex.xy_position - clipped_bounds.origin
471                        + tile.bounds.origin.map(Into::into),
472                    st_position: vertex.st_position,
473                    content_mask: ContentMask {
474                        bounds: tile.bounds.map(Into::into),
475                    },
476                }));
477            self.path_tiles.insert(path.id, tile);
478        }
479
480        for (texture_id, vertices) in vertices_by_texture_id {
481            let tex_info = self.atlas.get_texture_info(texture_id);
482            let globals = GlobalParams {
483                viewport_size: [tex_info.size.width as f32, tex_info.size.height as f32],
484                pad: [0; 2],
485            };
486
487            let vertex_buf = unsafe { self.instance_belt.alloc_data(&vertices, &self.gpu) };
488            let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
489                colors: &[gpu::RenderTarget {
490                    view: tex_info.raw_view,
491                    init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack),
492                    finish_op: gpu::FinishOp::Store,
493                }],
494                depth_stencil: None,
495            });
496
497            let mut encoder = pass.with(&self.pipelines.path_rasterization);
498            encoder.bind(
499                0,
500                &ShaderPathRasterizationData {
501                    globals,
502                    b_path_vertices: vertex_buf,
503                },
504            );
505            encoder.draw(0, vertices.len() as u32, 0, 1);
506        }
507    }
508
509    pub fn destroy(&mut self) {
510        self.wait_for_gpu();
511        self.atlas.destroy();
512        self.instance_belt.destroy(&self.gpu);
513        self.gpu.destroy_command_encoder(&mut self.command_encoder);
514    }
515
516    pub fn draw(&mut self, scene: &Scene) {
517        self.command_encoder.start();
518        self.atlas.before_frame(&mut self.command_encoder);
519        self.rasterize_paths(scene.paths());
520
521        let frame = {
522            profiling::scope!("acquire frame");
523            self.gpu.acquire_frame()
524        };
525        self.command_encoder.init_texture(frame.texture());
526
527        let globals = GlobalParams {
528            viewport_size: [
529                self.viewport_size.width as f32,
530                self.viewport_size.height as f32,
531            ],
532            pad: [0; 2],
533        };
534
535        if let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
536            colors: &[gpu::RenderTarget {
537                view: frame.texture_view(),
538                init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
539                finish_op: gpu::FinishOp::Store,
540            }],
541            depth_stencil: None,
542        }) {
543            profiling::scope!("render pass");
544            for batch in scene.batches() {
545                match batch {
546                    PrimitiveBatch::Quads(quads) => {
547                        let instance_buf =
548                            unsafe { self.instance_belt.alloc_data(quads, &self.gpu) };
549                        let mut encoder = pass.with(&self.pipelines.quads);
550                        encoder.bind(
551                            0,
552                            &ShaderQuadsData {
553                                globals,
554                                b_quads: instance_buf,
555                            },
556                        );
557                        encoder.draw(0, 4, 0, quads.len() as u32);
558                    }
559                    PrimitiveBatch::Shadows(shadows) => {
560                        let instance_buf =
561                            unsafe { self.instance_belt.alloc_data(shadows, &self.gpu) };
562                        let mut encoder = pass.with(&self.pipelines.shadows);
563                        encoder.bind(
564                            0,
565                            &ShaderShadowsData {
566                                globals,
567                                b_shadows: instance_buf,
568                            },
569                        );
570                        encoder.draw(0, 4, 0, shadows.len() as u32);
571                    }
572                    PrimitiveBatch::Paths(paths) => {
573                        let mut encoder = pass.with(&self.pipelines.paths);
574                        // todo(linux): group by texture ID
575                        for path in paths {
576                            let tile = &self.path_tiles[&path.id];
577                            let tex_info = self.atlas.get_texture_info(tile.texture_id);
578                            let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
579                            let sprites = [PathSprite {
580                                bounds: Bounds {
581                                    origin: origin.map(|p| p.floor()),
582                                    size: tile.bounds.size.map(Into::into),
583                                },
584                                color: path.color,
585                                tile: (*tile).clone(),
586                            }];
587
588                            let instance_buf =
589                                unsafe { self.instance_belt.alloc_data(&sprites, &self.gpu) };
590                            encoder.bind(
591                                0,
592                                &ShaderPathsData {
593                                    globals,
594                                    t_sprite: tex_info.raw_view,
595                                    s_sprite: self.atlas_sampler,
596                                    b_path_sprites: instance_buf,
597                                },
598                            );
599                            encoder.draw(0, 4, 0, sprites.len() as u32);
600                        }
601                    }
602                    PrimitiveBatch::Underlines(underlines) => {
603                        let instance_buf =
604                            unsafe { self.instance_belt.alloc_data(underlines, &self.gpu) };
605                        let mut encoder = pass.with(&self.pipelines.underlines);
606                        encoder.bind(
607                            0,
608                            &ShaderUnderlinesData {
609                                globals,
610                                b_underlines: instance_buf,
611                            },
612                        );
613                        encoder.draw(0, 4, 0, underlines.len() as u32);
614                    }
615                    PrimitiveBatch::MonochromeSprites {
616                        texture_id,
617                        sprites,
618                    } => {
619                        let tex_info = self.atlas.get_texture_info(texture_id);
620                        let instance_buf =
621                            unsafe { self.instance_belt.alloc_data(sprites, &self.gpu) };
622                        let mut encoder = pass.with(&self.pipelines.mono_sprites);
623                        encoder.bind(
624                            0,
625                            &ShaderMonoSpritesData {
626                                globals,
627                                t_sprite: tex_info.raw_view,
628                                s_sprite: self.atlas_sampler,
629                                b_mono_sprites: instance_buf,
630                            },
631                        );
632                        encoder.draw(0, 4, 0, sprites.len() as u32);
633                    }
634                    PrimitiveBatch::PolychromeSprites {
635                        texture_id,
636                        sprites,
637                    } => {
638                        let tex_info = self.atlas.get_texture_info(texture_id);
639                        let instance_buf =
640                            unsafe { self.instance_belt.alloc_data(sprites, &self.gpu) };
641                        let mut encoder = pass.with(&self.pipelines.poly_sprites);
642                        encoder.bind(
643                            0,
644                            &ShaderPolySpritesData {
645                                globals,
646                                t_sprite: tex_info.raw_view,
647                                s_sprite: self.atlas_sampler,
648                                b_poly_sprites: instance_buf,
649                            },
650                        );
651                        encoder.draw(0, 4, 0, sprites.len() as u32);
652                    }
653                    PrimitiveBatch::Surfaces(surfaces) => {
654                        let mut _encoder = pass.with(&self.pipelines.surfaces);
655
656                        for surface in surfaces {
657                            #[cfg(not(target_os = "macos"))]
658                            {
659                                let _ = surface;
660                                continue;
661                            };
662
663                            #[cfg(target_os = "macos")]
664                            {
665                                let (t_y, t_cb_cr) = {
666                                    use core_foundation::base::TCFType as _;
667                                    use std::ptr;
668
669                                    assert_eq!(
670                                    surface.image_buffer.pixel_format_type(),
671                                    media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
672                                );
673
674                                    let y_texture = unsafe {
675                                        self.core_video_texture_cache
676                                            .create_texture_from_image(
677                                                surface.image_buffer.as_concrete_TypeRef(),
678                                                ptr::null(),
679                                                metal::MTLPixelFormat::R8Unorm,
680                                                surface.image_buffer.plane_width(0),
681                                                surface.image_buffer.plane_height(0),
682                                                0,
683                                            )
684                                            .unwrap()
685                                    };
686                                    let cb_cr_texture = unsafe {
687                                        self.core_video_texture_cache
688                                            .create_texture_from_image(
689                                                surface.image_buffer.as_concrete_TypeRef(),
690                                                ptr::null(),
691                                                metal::MTLPixelFormat::RG8Unorm,
692                                                surface.image_buffer.plane_width(1),
693                                                surface.image_buffer.plane_height(1),
694                                                1,
695                                            )
696                                            .unwrap()
697                                    };
698                                    (
699                                        gpu::TextureView::from_metal_texture(
700                                            y_texture.as_texture_ref(),
701                                        ),
702                                        gpu::TextureView::from_metal_texture(
703                                            cb_cr_texture.as_texture_ref(),
704                                        ),
705                                    )
706                                };
707
708                                _encoder.bind(
709                                    0,
710                                    &ShaderSurfacesData {
711                                        globals,
712                                        surface_locals: SurfaceParams {
713                                            bounds: surface.bounds.into(),
714                                            content_mask: surface.content_mask.bounds.into(),
715                                        },
716                                        t_y,
717                                        t_cb_cr,
718                                        s_surface: self.atlas_sampler,
719                                    },
720                                );
721
722                                _encoder.draw(0, 4, 0, 1);
723                            }
724                        }
725                    }
726                }
727            }
728        }
729
730        self.command_encoder.present(frame);
731        let sync_point = self.gpu.submit(&mut self.command_encoder);
732
733        profiling::scope!("finish");
734        self.instance_belt.flush(&sync_point);
735        self.atlas.after_frame(&sync_point);
736        self.atlas.clear_textures(AtlasTextureKind::Path);
737
738        self.wait_for_gpu();
739        self.last_sync_point = Some(sync_point);
740    }
741}