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