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}