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}