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