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            },
 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                primitive: gpu::PrimitiveState {
212                    topology: gpu::PrimitiveTopology::TriangleStrip,
213                    ..Default::default()
214                },
215                depth_stencil: None,
216                fragment: shader.at("fs_quad"),
217                color_targets: &[gpu::ColorTargetState {
218                    format: surface_format,
219                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
220                    write_mask: gpu::ColorWrites::default(),
221                }],
222            }),
223            shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
224                name: "shadows",
225                data_layouts: &[&ShaderShadowsData::layout()],
226                vertex: shader.at("vs_shadow"),
227                primitive: gpu::PrimitiveState {
228                    topology: gpu::PrimitiveTopology::TriangleStrip,
229                    ..Default::default()
230                },
231                depth_stencil: None,
232                fragment: shader.at("fs_shadow"),
233                color_targets: &[gpu::ColorTargetState {
234                    format: surface_format,
235                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
236                    write_mask: gpu::ColorWrites::default(),
237                }],
238            }),
239            path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
240                name: "path_rasterization",
241                data_layouts: &[&ShaderPathRasterizationData::layout()],
242                vertex: shader.at("vs_path_rasterization"),
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                primitive: gpu::PrimitiveState {
260                    topology: gpu::PrimitiveTopology::TriangleStrip,
261                    ..Default::default()
262                },
263                depth_stencil: None,
264                fragment: shader.at("fs_path"),
265                color_targets: &[gpu::ColorTargetState {
266                    format: surface_format,
267                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
268                    write_mask: gpu::ColorWrites::default(),
269                }],
270            }),
271            underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
272                name: "underlines",
273                data_layouts: &[&ShaderUnderlinesData::layout()],
274                vertex: shader.at("vs_underline"),
275                primitive: gpu::PrimitiveState {
276                    topology: gpu::PrimitiveTopology::TriangleStrip,
277                    ..Default::default()
278                },
279                depth_stencil: None,
280                fragment: shader.at("fs_underline"),
281                color_targets: &[gpu::ColorTargetState {
282                    format: surface_format,
283                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
284                    write_mask: gpu::ColorWrites::default(),
285                }],
286            }),
287            mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
288                name: "mono-sprites",
289                data_layouts: &[&ShaderMonoSpritesData::layout()],
290                vertex: shader.at("vs_mono_sprite"),
291                primitive: gpu::PrimitiveState {
292                    topology: gpu::PrimitiveTopology::TriangleStrip,
293                    ..Default::default()
294                },
295                depth_stencil: None,
296                fragment: shader.at("fs_mono_sprite"),
297                color_targets: &[gpu::ColorTargetState {
298                    format: surface_format,
299                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
300                    write_mask: gpu::ColorWrites::default(),
301                }],
302            }),
303            poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
304                name: "poly-sprites",
305                data_layouts: &[&ShaderPolySpritesData::layout()],
306                vertex: shader.at("vs_poly_sprite"),
307                primitive: gpu::PrimitiveState {
308                    topology: gpu::PrimitiveTopology::TriangleStrip,
309                    ..Default::default()
310                },
311                depth_stencil: None,
312                fragment: shader.at("fs_poly_sprite"),
313                color_targets: &[gpu::ColorTargetState {
314                    format: surface_format,
315                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
316                    write_mask: gpu::ColorWrites::default(),
317                }],
318            }),
319            surfaces: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
320                name: "surfaces",
321                data_layouts: &[&ShaderSurfacesData::layout()],
322                vertex: shader.at("vs_surface"),
323                primitive: gpu::PrimitiveState {
324                    topology: gpu::PrimitiveTopology::TriangleStrip,
325                    ..Default::default()
326                },
327                depth_stencil: None,
328                fragment: shader.at("fs_surface"),
329                color_targets: &[gpu::ColorTargetState {
330                    format: surface_format,
331                    blend: Some(gpu::BlendState::ALPHA_BLENDING),
332                    write_mask: gpu::ColorWrites::default(),
333                }],
334            }),
335        }
336    }
337}
338
339pub struct BladeRenderer {
340    gpu: Arc<gpu::Context>,
341    command_encoder: gpu::CommandEncoder,
342    last_sync_point: Option<gpu::SyncPoint>,
343    pipelines: BladePipelines,
344    instance_belt: BladeBelt,
345    viewport_size: gpu::Extent,
346    path_tiles: HashMap<PathId, AtlasTile>,
347    atlas: Arc<BladeAtlas>,
348    atlas_sampler: gpu::Sampler,
349    #[cfg(target_os = "macos")]
350    core_video_texture_cache: CVMetalTextureCache,
351}
352
353impl BladeRenderer {
354    fn make_surface_config(size: gpu::Extent) -> gpu::SurfaceConfig {
355        gpu::SurfaceConfig {
356            size,
357            usage: gpu::TextureUsage::TARGET,
358            frame_count: SURFACE_FRAME_COUNT,
359            //Note: this matches the original logic of the Metal backend,
360            // but ultimaterly we need to switch to `Linear`.
361            color_space: gpu::ColorSpace::Srgb,
362        }
363    }
364
365    pub fn new(gpu: Arc<gpu::Context>, size: gpu::Extent) -> Self {
366        let surface_format = gpu.resize(Self::make_surface_config(size));
367        let command_encoder = gpu.create_command_encoder(gpu::CommandEncoderDesc {
368            name: "main",
369            buffer_count: 2,
370        });
371        let pipelines = BladePipelines::new(&gpu, surface_format);
372        let instance_belt = BladeBelt::new(BladeBeltDescriptor {
373            memory: gpu::Memory::Shared,
374            min_chunk_size: 0x1000,
375            alignment: 0x40, // Vulkan `minStorageBufferOffsetAlignment` on Intel Xe
376        });
377        let atlas = Arc::new(BladeAtlas::new(&gpu));
378        let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc {
379            name: "atlas",
380            mag_filter: gpu::FilterMode::Linear,
381            min_filter: gpu::FilterMode::Linear,
382            ..Default::default()
383        });
384
385        #[cfg(target_os = "macos")]
386        let core_video_texture_cache = unsafe {
387            use foreign_types::ForeignType as _;
388            CVMetalTextureCache::new(gpu.metal_device().as_ptr()).unwrap()
389        };
390
391        Self {
392            gpu,
393            command_encoder,
394            last_sync_point: None,
395            pipelines,
396            instance_belt,
397            viewport_size: size,
398            path_tiles: HashMap::default(),
399            atlas,
400            atlas_sampler,
401            #[cfg(target_os = "macos")]
402            core_video_texture_cache,
403        }
404    }
405
406    fn wait_for_gpu(&mut self) {
407        if let Some(last_sp) = self.last_sync_point.take() {
408            if !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {
409                panic!("GPU hung");
410            }
411        }
412    }
413
414    pub fn update_drawable_size(&mut self, size: Size<f64>) {
415        let gpu_size = gpu::Extent {
416            width: size.width as u32,
417            height: size.height as u32,
418            depth: 1,
419        };
420
421        if gpu_size != self.viewport_size() {
422            self.wait_for_gpu();
423            self.gpu.resize(Self::make_surface_config(gpu_size));
424            self.viewport_size = gpu_size;
425        }
426    }
427
428    pub fn viewport_size(&self) -> gpu::Extent {
429        self.viewport_size
430    }
431
432    pub fn sprite_atlas(&self) -> &Arc<BladeAtlas> {
433        &self.atlas
434    }
435
436    #[cfg(target_os = "macos")]
437    pub fn layer(&self) -> metal::MetalLayer {
438        self.gpu.metal_layer().unwrap()
439    }
440
441    #[cfg(target_os = "macos")]
442    pub fn layer_ptr(&self) -> *mut metal::CAMetalLayer {
443        use metal::foreign_types::ForeignType as _;
444        self.gpu.metal_layer().unwrap().as_ptr()
445    }
446
447    #[profiling::function]
448    fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
449        self.path_tiles.clear();
450        let mut vertices_by_texture_id = HashMap::default();
451
452        for path in paths {
453            let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
454            let tile = self.atlas.allocate_for_rendering(
455                clipped_bounds.size.map(Into::into),
456                AtlasTextureKind::Path,
457                &mut self.command_encoder,
458            );
459            vertices_by_texture_id
460                .entry(tile.texture_id)
461                .or_insert(Vec::new())
462                .extend(path.vertices.iter().map(|vertex| PathVertex {
463                    xy_position: vertex.xy_position - clipped_bounds.origin
464                        + tile.bounds.origin.map(Into::into),
465                    st_position: vertex.st_position,
466                    content_mask: ContentMask {
467                        bounds: tile.bounds.map(Into::into),
468                    },
469                }));
470            self.path_tiles.insert(path.id, tile);
471        }
472
473        for (texture_id, vertices) in vertices_by_texture_id {
474            let tex_info = self.atlas.get_texture_info(texture_id);
475            let globals = GlobalParams {
476                viewport_size: [tex_info.size.width as f32, tex_info.size.height as f32],
477                pad: [0; 2],
478            };
479
480            let vertex_buf = unsafe { self.instance_belt.alloc_data(&vertices, &self.gpu) };
481            let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
482                colors: &[gpu::RenderTarget {
483                    view: tex_info.raw_view,
484                    init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack),
485                    finish_op: gpu::FinishOp::Store,
486                }],
487                depth_stencil: None,
488            });
489
490            let mut encoder = pass.with(&self.pipelines.path_rasterization);
491            encoder.bind(
492                0,
493                &ShaderPathRasterizationData {
494                    globals,
495                    b_path_vertices: vertex_buf,
496                },
497            );
498            encoder.draw(0, vertices.len() as u32, 0, 1);
499        }
500    }
501
502    pub fn destroy(&mut self) {
503        self.wait_for_gpu();
504        self.atlas.destroy();
505        self.instance_belt.destroy(&self.gpu);
506        self.gpu.destroy_command_encoder(&mut self.command_encoder);
507    }
508
509    pub fn draw(&mut self, scene: &Scene) {
510        self.command_encoder.start();
511        self.atlas.before_frame(&mut self.command_encoder);
512        self.rasterize_paths(scene.paths());
513
514        let frame = {
515            profiling::scope!("acquire frame");
516            self.gpu.acquire_frame()
517        };
518        self.command_encoder.init_texture(frame.texture());
519
520        let globals = GlobalParams {
521            viewport_size: [
522                self.viewport_size.width as f32,
523                self.viewport_size.height as f32,
524            ],
525            pad: [0; 2],
526        };
527
528        if let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
529            colors: &[gpu::RenderTarget {
530                view: frame.texture_view(),
531                init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
532                finish_op: gpu::FinishOp::Store,
533            }],
534            depth_stencil: None,
535        }) {
536            profiling::scope!("render pass");
537            for batch in scene.batches() {
538                match batch {
539                    PrimitiveBatch::Quads(quads) => {
540                        let instance_buf =
541                            unsafe { self.instance_belt.alloc_data(quads, &self.gpu) };
542                        let mut encoder = pass.with(&self.pipelines.quads);
543                        encoder.bind(
544                            0,
545                            &ShaderQuadsData {
546                                globals,
547                                b_quads: instance_buf,
548                            },
549                        );
550                        encoder.draw(0, 4, 0, quads.len() as u32);
551                    }
552                    PrimitiveBatch::Shadows(shadows) => {
553                        let instance_buf =
554                            unsafe { self.instance_belt.alloc_data(shadows, &self.gpu) };
555                        let mut encoder = pass.with(&self.pipelines.shadows);
556                        encoder.bind(
557                            0,
558                            &ShaderShadowsData {
559                                globals,
560                                b_shadows: instance_buf,
561                            },
562                        );
563                        encoder.draw(0, 4, 0, shadows.len() as u32);
564                    }
565                    PrimitiveBatch::Paths(paths) => {
566                        let mut encoder = pass.with(&self.pipelines.paths);
567                        //todo!(linux): group by texture ID
568                        for path in paths {
569                            let tile = &self.path_tiles[&path.id];
570                            let tex_info = self.atlas.get_texture_info(tile.texture_id);
571                            let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
572                            let sprites = [PathSprite {
573                                bounds: Bounds {
574                                    origin: origin.map(|p| p.floor()),
575                                    size: tile.bounds.size.map(Into::into),
576                                },
577                                color: path.color,
578                                tile: (*tile).clone(),
579                            }];
580
581                            let instance_buf =
582                                unsafe { self.instance_belt.alloc_data(&sprites, &self.gpu) };
583                            encoder.bind(
584                                0,
585                                &ShaderPathsData {
586                                    globals,
587                                    t_sprite: tex_info.raw_view,
588                                    s_sprite: self.atlas_sampler,
589                                    b_path_sprites: instance_buf,
590                                },
591                            );
592                            encoder.draw(0, 4, 0, sprites.len() as u32);
593                        }
594                    }
595                    PrimitiveBatch::Underlines(underlines) => {
596                        let instance_buf =
597                            unsafe { self.instance_belt.alloc_data(underlines, &self.gpu) };
598                        let mut encoder = pass.with(&self.pipelines.underlines);
599                        encoder.bind(
600                            0,
601                            &ShaderUnderlinesData {
602                                globals,
603                                b_underlines: instance_buf,
604                            },
605                        );
606                        encoder.draw(0, 4, 0, underlines.len() as u32);
607                    }
608                    PrimitiveBatch::MonochromeSprites {
609                        texture_id,
610                        sprites,
611                    } => {
612                        let tex_info = self.atlas.get_texture_info(texture_id);
613                        let instance_buf =
614                            unsafe { self.instance_belt.alloc_data(sprites, &self.gpu) };
615                        let mut encoder = pass.with(&self.pipelines.mono_sprites);
616                        encoder.bind(
617                            0,
618                            &ShaderMonoSpritesData {
619                                globals,
620                                t_sprite: tex_info.raw_view,
621                                s_sprite: self.atlas_sampler,
622                                b_mono_sprites: instance_buf,
623                            },
624                        );
625                        encoder.draw(0, 4, 0, sprites.len() as u32);
626                    }
627                    PrimitiveBatch::PolychromeSprites {
628                        texture_id,
629                        sprites,
630                    } => {
631                        let tex_info = self.atlas.get_texture_info(texture_id);
632                        let instance_buf =
633                            unsafe { self.instance_belt.alloc_data(sprites, &self.gpu) };
634                        let mut encoder = pass.with(&self.pipelines.poly_sprites);
635                        encoder.bind(
636                            0,
637                            &ShaderPolySpritesData {
638                                globals,
639                                t_sprite: tex_info.raw_view,
640                                s_sprite: self.atlas_sampler,
641                                b_poly_sprites: instance_buf,
642                            },
643                        );
644                        encoder.draw(0, 4, 0, sprites.len() as u32);
645                    }
646                    PrimitiveBatch::Surfaces(surfaces) => {
647                        let mut _encoder = pass.with(&self.pipelines.surfaces);
648
649                        for surface in surfaces {
650                            #[cfg(not(target_os = "macos"))]
651                            {
652                                let _ = surface;
653                                continue;
654                            };
655
656                            #[cfg(target_os = "macos")]
657                            {
658                                let (t_y, t_cb_cr) = {
659                                    use core_foundation::base::TCFType as _;
660                                    use std::ptr;
661
662                                    assert_eq!(
663                                    surface.image_buffer.pixel_format_type(),
664                                    media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
665                                );
666
667                                    let y_texture = unsafe {
668                                        self.core_video_texture_cache
669                                            .create_texture_from_image(
670                                                surface.image_buffer.as_concrete_TypeRef(),
671                                                ptr::null(),
672                                                metal::MTLPixelFormat::R8Unorm,
673                                                surface.image_buffer.plane_width(0),
674                                                surface.image_buffer.plane_height(0),
675                                                0,
676                                            )
677                                            .unwrap()
678                                    };
679                                    let cb_cr_texture = unsafe {
680                                        self.core_video_texture_cache
681                                            .create_texture_from_image(
682                                                surface.image_buffer.as_concrete_TypeRef(),
683                                                ptr::null(),
684                                                metal::MTLPixelFormat::RG8Unorm,
685                                                surface.image_buffer.plane_width(1),
686                                                surface.image_buffer.plane_height(1),
687                                                1,
688                                            )
689                                            .unwrap()
690                                    };
691                                    (
692                                        gpu::TextureView::from_metal_texture(
693                                            y_texture.as_texture_ref(),
694                                        ),
695                                        gpu::TextureView::from_metal_texture(
696                                            cb_cr_texture.as_texture_ref(),
697                                        ),
698                                    )
699                                };
700
701                                _encoder.bind(
702                                    0,
703                                    &ShaderSurfacesData {
704                                        globals,
705                                        surface_locals: SurfaceParams {
706                                            bounds: surface.bounds.into(),
707                                            content_mask: surface.content_mask.bounds.into(),
708                                        },
709                                        t_y,
710                                        t_cb_cr,
711                                        s_surface: self.atlas_sampler,
712                                    },
713                                );
714
715                                _encoder.draw(0, 4, 0, 1);
716                            }
717                        }
718                    }
719                }
720            }
721        }
722
723        self.command_encoder.present(frame);
724        let sync_point = self.gpu.submit(&mut self.command_encoder);
725
726        profiling::scope!("finish");
727        self.instance_belt.flush(&sync_point);
728        self.atlas.after_frame(&sync_point);
729        self.atlas.clear_textures(AtlasTextureKind::Path);
730
731        self.wait_for_gpu();
732        self.last_sync_point = Some(sync_point);
733    }
734}