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}