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