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