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