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 fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
448 self.path_tiles.clear();
449 let mut vertices_by_texture_id = HashMap::default();
450
451 for path in paths {
452 let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
453 let tile = self.atlas.allocate_for_rendering(
454 clipped_bounds.size.map(Into::into),
455 AtlasTextureKind::Path,
456 &mut self.command_encoder,
457 );
458 vertices_by_texture_id
459 .entry(tile.texture_id)
460 .or_insert(Vec::new())
461 .extend(path.vertices.iter().map(|vertex| PathVertex {
462 xy_position: vertex.xy_position - clipped_bounds.origin
463 + tile.bounds.origin.map(Into::into),
464 st_position: vertex.st_position,
465 content_mask: ContentMask {
466 bounds: tile.bounds.map(Into::into),
467 },
468 }));
469 self.path_tiles.insert(path.id, tile);
470 }
471
472 for (texture_id, vertices) in vertices_by_texture_id {
473 let tex_info = self.atlas.get_texture_info(texture_id);
474 let globals = GlobalParams {
475 viewport_size: [tex_info.size.width as f32, tex_info.size.height as f32],
476 pad: [0; 2],
477 };
478
479 let vertex_buf = unsafe { self.instance_belt.alloc_data(&vertices, &self.gpu) };
480 let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
481 colors: &[gpu::RenderTarget {
482 view: tex_info.raw_view,
483 init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack),
484 finish_op: gpu::FinishOp::Store,
485 }],
486 depth_stencil: None,
487 });
488
489 let mut encoder = pass.with(&self.pipelines.path_rasterization);
490 encoder.bind(
491 0,
492 &ShaderPathRasterizationData {
493 globals,
494 b_path_vertices: vertex_buf,
495 },
496 );
497 encoder.draw(0, vertices.len() as u32, 0, 1);
498 }
499 }
500
501 pub fn destroy(&mut self) {
502 self.wait_for_gpu();
503 self.atlas.destroy();
504 self.instance_belt.destroy(&self.gpu);
505 self.gpu.destroy_command_encoder(&mut self.command_encoder);
506 }
507
508 pub fn draw(&mut self, scene: &Scene) {
509 let frame = self.gpu.acquire_frame();
510 self.command_encoder.start();
511 self.command_encoder.init_texture(frame.texture());
512
513 self.atlas.before_frame(&mut self.command_encoder);
514 self.rasterize_paths(scene.paths());
515
516 let globals = GlobalParams {
517 viewport_size: [
518 self.viewport_size.width as f32,
519 self.viewport_size.height as f32,
520 ],
521 pad: [0; 2],
522 };
523
524 if let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
525 colors: &[gpu::RenderTarget {
526 view: frame.texture_view(),
527 init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
528 finish_op: gpu::FinishOp::Store,
529 }],
530 depth_stencil: None,
531 }) {
532 for batch in scene.batches() {
533 match batch {
534 PrimitiveBatch::Quads(quads) => {
535 let instance_buf =
536 unsafe { self.instance_belt.alloc_data(quads, &self.gpu) };
537 let mut encoder = pass.with(&self.pipelines.quads);
538 encoder.bind(
539 0,
540 &ShaderQuadsData {
541 globals,
542 b_quads: instance_buf,
543 },
544 );
545 encoder.draw(0, 4, 0, quads.len() as u32);
546 }
547 PrimitiveBatch::Shadows(shadows) => {
548 let instance_buf =
549 unsafe { self.instance_belt.alloc_data(shadows, &self.gpu) };
550 let mut encoder = pass.with(&self.pipelines.shadows);
551 encoder.bind(
552 0,
553 &ShaderShadowsData {
554 globals,
555 b_shadows: instance_buf,
556 },
557 );
558 encoder.draw(0, 4, 0, shadows.len() as u32);
559 }
560 PrimitiveBatch::Paths(paths) => {
561 let mut encoder = pass.with(&self.pipelines.paths);
562 //todo!(linux): group by texture ID
563 for path in paths {
564 let tile = &self.path_tiles[&path.id];
565 let tex_info = self.atlas.get_texture_info(tile.texture_id);
566 let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
567 let sprites = [PathSprite {
568 bounds: Bounds {
569 origin: origin.map(|p| p.floor()),
570 size: tile.bounds.size.map(Into::into),
571 },
572 color: path.color,
573 tile: (*tile).clone(),
574 }];
575
576 let instance_buf =
577 unsafe { self.instance_belt.alloc_data(&sprites, &self.gpu) };
578 encoder.bind(
579 0,
580 &ShaderPathsData {
581 globals,
582 t_sprite: tex_info.raw_view,
583 s_sprite: self.atlas_sampler,
584 b_path_sprites: instance_buf,
585 },
586 );
587 encoder.draw(0, 4, 0, sprites.len() as u32);
588 }
589 }
590 PrimitiveBatch::Underlines(underlines) => {
591 let instance_buf =
592 unsafe { self.instance_belt.alloc_data(underlines, &self.gpu) };
593 let mut encoder = pass.with(&self.pipelines.underlines);
594 encoder.bind(
595 0,
596 &ShaderUnderlinesData {
597 globals,
598 b_underlines: instance_buf,
599 },
600 );
601 encoder.draw(0, 4, 0, underlines.len() as u32);
602 }
603 PrimitiveBatch::MonochromeSprites {
604 texture_id,
605 sprites,
606 } => {
607 let tex_info = self.atlas.get_texture_info(texture_id);
608 let instance_buf =
609 unsafe { self.instance_belt.alloc_data(sprites, &self.gpu) };
610 let mut encoder = pass.with(&self.pipelines.mono_sprites);
611 encoder.bind(
612 0,
613 &ShaderMonoSpritesData {
614 globals,
615 t_sprite: tex_info.raw_view,
616 s_sprite: self.atlas_sampler,
617 b_mono_sprites: instance_buf,
618 },
619 );
620 encoder.draw(0, 4, 0, sprites.len() as u32);
621 }
622 PrimitiveBatch::PolychromeSprites {
623 texture_id,
624 sprites,
625 } => {
626 let tex_info = self.atlas.get_texture_info(texture_id);
627 let instance_buf =
628 unsafe { self.instance_belt.alloc_data(sprites, &self.gpu) };
629 let mut encoder = pass.with(&self.pipelines.poly_sprites);
630 encoder.bind(
631 0,
632 &ShaderPolySpritesData {
633 globals,
634 t_sprite: tex_info.raw_view,
635 s_sprite: self.atlas_sampler,
636 b_poly_sprites: instance_buf,
637 },
638 );
639 encoder.draw(0, 4, 0, sprites.len() as u32);
640 }
641 PrimitiveBatch::Surfaces(surfaces) => {
642 let mut _encoder = pass.with(&self.pipelines.surfaces);
643
644 for surface in surfaces {
645 #[cfg(not(target_os = "macos"))]
646 {
647 let _ = surface;
648 continue;
649 };
650
651 #[cfg(target_os = "macos")]
652 {
653 let (t_y, t_cb_cr) = {
654 use core_foundation::base::TCFType as _;
655 use std::ptr;
656
657 assert_eq!(
658 surface.image_buffer.pixel_format_type(),
659 media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
660 );
661
662 let y_texture = unsafe {
663 self.core_video_texture_cache
664 .create_texture_from_image(
665 surface.image_buffer.as_concrete_TypeRef(),
666 ptr::null(),
667 metal::MTLPixelFormat::R8Unorm,
668 surface.image_buffer.plane_width(0),
669 surface.image_buffer.plane_height(0),
670 0,
671 )
672 .unwrap()
673 };
674 let cb_cr_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::RG8Unorm,
680 surface.image_buffer.plane_width(1),
681 surface.image_buffer.plane_height(1),
682 1,
683 )
684 .unwrap()
685 };
686 (
687 gpu::TextureView::from_metal_texture(
688 y_texture.as_texture_ref(),
689 ),
690 gpu::TextureView::from_metal_texture(
691 cb_cr_texture.as_texture_ref(),
692 ),
693 )
694 };
695
696 _encoder.bind(
697 0,
698 &ShaderSurfacesData {
699 globals,
700 surface_locals: SurfaceParams {
701 bounds: surface.bounds.into(),
702 content_mask: surface.content_mask.bounds.into(),
703 },
704 t_y,
705 t_cb_cr,
706 s_surface: self.atlas_sampler,
707 },
708 );
709
710 _encoder.draw(0, 4, 0, 1);
711 }
712 }
713 }
714 }
715 }
716 }
717
718 self.command_encoder.present(frame);
719 let sync_point = self.gpu.submit(&mut self.command_encoder);
720
721 self.instance_belt.flush(&sync_point);
722 self.atlas.after_frame(&sync_point);
723 self.atlas.clear_textures(AtlasTextureKind::Path);
724
725 self.wait_for_gpu();
726 self.last_sync_point = Some(sync_point);
727 }
728}