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