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 transparent: bool,
32) -> Renderer {
33 use raw_window_handle as rwh;
34 struct RawWindow {
35 view: *mut c_void,
36 }
37
38 impl rwh::HasWindowHandle for RawWindow {
39 fn window_handle(&self) -> Result<rwh::WindowHandle, rwh::HandleError> {
40 let view = NonNull::new(self.view).unwrap();
41 let handle = rwh::AppKitWindowHandle::new(view);
42 Ok(unsafe { rwh::WindowHandle::borrow_raw(handle.into()) })
43 }
44 }
45 impl rwh::HasDisplayHandle for RawWindow {
46 fn display_handle(&self) -> Result<rwh::DisplayHandle, rwh::HandleError> {
47 let handle = rwh::AppKitDisplayHandle::new();
48 Ok(unsafe { rwh::DisplayHandle::borrow_raw(handle.into()) })
49 }
50 }
51
52 let gpu = Arc::new(
53 gpu::Context::init_windowed(
54 &RawWindow {
55 view: native_view as *mut _,
56 },
57 gpu::ContextDesc {
58 validation: cfg!(debug_assertions),
59 capture: false,
60 overlay: false,
61 },
62 )
63 .unwrap(),
64 );
65
66 BladeRenderer::new(
67 gpu,
68 BladeSurfaceConfig {
69 size: gpu::Extent {
70 width: bounds.width as u32,
71 height: bounds.height as u32,
72 depth: 1,
73 },
74 transparent,
75 },
76 )
77}
78
79#[repr(C)]
80#[derive(Clone, Copy, Pod, Zeroable)]
81struct GlobalParams {
82 viewport_size: [f32; 2],
83 premultiplied_alpha: u32,
84 pad: u32,
85}
86
87//Note: we can't use `Bounds` directly here because
88// it doesn't implement Pod + Zeroable
89#[repr(C)]
90#[derive(Clone, Copy, Pod, Zeroable)]
91struct PodBounds {
92 origin: [f32; 2],
93 size: [f32; 2],
94}
95
96impl From<Bounds<ScaledPixels>> for PodBounds {
97 fn from(bounds: Bounds<ScaledPixels>) -> Self {
98 Self {
99 origin: [bounds.origin.x.0, bounds.origin.y.0],
100 size: [bounds.size.width.0, bounds.size.height.0],
101 }
102 }
103}
104
105#[repr(C)]
106#[derive(Clone, Copy, Pod, Zeroable)]
107struct SurfaceParams {
108 bounds: PodBounds,
109 content_mask: PodBounds,
110}
111
112#[derive(blade_macros::ShaderData)]
113struct ShaderQuadsData {
114 globals: GlobalParams,
115 b_quads: gpu::BufferPiece,
116}
117
118#[derive(blade_macros::ShaderData)]
119struct ShaderShadowsData {
120 globals: GlobalParams,
121 b_shadows: gpu::BufferPiece,
122}
123
124#[derive(blade_macros::ShaderData)]
125struct ShaderPathRasterizationData {
126 globals: GlobalParams,
127 b_path_vertices: gpu::BufferPiece,
128}
129
130#[derive(blade_macros::ShaderData)]
131struct ShaderPathsData {
132 globals: GlobalParams,
133 t_sprite: gpu::TextureView,
134 s_sprite: gpu::Sampler,
135 b_path_sprites: gpu::BufferPiece,
136}
137
138#[derive(blade_macros::ShaderData)]
139struct ShaderUnderlinesData {
140 globals: GlobalParams,
141 b_underlines: gpu::BufferPiece,
142}
143
144#[derive(blade_macros::ShaderData)]
145struct ShaderMonoSpritesData {
146 globals: GlobalParams,
147 t_sprite: gpu::TextureView,
148 s_sprite: gpu::Sampler,
149 b_mono_sprites: gpu::BufferPiece,
150}
151
152#[derive(blade_macros::ShaderData)]
153struct ShaderPolySpritesData {
154 globals: GlobalParams,
155 t_sprite: gpu::TextureView,
156 s_sprite: gpu::Sampler,
157 b_poly_sprites: gpu::BufferPiece,
158}
159
160#[derive(blade_macros::ShaderData)]
161struct ShaderSurfacesData {
162 globals: GlobalParams,
163 surface_locals: SurfaceParams,
164 t_y: gpu::TextureView,
165 t_cb_cr: gpu::TextureView,
166 s_surface: gpu::Sampler,
167}
168
169#[derive(Clone, Debug, Eq, PartialEq)]
170#[repr(C)]
171struct PathSprite {
172 bounds: Bounds<ScaledPixels>,
173 color: Hsla,
174 tile: AtlasTile,
175}
176
177struct BladePipelines {
178 quads: gpu::RenderPipeline,
179 shadows: gpu::RenderPipeline,
180 path_rasterization: gpu::RenderPipeline,
181 paths: gpu::RenderPipeline,
182 underlines: gpu::RenderPipeline,
183 mono_sprites: gpu::RenderPipeline,
184 poly_sprites: gpu::RenderPipeline,
185 surfaces: gpu::RenderPipeline,
186}
187
188impl BladePipelines {
189 fn new(gpu: &gpu::Context, surface_info: gpu::SurfaceInfo) -> Self {
190 use gpu::ShaderData as _;
191
192 log::info!(
193 "Initializing Blade pipelines for surface {:?}",
194 surface_info
195 );
196 let shader = gpu.create_shader(gpu::ShaderDesc {
197 source: include_str!("shaders.wgsl"),
198 });
199 shader.check_struct_size::<GlobalParams>();
200 shader.check_struct_size::<SurfaceParams>();
201 shader.check_struct_size::<Quad>();
202 shader.check_struct_size::<Shadow>();
203 assert_eq!(
204 mem::size_of::<PathVertex<ScaledPixels>>(),
205 shader.get_struct_size("PathVertex") as usize,
206 );
207 shader.check_struct_size::<PathSprite>();
208 shader.check_struct_size::<Underline>();
209 shader.check_struct_size::<MonochromeSprite>();
210 shader.check_struct_size::<PolychromeSprite>();
211
212 // See https://apoorvaj.io/alpha-compositing-opengl-blending-and-premultiplied-alpha/
213 let blend_mode = match surface_info.alpha {
214 gpu::AlphaMode::Ignored => gpu::BlendState::ALPHA_BLENDING,
215 gpu::AlphaMode::PreMultiplied => gpu::BlendState::PREMULTIPLIED_ALPHA_BLENDING,
216 gpu::AlphaMode::PostMultiplied => gpu::BlendState::ALPHA_BLENDING,
217 };
218 let color_targets = &[gpu::ColorTargetState {
219 format: surface_info.format,
220 blend: Some(blend_mode),
221 write_mask: gpu::ColorWrites::default(),
222 }];
223
224 Self {
225 quads: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
226 name: "quads",
227 data_layouts: &[&ShaderQuadsData::layout()],
228 vertex: shader.at("vs_quad"),
229 vertex_fetches: &[],
230 primitive: gpu::PrimitiveState {
231 topology: gpu::PrimitiveTopology::TriangleStrip,
232 ..Default::default()
233 },
234 depth_stencil: None,
235 fragment: shader.at("fs_quad"),
236 color_targets,
237 }),
238 shadows: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
239 name: "shadows",
240 data_layouts: &[&ShaderShadowsData::layout()],
241 vertex: shader.at("vs_shadow"),
242 vertex_fetches: &[],
243 primitive: gpu::PrimitiveState {
244 topology: gpu::PrimitiveTopology::TriangleStrip,
245 ..Default::default()
246 },
247 depth_stencil: None,
248 fragment: shader.at("fs_shadow"),
249 color_targets,
250 }),
251 path_rasterization: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
252 name: "path_rasterization",
253 data_layouts: &[&ShaderPathRasterizationData::layout()],
254 vertex: shader.at("vs_path_rasterization"),
255 vertex_fetches: &[],
256 primitive: gpu::PrimitiveState {
257 topology: gpu::PrimitiveTopology::TriangleList,
258 ..Default::default()
259 },
260 depth_stencil: None,
261 fragment: shader.at("fs_path_rasterization"),
262 color_targets: &[gpu::ColorTargetState {
263 format: PATH_TEXTURE_FORMAT,
264 blend: Some(gpu::BlendState::ADDITIVE),
265 write_mask: gpu::ColorWrites::default(),
266 }],
267 }),
268 paths: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
269 name: "paths",
270 data_layouts: &[&ShaderPathsData::layout()],
271 vertex: shader.at("vs_path"),
272 vertex_fetches: &[],
273 primitive: gpu::PrimitiveState {
274 topology: gpu::PrimitiveTopology::TriangleStrip,
275 ..Default::default()
276 },
277 depth_stencil: None,
278 fragment: shader.at("fs_path"),
279 color_targets,
280 }),
281 underlines: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
282 name: "underlines",
283 data_layouts: &[&ShaderUnderlinesData::layout()],
284 vertex: shader.at("vs_underline"),
285 vertex_fetches: &[],
286 primitive: gpu::PrimitiveState {
287 topology: gpu::PrimitiveTopology::TriangleStrip,
288 ..Default::default()
289 },
290 depth_stencil: None,
291 fragment: shader.at("fs_underline"),
292 color_targets,
293 }),
294 mono_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
295 name: "mono-sprites",
296 data_layouts: &[&ShaderMonoSpritesData::layout()],
297 vertex: shader.at("vs_mono_sprite"),
298 vertex_fetches: &[],
299 primitive: gpu::PrimitiveState {
300 topology: gpu::PrimitiveTopology::TriangleStrip,
301 ..Default::default()
302 },
303 depth_stencil: None,
304 fragment: shader.at("fs_mono_sprite"),
305 color_targets,
306 }),
307 poly_sprites: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
308 name: "poly-sprites",
309 data_layouts: &[&ShaderPolySpritesData::layout()],
310 vertex: shader.at("vs_poly_sprite"),
311 vertex_fetches: &[],
312 primitive: gpu::PrimitiveState {
313 topology: gpu::PrimitiveTopology::TriangleStrip,
314 ..Default::default()
315 },
316 depth_stencil: None,
317 fragment: shader.at("fs_poly_sprite"),
318 color_targets,
319 }),
320 surfaces: gpu.create_render_pipeline(gpu::RenderPipelineDesc {
321 name: "surfaces",
322 data_layouts: &[&ShaderSurfacesData::layout()],
323 vertex: shader.at("vs_surface"),
324 vertex_fetches: &[],
325 primitive: gpu::PrimitiveState {
326 topology: gpu::PrimitiveTopology::TriangleStrip,
327 ..Default::default()
328 },
329 depth_stencil: None,
330 fragment: shader.at("fs_surface"),
331 color_targets,
332 }),
333 }
334 }
335}
336
337pub struct BladeSurfaceConfig {
338 pub size: gpu::Extent,
339 pub transparent: bool,
340}
341
342pub struct BladeRenderer {
343 gpu: Arc<gpu::Context>,
344 surface_config: gpu::SurfaceConfig,
345 alpha_mode: gpu::AlphaMode,
346 command_encoder: gpu::CommandEncoder,
347 last_sync_point: Option<gpu::SyncPoint>,
348 pipelines: BladePipelines,
349 instance_belt: BladeBelt,
350 path_tiles: HashMap<PathId, AtlasTile>,
351 atlas: Arc<BladeAtlas>,
352 atlas_sampler: gpu::Sampler,
353 #[cfg(target_os = "macos")]
354 core_video_texture_cache: CVMetalTextureCache,
355}
356
357impl BladeRenderer {
358 pub fn new(gpu: Arc<gpu::Context>, config: BladeSurfaceConfig) -> Self {
359 let surface_config = gpu::SurfaceConfig {
360 size: config.size,
361 usage: gpu::TextureUsage::TARGET,
362 display_sync: gpu::DisplaySync::Recent,
363 //Note: this matches the original logic of the Metal backend,
364 // but ultimaterly we need to switch to `Linear`.
365 color_space: gpu::ColorSpace::Srgb,
366 allow_exclusive_full_screen: false,
367 transparent: config.transparent,
368 };
369 let surface_info = gpu.resize(surface_config);
370
371 let command_encoder = gpu.create_command_encoder(gpu::CommandEncoderDesc {
372 name: "main",
373 buffer_count: 2,
374 });
375 let pipelines = BladePipelines::new(&gpu, surface_info);
376 let instance_belt = BladeBelt::new(BladeBeltDescriptor {
377 memory: gpu::Memory::Shared,
378 min_chunk_size: 0x1000,
379 alignment: 0x40, // Vulkan `minStorageBufferOffsetAlignment` on Intel Xe
380 });
381 let atlas = Arc::new(BladeAtlas::new(&gpu));
382 let atlas_sampler = gpu.create_sampler(gpu::SamplerDesc {
383 name: "atlas",
384 mag_filter: gpu::FilterMode::Linear,
385 min_filter: gpu::FilterMode::Linear,
386 ..Default::default()
387 });
388
389 #[cfg(target_os = "macos")]
390 let core_video_texture_cache = unsafe {
391 use foreign_types::ForeignType as _;
392 CVMetalTextureCache::new(gpu.metal_device().as_ptr()).unwrap()
393 };
394
395 Self {
396 gpu,
397 surface_config,
398 alpha_mode: surface_info.alpha,
399 command_encoder,
400 last_sync_point: None,
401 pipelines,
402 instance_belt,
403 path_tiles: HashMap::default(),
404 atlas,
405 atlas_sampler,
406 #[cfg(target_os = "macos")]
407 core_video_texture_cache,
408 }
409 }
410
411 fn wait_for_gpu(&mut self) {
412 if let Some(last_sp) = self.last_sync_point.take() {
413 if !self.gpu.wait_for(&last_sp, MAX_FRAME_TIME_MS) {
414 panic!("GPU hung");
415 }
416 }
417 }
418
419 pub fn update_drawable_size(&mut self, size: Size<f64>) {
420 let gpu_size = gpu::Extent {
421 width: size.width as u32,
422 height: size.height as u32,
423 depth: 1,
424 };
425
426 if gpu_size != self.surface_config.size {
427 self.wait_for_gpu();
428 self.surface_config.size = gpu_size;
429 self.gpu.resize(self.surface_config);
430 }
431 }
432
433 pub fn update_transparency(&mut self, transparent: bool) {
434 if transparent != self.surface_config.transparent {
435 self.wait_for_gpu();
436 self.surface_config.transparent = transparent;
437 let surface_info = self.gpu.resize(self.surface_config);
438 self.pipelines = BladePipelines::new(&self.gpu, surface_info);
439 self.alpha_mode = surface_info.alpha;
440 }
441 }
442
443 #[cfg_attr(target_os = "macos", allow(dead_code))]
444 pub fn viewport_size(&self) -> gpu::Extent {
445 self.surface_config.size
446 }
447
448 pub fn sprite_atlas(&self) -> &Arc<BladeAtlas> {
449 &self.atlas
450 }
451
452 #[cfg(target_os = "macos")]
453 pub fn layer(&self) -> metal::MetalLayer {
454 self.gpu.metal_layer().unwrap()
455 }
456
457 #[cfg(target_os = "macos")]
458 pub fn layer_ptr(&self) -> *mut metal::CAMetalLayer {
459 use metal::foreign_types::ForeignType as _;
460 self.gpu.metal_layer().unwrap().as_ptr()
461 }
462
463 #[profiling::function]
464 fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
465 self.path_tiles.clear();
466 let mut vertices_by_texture_id = HashMap::default();
467
468 for path in paths {
469 let clipped_bounds = path.bounds.intersect(&path.content_mask.bounds);
470 let tile = self.atlas.allocate_for_rendering(
471 clipped_bounds.size.map(Into::into),
472 AtlasTextureKind::Path,
473 &mut self.command_encoder,
474 );
475 vertices_by_texture_id
476 .entry(tile.texture_id)
477 .or_insert(Vec::new())
478 .extend(path.vertices.iter().map(|vertex| PathVertex {
479 xy_position: vertex.xy_position - clipped_bounds.origin
480 + tile.bounds.origin.map(Into::into),
481 st_position: vertex.st_position,
482 content_mask: ContentMask {
483 bounds: tile.bounds.map(Into::into),
484 },
485 }));
486 self.path_tiles.insert(path.id, tile);
487 }
488
489 for (texture_id, vertices) in vertices_by_texture_id {
490 let tex_info = self.atlas.get_texture_info(texture_id);
491 let globals = GlobalParams {
492 viewport_size: [tex_info.size.width as f32, tex_info.size.height as f32],
493 premultiplied_alpha: 0,
494 pad: 0,
495 };
496
497 let vertex_buf = unsafe { self.instance_belt.alloc_data(&vertices, &self.gpu) };
498 let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
499 colors: &[gpu::RenderTarget {
500 view: tex_info.raw_view,
501 init_op: gpu::InitOp::Clear(gpu::TextureColor::OpaqueBlack),
502 finish_op: gpu::FinishOp::Store,
503 }],
504 depth_stencil: None,
505 });
506
507 let mut encoder = pass.with(&self.pipelines.path_rasterization);
508 encoder.bind(
509 0,
510 &ShaderPathRasterizationData {
511 globals,
512 b_path_vertices: vertex_buf,
513 },
514 );
515 encoder.draw(0, vertices.len() as u32, 0, 1);
516 }
517 }
518
519 pub fn destroy(&mut self) {
520 self.wait_for_gpu();
521 self.atlas.destroy();
522 self.instance_belt.destroy(&self.gpu);
523 self.gpu.destroy_command_encoder(&mut self.command_encoder);
524 }
525
526 pub fn draw(&mut self, scene: &Scene) {
527 self.command_encoder.start();
528 self.atlas.before_frame(&mut self.command_encoder);
529 self.rasterize_paths(scene.paths());
530
531 let frame = {
532 profiling::scope!("acquire frame");
533 self.gpu.acquire_frame()
534 };
535 self.command_encoder.init_texture(frame.texture());
536
537 let globals = GlobalParams {
538 viewport_size: [
539 self.surface_config.size.width as f32,
540 self.surface_config.size.height as f32,
541 ],
542 premultiplied_alpha: match self.alpha_mode {
543 gpu::AlphaMode::Ignored | gpu::AlphaMode::PostMultiplied => 0,
544 gpu::AlphaMode::PreMultiplied => 1,
545 },
546 pad: 0,
547 };
548
549 if let mut pass = self.command_encoder.render(gpu::RenderTargetSet {
550 colors: &[gpu::RenderTarget {
551 view: frame.texture_view(),
552 init_op: gpu::InitOp::Clear(gpu::TextureColor::TransparentBlack),
553 finish_op: gpu::FinishOp::Store,
554 }],
555 depth_stencil: None,
556 }) {
557 profiling::scope!("render pass");
558 for batch in scene.batches() {
559 match batch {
560 PrimitiveBatch::Quads(quads) => {
561 let instance_buf =
562 unsafe { self.instance_belt.alloc_data(quads, &self.gpu) };
563 let mut encoder = pass.with(&self.pipelines.quads);
564 encoder.bind(
565 0,
566 &ShaderQuadsData {
567 globals,
568 b_quads: instance_buf,
569 },
570 );
571 encoder.draw(0, 4, 0, quads.len() as u32);
572 }
573 PrimitiveBatch::Shadows(shadows) => {
574 let instance_buf =
575 unsafe { self.instance_belt.alloc_data(shadows, &self.gpu) };
576 let mut encoder = pass.with(&self.pipelines.shadows);
577 encoder.bind(
578 0,
579 &ShaderShadowsData {
580 globals,
581 b_shadows: instance_buf,
582 },
583 );
584 encoder.draw(0, 4, 0, shadows.len() as u32);
585 }
586 PrimitiveBatch::Paths(paths) => {
587 let mut encoder = pass.with(&self.pipelines.paths);
588 // todo(linux): group by texture ID
589 for path in paths {
590 let tile = &self.path_tiles[&path.id];
591 let tex_info = self.atlas.get_texture_info(tile.texture_id);
592 let origin = path.bounds.intersect(&path.content_mask.bounds).origin;
593 let sprites = [PathSprite {
594 bounds: Bounds {
595 origin: origin.map(|p| p.floor()),
596 size: tile.bounds.size.map(Into::into),
597 },
598 color: path.color,
599 tile: (*tile).clone(),
600 }];
601
602 let instance_buf =
603 unsafe { self.instance_belt.alloc_data(&sprites, &self.gpu) };
604 encoder.bind(
605 0,
606 &ShaderPathsData {
607 globals,
608 t_sprite: tex_info.raw_view,
609 s_sprite: self.atlas_sampler,
610 b_path_sprites: instance_buf,
611 },
612 );
613 encoder.draw(0, 4, 0, sprites.len() as u32);
614 }
615 }
616 PrimitiveBatch::Underlines(underlines) => {
617 let instance_buf =
618 unsafe { self.instance_belt.alloc_data(underlines, &self.gpu) };
619 let mut encoder = pass.with(&self.pipelines.underlines);
620 encoder.bind(
621 0,
622 &ShaderUnderlinesData {
623 globals,
624 b_underlines: instance_buf,
625 },
626 );
627 encoder.draw(0, 4, 0, underlines.len() as u32);
628 }
629 PrimitiveBatch::MonochromeSprites {
630 texture_id,
631 sprites,
632 } => {
633 let tex_info = self.atlas.get_texture_info(texture_id);
634 let instance_buf =
635 unsafe { self.instance_belt.alloc_data(sprites, &self.gpu) };
636 let mut encoder = pass.with(&self.pipelines.mono_sprites);
637 encoder.bind(
638 0,
639 &ShaderMonoSpritesData {
640 globals,
641 t_sprite: tex_info.raw_view,
642 s_sprite: self.atlas_sampler,
643 b_mono_sprites: instance_buf,
644 },
645 );
646 encoder.draw(0, 4, 0, sprites.len() as u32);
647 }
648 PrimitiveBatch::PolychromeSprites {
649 texture_id,
650 sprites,
651 } => {
652 let tex_info = self.atlas.get_texture_info(texture_id);
653 let instance_buf =
654 unsafe { self.instance_belt.alloc_data(sprites, &self.gpu) };
655 let mut encoder = pass.with(&self.pipelines.poly_sprites);
656 encoder.bind(
657 0,
658 &ShaderPolySpritesData {
659 globals,
660 t_sprite: tex_info.raw_view,
661 s_sprite: self.atlas_sampler,
662 b_poly_sprites: instance_buf,
663 },
664 );
665 encoder.draw(0, 4, 0, sprites.len() as u32);
666 }
667 PrimitiveBatch::Surfaces(surfaces) => {
668 let mut _encoder = pass.with(&self.pipelines.surfaces);
669
670 for surface in surfaces {
671 #[cfg(not(target_os = "macos"))]
672 {
673 let _ = surface;
674 continue;
675 };
676
677 #[cfg(target_os = "macos")]
678 {
679 let (t_y, t_cb_cr) = {
680 use core_foundation::base::TCFType as _;
681 use std::ptr;
682
683 assert_eq!(
684 surface.image_buffer.pixel_format_type(),
685 media::core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
686 );
687
688 let y_texture = unsafe {
689 self.core_video_texture_cache
690 .create_texture_from_image(
691 surface.image_buffer.as_concrete_TypeRef(),
692 ptr::null(),
693 metal::MTLPixelFormat::R8Unorm,
694 surface.image_buffer.plane_width(0),
695 surface.image_buffer.plane_height(0),
696 0,
697 )
698 .unwrap()
699 };
700 let cb_cr_texture = unsafe {
701 self.core_video_texture_cache
702 .create_texture_from_image(
703 surface.image_buffer.as_concrete_TypeRef(),
704 ptr::null(),
705 metal::MTLPixelFormat::RG8Unorm,
706 surface.image_buffer.plane_width(1),
707 surface.image_buffer.plane_height(1),
708 1,
709 )
710 .unwrap()
711 };
712 (
713 gpu::TextureView::from_metal_texture(
714 y_texture.as_texture_ref(),
715 ),
716 gpu::TextureView::from_metal_texture(
717 cb_cr_texture.as_texture_ref(),
718 ),
719 )
720 };
721
722 _encoder.bind(
723 0,
724 &ShaderSurfacesData {
725 globals,
726 surface_locals: SurfaceParams {
727 bounds: surface.bounds.into(),
728 content_mask: surface.content_mask.bounds.into(),
729 },
730 t_y,
731 t_cb_cr,
732 s_surface: self.atlas_sampler,
733 },
734 );
735
736 _encoder.draw(0, 4, 0, 1);
737 }
738 }
739 }
740 }
741 }
742 }
743
744 self.command_encoder.present(frame);
745 let sync_point = self.gpu.submit(&mut self.command_encoder);
746
747 profiling::scope!("finish");
748 self.instance_belt.flush(&sync_point);
749 self.atlas.after_frame(&sync_point);
750 self.atlas.clear_textures(AtlasTextureKind::Path);
751
752 self.wait_for_gpu();
753 self.last_sync_point = Some(sync_point);
754 }
755}