1use super::{atlas::AtlasAllocator, image_cache::ImageCache, sprite_cache::SpriteCache};
2use crate::{
3 color::Color,
4 geometry::{
5 rect::RectF,
6 vector::{vec2f, vec2i, Vector2F},
7 },
8 platform,
9 scene::{Glyph, Icon, Image, ImageGlyph, Layer, Quad, Scene, Shadow, Underline},
10};
11use cocoa::{
12 base::{NO, YES},
13 foundation::NSUInteger,
14 quartzcore::AutoresizingMask,
15};
16use core_foundation::base::TCFType;
17use foreign_types::ForeignTypeRef;
18use log::warn;
19use media::core_video::{self, CVMetalTextureCache};
20use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
21use objc::{self, msg_send, sel, sel_impl};
22use shaders::ToFloat2 as _;
23use std::{collections::HashMap, ffi::c_void, iter::Peekable, mem, ptr, sync::Arc, vec};
24
25const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
26const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
27
28pub struct Renderer {
29 layer: metal::MetalLayer,
30 command_queue: CommandQueue,
31 sprite_cache: SpriteCache,
32 image_cache: ImageCache,
33 path_atlases: AtlasAllocator,
34 quad_pipeline_state: metal::RenderPipelineState,
35 shadow_pipeline_state: metal::RenderPipelineState,
36 sprite_pipeline_state: metal::RenderPipelineState,
37 image_pipeline_state: metal::RenderPipelineState,
38 surface_pipeline_state: metal::RenderPipelineState,
39 path_atlas_pipeline_state: metal::RenderPipelineState,
40 underline_pipeline_state: metal::RenderPipelineState,
41 unit_vertices: metal::Buffer,
42 instances: metal::Buffer,
43 cv_texture_cache: core_video::CVMetalTextureCache,
44}
45
46struct PathSprite {
47 layer_id: usize,
48 atlas_id: usize,
49 shader_data: shaders::GPUISprite,
50}
51
52pub struct Surface {
53 pub bounds: RectF,
54 pub image_buffer: core_video::CVImageBuffer,
55}
56
57impl Renderer {
58 pub fn new(is_opaque: bool, fonts: Arc<dyn platform::FontSystem>) -> Self {
59 const PIXEL_FORMAT: MTLPixelFormat = MTLPixelFormat::BGRA8Unorm;
60
61 let device: metal::Device = if let Some(device) = metal::Device::system_default() {
62 device
63 } else {
64 log::error!("unable to access a compatible graphics device");
65 std::process::exit(1);
66 };
67
68 let layer = metal::MetalLayer::new();
69 layer.set_device(&device);
70 layer.set_pixel_format(PIXEL_FORMAT);
71 layer.set_presents_with_transaction(true);
72 layer.set_opaque(is_opaque);
73 unsafe {
74 let _: () = msg_send![&*layer, setAllowsNextDrawableTimeout: NO];
75 let _: () = msg_send![&*layer, setNeedsDisplayOnBoundsChange: YES];
76 let _: () = msg_send![
77 &*layer,
78 setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE
79 | AutoresizingMask::HEIGHT_SIZABLE
80 ];
81 }
82
83 let library = device
84 .new_library_with_data(SHADERS_METALLIB)
85 .expect("error building metal library");
86
87 let unit_vertices = [
88 (0., 0.).to_float2(),
89 (1., 0.).to_float2(),
90 (0., 1.).to_float2(),
91 (0., 1.).to_float2(),
92 (1., 0.).to_float2(),
93 (1., 1.).to_float2(),
94 ];
95 let unit_vertices = device.new_buffer_with_data(
96 unit_vertices.as_ptr() as *const c_void,
97 (unit_vertices.len() * mem::size_of::<shaders::vector_float2>()) as u64,
98 MTLResourceOptions::StorageModeManaged,
99 );
100 let instances = device.new_buffer(
101 INSTANCE_BUFFER_SIZE as u64,
102 MTLResourceOptions::StorageModeManaged,
103 );
104
105 let sprite_cache = SpriteCache::new(device.clone(), vec2i(1024, 768), 1., fonts.clone());
106 let image_cache = ImageCache::new(device.clone(), vec2i(1024, 768), 1., fonts);
107 let path_atlases =
108 AtlasAllocator::new(device.clone(), build_path_atlas_texture_descriptor());
109 let quad_pipeline_state = build_pipeline_state(
110 &device,
111 &library,
112 "quad",
113 "quad_vertex",
114 "quad_fragment",
115 PIXEL_FORMAT,
116 );
117 let shadow_pipeline_state = build_pipeline_state(
118 &device,
119 &library,
120 "shadow",
121 "shadow_vertex",
122 "shadow_fragment",
123 PIXEL_FORMAT,
124 );
125 let sprite_pipeline_state = build_pipeline_state(
126 &device,
127 &library,
128 "sprite",
129 "sprite_vertex",
130 "sprite_fragment",
131 PIXEL_FORMAT,
132 );
133 let image_pipeline_state = build_pipeline_state(
134 &device,
135 &library,
136 "image",
137 "image_vertex",
138 "image_fragment",
139 PIXEL_FORMAT,
140 );
141 let surface_pipeline_state = build_pipeline_state(
142 &device,
143 &library,
144 "surface",
145 "surface_vertex",
146 "surface_fragment",
147 PIXEL_FORMAT,
148 );
149 let path_atlas_pipeline_state = build_path_atlas_pipeline_state(
150 &device,
151 &library,
152 "path_atlas",
153 "path_atlas_vertex",
154 "path_atlas_fragment",
155 MTLPixelFormat::R16Float,
156 );
157 let underline_pipeline_state = build_pipeline_state(
158 &device,
159 &library,
160 "underline",
161 "underline_vertex",
162 "underline_fragment",
163 PIXEL_FORMAT,
164 );
165 let cv_texture_cache = CVMetalTextureCache::new(device.as_ptr()).unwrap();
166 Self {
167 layer,
168 command_queue: device.new_command_queue(),
169 sprite_cache,
170 image_cache,
171 path_atlases,
172 quad_pipeline_state,
173 shadow_pipeline_state,
174 sprite_pipeline_state,
175 image_pipeline_state,
176 surface_pipeline_state,
177 path_atlas_pipeline_state,
178 underline_pipeline_state,
179 unit_vertices,
180 instances,
181 cv_texture_cache,
182 }
183 }
184
185 pub fn layer(&self) -> &metal::MetalLayerRef {
186 &*self.layer
187 }
188
189 pub fn render(&mut self, scene: &Scene) {
190 let layer = self.layer.clone();
191 let drawable_size = layer.drawable_size();
192 let drawable = if let Some(drawable) = layer.next_drawable() {
193 drawable
194 } else {
195 log::error!(
196 "failed to retrieve next drawable, drawable size: {:?}",
197 drawable_size
198 );
199 return;
200 };
201 let command_queue = self.command_queue.clone();
202 let command_buffer = command_queue.new_command_buffer();
203
204 self.sprite_cache.set_scale_factor(scene.scale_factor());
205 self.image_cache.set_scale_factor(scene.scale_factor());
206
207 let mut offset = 0;
208
209 let path_sprites = self.render_path_atlases(scene, &mut offset, command_buffer);
210 self.render_layers(
211 scene,
212 path_sprites,
213 &mut offset,
214 vec2f(drawable_size.width as f32, drawable_size.height as f32),
215 command_buffer,
216 drawable.texture(),
217 );
218 self.instances.did_modify_range(NSRange {
219 location: 0,
220 length: offset as NSUInteger,
221 });
222 self.image_cache.finish_frame();
223
224 command_buffer.commit();
225 command_buffer.wait_until_completed();
226 drawable.present();
227 }
228
229 fn render_path_atlases(
230 &mut self,
231 scene: &Scene,
232 offset: &mut usize,
233 command_buffer: &metal::CommandBufferRef,
234 ) -> Vec<PathSprite> {
235 self.path_atlases.clear();
236 let mut sprites = Vec::new();
237 let mut vertices = Vec::<shaders::GPUIPathVertex>::new();
238 let mut current_atlas_id = None;
239 for (layer_id, layer) in scene.layers().enumerate() {
240 for path in layer.paths() {
241 let origin = path.bounds.origin() * scene.scale_factor();
242 let size = (path.bounds.size() * scene.scale_factor()).ceil();
243
244 let path_allocation = self.path_atlases.allocate(size.to_i32());
245 if path_allocation.is_none() {
246 // Path size was likely zero.
247 warn!("could not allocate path texture of size {:?}", size);
248 continue;
249 }
250 let (alloc_id, atlas_origin) = path_allocation.unwrap();
251 let atlas_origin = atlas_origin.to_f32();
252 sprites.push(PathSprite {
253 layer_id,
254 atlas_id: alloc_id.atlas_id,
255 shader_data: shaders::GPUISprite {
256 origin: origin.floor().to_float2(),
257 target_size: size.to_float2(),
258 source_size: size.to_float2(),
259 atlas_origin: atlas_origin.to_float2(),
260 color: path.color.to_uchar4(),
261 compute_winding: 1,
262 },
263 });
264
265 if let Some(current_atlas_id) = current_atlas_id {
266 if alloc_id.atlas_id != current_atlas_id {
267 self.render_paths_to_atlas(
268 offset,
269 &vertices,
270 current_atlas_id,
271 command_buffer,
272 );
273 vertices.clear();
274 }
275 }
276
277 current_atlas_id = Some(alloc_id.atlas_id);
278
279 for vertex in &path.vertices {
280 let xy_position =
281 (vertex.xy_position - path.bounds.origin()) * scene.scale_factor();
282 vertices.push(shaders::GPUIPathVertex {
283 xy_position: (atlas_origin + xy_position).to_float2(),
284 st_position: vertex.st_position.to_float2(),
285 clip_rect_origin: atlas_origin.to_float2(),
286 clip_rect_size: size.to_float2(),
287 });
288 }
289 }
290 }
291
292 if let Some(atlas_id) = current_atlas_id {
293 self.render_paths_to_atlas(offset, &vertices, atlas_id, command_buffer);
294 }
295
296 sprites
297 }
298
299 fn render_paths_to_atlas(
300 &mut self,
301 offset: &mut usize,
302 vertices: &[shaders::GPUIPathVertex],
303 atlas_id: usize,
304 command_buffer: &metal::CommandBufferRef,
305 ) {
306 align_offset(offset);
307 let next_offset = *offset + vertices.len() * mem::size_of::<shaders::GPUIPathVertex>();
308 assert!(
309 next_offset <= INSTANCE_BUFFER_SIZE,
310 "instance buffer exhausted"
311 );
312
313 let render_pass_descriptor = metal::RenderPassDescriptor::new();
314 let color_attachment = render_pass_descriptor
315 .color_attachments()
316 .object_at(0)
317 .unwrap();
318 let texture = self.path_atlases.texture(atlas_id).unwrap();
319 color_attachment.set_texture(Some(texture));
320 color_attachment.set_load_action(metal::MTLLoadAction::Clear);
321 color_attachment.set_store_action(metal::MTLStoreAction::Store);
322 color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 1.));
323
324 let path_atlas_command_encoder =
325 command_buffer.new_render_command_encoder(render_pass_descriptor);
326 path_atlas_command_encoder.set_render_pipeline_state(&self.path_atlas_pipeline_state);
327 path_atlas_command_encoder.set_vertex_buffer(
328 shaders::GPUIPathAtlasVertexInputIndex_GPUIPathAtlasVertexInputIndexVertices as u64,
329 Some(&self.instances),
330 *offset as u64,
331 );
332 path_atlas_command_encoder.set_vertex_bytes(
333 shaders::GPUIPathAtlasVertexInputIndex_GPUIPathAtlasVertexInputIndexAtlasSize as u64,
334 mem::size_of::<shaders::vector_float2>() as u64,
335 [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
336 as *const c_void,
337 );
338
339 let buffer_contents = unsafe {
340 (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIPathVertex
341 };
342
343 for (ix, vertex) in vertices.iter().enumerate() {
344 unsafe {
345 *buffer_contents.add(ix) = *vertex;
346 }
347 }
348
349 path_atlas_command_encoder.draw_primitives(
350 metal::MTLPrimitiveType::Triangle,
351 0,
352 vertices.len() as u64,
353 );
354 path_atlas_command_encoder.end_encoding();
355 *offset = next_offset;
356 }
357
358 fn render_layers(
359 &mut self,
360 scene: &Scene,
361 path_sprites: Vec<PathSprite>,
362 offset: &mut usize,
363 drawable_size: Vector2F,
364 command_buffer: &metal::CommandBufferRef,
365 output: &metal::TextureRef,
366 ) {
367 let render_pass_descriptor = metal::RenderPassDescriptor::new();
368 let color_attachment = render_pass_descriptor
369 .color_attachments()
370 .object_at(0)
371 .unwrap();
372 color_attachment.set_texture(Some(output));
373 color_attachment.set_load_action(metal::MTLLoadAction::Clear);
374 color_attachment.set_store_action(metal::MTLStoreAction::Store);
375 let alpha = if self.layer.is_opaque() { 1. } else { 0. };
376 color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., alpha));
377 let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
378
379 command_encoder.set_viewport(metal::MTLViewport {
380 originX: 0.0,
381 originY: 0.0,
382 width: drawable_size.x() as f64,
383 height: drawable_size.y() as f64,
384 znear: 0.0,
385 zfar: 1.0,
386 });
387
388 let scale_factor = scene.scale_factor();
389 let mut path_sprites = path_sprites.into_iter().peekable();
390 for (layer_id, layer) in scene.layers().enumerate() {
391 self.clip(scene, layer, drawable_size, command_encoder);
392 self.render_shadows(
393 layer.shadows(),
394 scale_factor,
395 offset,
396 drawable_size,
397 command_encoder,
398 );
399 self.render_quads(
400 layer.quads(),
401 scale_factor,
402 offset,
403 drawable_size,
404 command_encoder,
405 );
406 self.render_path_sprites(
407 layer_id,
408 &mut path_sprites,
409 offset,
410 drawable_size,
411 command_encoder,
412 );
413 self.render_underlines(
414 layer.underlines(),
415 scale_factor,
416 offset,
417 drawable_size,
418 command_encoder,
419 );
420 self.render_sprites(
421 layer.glyphs(),
422 layer.icons(),
423 scale_factor,
424 offset,
425 drawable_size,
426 command_encoder,
427 );
428 self.render_images(
429 layer.images(),
430 layer.image_glyphs(),
431 scale_factor,
432 offset,
433 drawable_size,
434 command_encoder,
435 );
436 self.render_surfaces(
437 layer.surfaces(),
438 scale_factor,
439 offset,
440 drawable_size,
441 command_encoder,
442 );
443 }
444
445 command_encoder.end_encoding();
446 }
447
448 fn clip(
449 &mut self,
450 scene: &Scene,
451 layer: &Layer,
452 drawable_size: Vector2F,
453 command_encoder: &metal::RenderCommandEncoderRef,
454 ) {
455 let clip_bounds = (layer
456 .clip_bounds()
457 .unwrap_or_else(|| RectF::new(vec2f(0., 0.), drawable_size / scene.scale_factor()))
458 * scene.scale_factor())
459 .round();
460 command_encoder.set_scissor_rect(metal::MTLScissorRect {
461 x: clip_bounds.origin_x() as NSUInteger,
462 y: clip_bounds.origin_y() as NSUInteger,
463 width: clip_bounds.width() as NSUInteger,
464 height: clip_bounds.height() as NSUInteger,
465 });
466 }
467
468 fn render_shadows(
469 &mut self,
470 shadows: &[Shadow],
471 scale_factor: f32,
472 offset: &mut usize,
473 drawable_size: Vector2F,
474 command_encoder: &metal::RenderCommandEncoderRef,
475 ) {
476 if shadows.is_empty() {
477 return;
478 }
479
480 align_offset(offset);
481 let next_offset = *offset + shadows.len() * mem::size_of::<shaders::GPUIShadow>();
482 assert!(
483 next_offset <= INSTANCE_BUFFER_SIZE,
484 "instance buffer exhausted"
485 );
486
487 command_encoder.set_render_pipeline_state(&self.shadow_pipeline_state);
488 command_encoder.set_vertex_buffer(
489 shaders::GPUIShadowInputIndex_GPUIShadowInputIndexVertices as u64,
490 Some(&self.unit_vertices),
491 0,
492 );
493 command_encoder.set_vertex_buffer(
494 shaders::GPUIShadowInputIndex_GPUIShadowInputIndexShadows as u64,
495 Some(&self.instances),
496 *offset as u64,
497 );
498 command_encoder.set_vertex_bytes(
499 shaders::GPUIShadowInputIndex_GPUIShadowInputIndexUniforms as u64,
500 mem::size_of::<shaders::GPUIUniforms>() as u64,
501 [shaders::GPUIUniforms {
502 viewport_size: drawable_size.to_float2(),
503 }]
504 .as_ptr() as *const c_void,
505 );
506
507 let buffer_contents = unsafe {
508 (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIShadow
509 };
510 for (ix, shadow) in shadows.iter().enumerate() {
511 let shape_bounds = shadow.bounds * scale_factor;
512 let corner_radii = shadow.corner_radii * scale_factor;
513 let shader_shadow = shaders::GPUIShadow {
514 origin: shape_bounds.origin().to_float2(),
515 size: shape_bounds.size().to_float2(),
516 corner_radius_top_left: corner_radii.top_left,
517 corner_radius_top_right: corner_radii.top_right,
518 corner_radius_bottom_right: corner_radii.bottom_right,
519 corner_radius_bottom_left: corner_radii.bottom_left,
520 sigma: shadow.sigma,
521 color: shadow.color.to_uchar4(),
522 };
523 unsafe {
524 *(buffer_contents.add(ix)) = shader_shadow;
525 }
526 }
527
528 command_encoder.draw_primitives_instanced(
529 metal::MTLPrimitiveType::Triangle,
530 0,
531 6,
532 shadows.len() as u64,
533 );
534 *offset = next_offset;
535 }
536
537 fn render_quads(
538 &mut self,
539 quads: &[Quad],
540 scale_factor: f32,
541 offset: &mut usize,
542 drawable_size: Vector2F,
543 command_encoder: &metal::RenderCommandEncoderRef,
544 ) {
545 if quads.is_empty() {
546 return;
547 }
548 align_offset(offset);
549 let next_offset = *offset + quads.len() * mem::size_of::<shaders::GPUIQuad>();
550 assert!(
551 next_offset <= INSTANCE_BUFFER_SIZE,
552 "instance buffer exhausted"
553 );
554
555 command_encoder.set_render_pipeline_state(&self.quad_pipeline_state);
556 command_encoder.set_vertex_buffer(
557 shaders::GPUIQuadInputIndex_GPUIQuadInputIndexVertices as u64,
558 Some(&self.unit_vertices),
559 0,
560 );
561 command_encoder.set_vertex_buffer(
562 shaders::GPUIQuadInputIndex_GPUIQuadInputIndexQuads as u64,
563 Some(&self.instances),
564 *offset as u64,
565 );
566 command_encoder.set_vertex_bytes(
567 shaders::GPUIQuadInputIndex_GPUIQuadInputIndexUniforms as u64,
568 mem::size_of::<shaders::GPUIUniforms>() as u64,
569 [shaders::GPUIUniforms {
570 viewport_size: drawable_size.to_float2(),
571 }]
572 .as_ptr() as *const c_void,
573 );
574
575 let buffer_contents = unsafe {
576 (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIQuad
577 };
578 for (ix, quad) in quads.iter().enumerate() {
579 let bounds = quad.bounds * scale_factor;
580 let shader_quad = shaders::GPUIQuad {
581 origin: bounds.origin().round().to_float2(),
582 size: bounds.size().round().to_float2(),
583 background_color: quad
584 .background
585 .unwrap_or_else(Color::transparent_black)
586 .to_uchar4(),
587 border_top: quad.border.top * scale_factor,
588 border_right: quad.border.right * scale_factor,
589 border_bottom: quad.border.bottom * scale_factor,
590 border_left: quad.border.left * scale_factor,
591 border_color: quad.border.color.to_uchar4(),
592 corner_radius_top_left: quad.corner_radii.top_left * scale_factor,
593 corner_radius_top_right: quad.corner_radii.top_right * scale_factor,
594 corner_radius_bottom_right: quad.corner_radii.bottom_right * scale_factor,
595 corner_radius_bottom_left: quad.corner_radii.bottom_left * scale_factor,
596 };
597 unsafe {
598 *(buffer_contents.add(ix)) = shader_quad;
599 }
600 }
601
602 command_encoder.draw_primitives_instanced(
603 metal::MTLPrimitiveType::Triangle,
604 0,
605 6,
606 quads.len() as u64,
607 );
608 *offset = next_offset;
609 }
610
611 fn render_sprites(
612 &mut self,
613 glyphs: &[Glyph],
614 icons: &[Icon],
615 scale_factor: f32,
616 offset: &mut usize,
617 drawable_size: Vector2F,
618 command_encoder: &metal::RenderCommandEncoderRef,
619 ) {
620 if glyphs.is_empty() && icons.is_empty() {
621 return;
622 }
623
624 let mut sprites_by_atlas = HashMap::new();
625
626 for glyph in glyphs {
627 if let Some(sprite) = self.sprite_cache.render_glyph(
628 glyph.font_id,
629 glyph.font_size,
630 glyph.id,
631 glyph.origin,
632 ) {
633 // Snap sprite to pixel grid.
634 let origin = (glyph.origin * scale_factor).floor() + sprite.offset.to_f32();
635 sprites_by_atlas
636 .entry(sprite.atlas_id)
637 .or_insert_with(Vec::new)
638 .push(shaders::GPUISprite {
639 origin: origin.to_float2(),
640 target_size: sprite.size.to_float2(),
641 source_size: sprite.size.to_float2(),
642 atlas_origin: sprite.atlas_origin.to_float2(),
643 color: glyph.color.to_uchar4(),
644 compute_winding: 0,
645 });
646 }
647 }
648
649 for icon in icons {
650 // Snap sprite to pixel grid.
651 let origin = (icon.bounds.origin() * scale_factor).floor();
652 let target_size = (icon.bounds.size() * scale_factor).ceil();
653 let source_size = (target_size * 2.).to_i32();
654
655 let sprite =
656 self.sprite_cache
657 .render_icon(source_size, icon.path.clone(), icon.svg.clone());
658 if sprite.is_none() {
659 continue;
660 }
661 let sprite = sprite.unwrap();
662
663 sprites_by_atlas
664 .entry(sprite.atlas_id)
665 .or_insert_with(Vec::new)
666 .push(shaders::GPUISprite {
667 origin: origin.to_float2(),
668 target_size: target_size.to_float2(),
669 source_size: sprite.size.to_float2(),
670 atlas_origin: sprite.atlas_origin.to_float2(),
671 color: icon.color.to_uchar4(),
672 compute_winding: 0,
673 });
674 }
675
676 command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
677 command_encoder.set_vertex_buffer(
678 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
679 Some(&self.unit_vertices),
680 0,
681 );
682 command_encoder.set_vertex_bytes(
683 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64,
684 mem::size_of::<shaders::vector_float2>() as u64,
685 [drawable_size.to_float2()].as_ptr() as *const c_void,
686 );
687
688 for (atlas_id, sprites) in sprites_by_atlas {
689 align_offset(offset);
690 let next_offset = *offset + sprites.len() * mem::size_of::<shaders::GPUISprite>();
691 assert!(
692 next_offset <= INSTANCE_BUFFER_SIZE,
693 "instance buffer exhausted"
694 );
695
696 let texture = self.sprite_cache.atlas_texture(atlas_id).unwrap();
697 command_encoder.set_vertex_buffer(
698 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
699 Some(&self.instances),
700 *offset as u64,
701 );
702 command_encoder.set_vertex_bytes(
703 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
704 mem::size_of::<shaders::vector_float2>() as u64,
705 [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
706 as *const c_void,
707 );
708
709 command_encoder.set_fragment_texture(
710 shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
711 Some(texture),
712 );
713
714 unsafe {
715 let buffer_contents =
716 (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUISprite;
717 std::ptr::copy_nonoverlapping(sprites.as_ptr(), buffer_contents, sprites.len());
718 }
719
720 command_encoder.draw_primitives_instanced(
721 metal::MTLPrimitiveType::Triangle,
722 0,
723 6,
724 sprites.len() as u64,
725 );
726 *offset = next_offset;
727 }
728 }
729
730 fn render_images(
731 &mut self,
732 images: &[Image],
733 image_glyphs: &[ImageGlyph],
734 scale_factor: f32,
735 offset: &mut usize,
736 drawable_size: Vector2F,
737 command_encoder: &metal::RenderCommandEncoderRef,
738 ) {
739 if images.is_empty() && image_glyphs.is_empty() {
740 return;
741 }
742
743 let mut images_by_atlas = HashMap::new();
744 for image in images {
745 let origin = image.bounds.origin() * scale_factor;
746 let target_size = image.bounds.size() * scale_factor;
747 let corner_radii = image.corner_radii * scale_factor;
748 let (alloc_id, atlas_bounds) = self.image_cache.render(&image.data);
749 images_by_atlas
750 .entry(alloc_id.atlas_id)
751 .or_insert_with(Vec::new)
752 .push(shaders::GPUIImage {
753 origin: origin.to_float2(),
754 target_size: target_size.to_float2(),
755 source_size: atlas_bounds.size().to_float2(),
756 atlas_origin: atlas_bounds.origin().to_float2(),
757 border_top: image.border.top * scale_factor,
758 border_right: image.border.right * scale_factor,
759 border_bottom: image.border.bottom * scale_factor,
760 border_left: image.border.left * scale_factor,
761 border_color: image.border.color.to_uchar4(),
762 corner_radius_top_left: corner_radii.top_left,
763 corner_radius_top_right: corner_radii.top_right,
764 corner_radius_bottom_right: corner_radii.bottom_right,
765 corner_radius_bottom_left: corner_radii.bottom_left,
766 grayscale: image.grayscale as u8,
767 });
768 }
769
770 for image_glyph in image_glyphs {
771 let origin = (image_glyph.origin * scale_factor).floor();
772 if let Some((alloc_id, atlas_bounds, glyph_origin)) =
773 self.image_cache.render_glyph(image_glyph)
774 {
775 images_by_atlas
776 .entry(alloc_id.atlas_id)
777 .or_insert_with(Vec::new)
778 .push(shaders::GPUIImage {
779 origin: (origin + glyph_origin.to_f32()).to_float2(),
780 target_size: atlas_bounds.size().to_float2(),
781 source_size: atlas_bounds.size().to_float2(),
782 atlas_origin: atlas_bounds.origin().to_float2(),
783 border_top: 0.,
784 border_right: 0.,
785 border_bottom: 0.,
786 border_left: 0.,
787 border_color: Default::default(),
788 corner_radius_top_left: 0.,
789 corner_radius_top_right: 0.,
790 corner_radius_bottom_right: 0.,
791 corner_radius_bottom_left: 0.,
792 grayscale: false as u8,
793 });
794 } else {
795 log::warn!("could not render glyph with id {}", image_glyph.id);
796 }
797 }
798
799 command_encoder.set_render_pipeline_state(&self.image_pipeline_state);
800 command_encoder.set_vertex_buffer(
801 shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexVertices as u64,
802 Some(&self.unit_vertices),
803 0,
804 );
805 command_encoder.set_vertex_bytes(
806 shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexViewportSize as u64,
807 mem::size_of::<shaders::vector_float2>() as u64,
808 [drawable_size.to_float2()].as_ptr() as *const c_void,
809 );
810
811 for (atlas_id, images) in images_by_atlas {
812 align_offset(offset);
813 let next_offset = *offset + images.len() * mem::size_of::<shaders::GPUIImage>();
814 assert!(
815 next_offset <= INSTANCE_BUFFER_SIZE,
816 "instance buffer exhausted"
817 );
818
819 let texture = self.image_cache.atlas_texture(atlas_id).unwrap();
820 command_encoder.set_vertex_buffer(
821 shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexImages as u64,
822 Some(&self.instances),
823 *offset as u64,
824 );
825 command_encoder.set_vertex_bytes(
826 shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexAtlasSize as u64,
827 mem::size_of::<shaders::vector_float2>() as u64,
828 [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
829 as *const c_void,
830 );
831 command_encoder.set_fragment_texture(
832 shaders::GPUIImageFragmentInputIndex_GPUIImageFragmentInputIndexAtlas as u64,
833 Some(texture),
834 );
835
836 unsafe {
837 let buffer_contents =
838 (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIImage;
839 std::ptr::copy_nonoverlapping(images.as_ptr(), buffer_contents, images.len());
840 }
841
842 command_encoder.draw_primitives_instanced(
843 metal::MTLPrimitiveType::Triangle,
844 0,
845 6,
846 images.len() as u64,
847 );
848 *offset = next_offset;
849 }
850 }
851
852 fn render_surfaces(
853 &mut self,
854 surfaces: &[Surface],
855 scale_factor: f32,
856 offset: &mut usize,
857 drawable_size: Vector2F,
858 command_encoder: &metal::RenderCommandEncoderRef,
859 ) {
860 if surfaces.is_empty() {
861 return;
862 }
863
864 command_encoder.set_render_pipeline_state(&self.surface_pipeline_state);
865 command_encoder.set_vertex_buffer(
866 shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexVertices as u64,
867 Some(&self.unit_vertices),
868 0,
869 );
870 command_encoder.set_vertex_bytes(
871 shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexViewportSize as u64,
872 mem::size_of::<shaders::vector_float2>() as u64,
873 [drawable_size.to_float2()].as_ptr() as *const c_void,
874 );
875
876 for surface in surfaces {
877 let origin = surface.bounds.origin() * scale_factor;
878 let source_size = vec2i(
879 surface.image_buffer.width() as i32,
880 surface.image_buffer.height() as i32,
881 );
882 let target_size = surface.bounds.size() * scale_factor;
883
884 assert_eq!(
885 surface.image_buffer.pixel_format_type(),
886 core_video::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
887 );
888
889 let y_texture = self
890 .cv_texture_cache
891 .create_texture_from_image(
892 surface.image_buffer.as_concrete_TypeRef(),
893 ptr::null(),
894 MTLPixelFormat::R8Unorm,
895 surface.image_buffer.plane_width(0),
896 surface.image_buffer.plane_height(0),
897 0,
898 )
899 .unwrap();
900 let cb_cr_texture = self
901 .cv_texture_cache
902 .create_texture_from_image(
903 surface.image_buffer.as_concrete_TypeRef(),
904 ptr::null(),
905 MTLPixelFormat::RG8Unorm,
906 surface.image_buffer.plane_width(1),
907 surface.image_buffer.plane_height(1),
908 1,
909 )
910 .unwrap();
911
912 align_offset(offset);
913 let next_offset = *offset + mem::size_of::<shaders::GPUISurface>();
914 assert!(
915 next_offset <= INSTANCE_BUFFER_SIZE,
916 "instance buffer exhausted"
917 );
918
919 command_encoder.set_vertex_buffer(
920 shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexSurfaces as u64,
921 Some(&self.instances),
922 *offset as u64,
923 );
924 command_encoder.set_vertex_bytes(
925 shaders::GPUISurfaceVertexInputIndex_GPUISurfaceVertexInputIndexAtlasSize as u64,
926 mem::size_of::<shaders::vector_float2>() as u64,
927 [source_size.to_float2()].as_ptr() as *const c_void,
928 );
929 command_encoder.set_fragment_texture(
930 shaders::GPUISurfaceFragmentInputIndex_GPUISurfaceFragmentInputIndexYAtlas as u64,
931 Some(y_texture.as_texture_ref()),
932 );
933 command_encoder.set_fragment_texture(
934 shaders::GPUISurfaceFragmentInputIndex_GPUISurfaceFragmentInputIndexCbCrAtlas
935 as u64,
936 Some(cb_cr_texture.as_texture_ref()),
937 );
938
939 unsafe {
940 let buffer_contents = (self.instances.contents() as *mut u8).add(*offset)
941 as *mut shaders::GPUISurface;
942 std::ptr::write(
943 buffer_contents,
944 shaders::GPUISurface {
945 origin: origin.to_float2(),
946 target_size: target_size.to_float2(),
947 source_size: source_size.to_float2(),
948 },
949 );
950 }
951
952 command_encoder.draw_primitives(metal::MTLPrimitiveType::Triangle, 0, 6);
953 *offset = next_offset;
954 }
955 }
956
957 fn render_path_sprites(
958 &mut self,
959 layer_id: usize,
960 sprites: &mut Peekable<vec::IntoIter<PathSprite>>,
961 offset: &mut usize,
962 drawable_size: Vector2F,
963 command_encoder: &metal::RenderCommandEncoderRef,
964 ) {
965 command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
966 command_encoder.set_vertex_buffer(
967 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
968 Some(&self.unit_vertices),
969 0,
970 );
971 command_encoder.set_vertex_bytes(
972 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64,
973 mem::size_of::<shaders::vector_float2>() as u64,
974 [drawable_size.to_float2()].as_ptr() as *const c_void,
975 );
976
977 let mut atlas_id = None;
978 let mut atlas_sprite_count = 0;
979 align_offset(offset);
980
981 while let Some(sprite) = sprites.peek() {
982 if sprite.layer_id != layer_id {
983 break;
984 }
985
986 let sprite = sprites.next().unwrap();
987 if let Some(atlas_id) = atlas_id.as_mut() {
988 if sprite.atlas_id != *atlas_id {
989 self.render_path_sprites_for_atlas(
990 offset,
991 *atlas_id,
992 atlas_sprite_count,
993 command_encoder,
994 );
995
996 *atlas_id = sprite.atlas_id;
997 atlas_sprite_count = 0;
998 align_offset(offset);
999 }
1000 } else {
1001 atlas_id = Some(sprite.atlas_id);
1002 }
1003
1004 unsafe {
1005 let buffer_contents =
1006 (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUISprite;
1007 *buffer_contents.add(atlas_sprite_count) = sprite.shader_data;
1008 }
1009
1010 atlas_sprite_count += 1;
1011 }
1012
1013 if let Some(atlas_id) = atlas_id {
1014 self.render_path_sprites_for_atlas(
1015 offset,
1016 atlas_id,
1017 atlas_sprite_count,
1018 command_encoder,
1019 );
1020 }
1021 }
1022
1023 fn render_path_sprites_for_atlas(
1024 &mut self,
1025 offset: &mut usize,
1026 atlas_id: usize,
1027 sprite_count: usize,
1028 command_encoder: &metal::RenderCommandEncoderRef,
1029 ) {
1030 let next_offset = *offset + sprite_count * mem::size_of::<shaders::GPUISprite>();
1031 assert!(
1032 next_offset <= INSTANCE_BUFFER_SIZE,
1033 "instance buffer exhausted"
1034 );
1035 command_encoder.set_vertex_buffer(
1036 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
1037 Some(&self.instances),
1038 *offset as u64,
1039 );
1040 let texture = self.path_atlases.texture(atlas_id).unwrap();
1041 command_encoder.set_fragment_texture(
1042 shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
1043 Some(texture),
1044 );
1045 command_encoder.set_vertex_bytes(
1046 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
1047 mem::size_of::<shaders::vector_float2>() as u64,
1048 [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
1049 as *const c_void,
1050 );
1051
1052 command_encoder.draw_primitives_instanced(
1053 metal::MTLPrimitiveType::Triangle,
1054 0,
1055 6,
1056 sprite_count as u64,
1057 );
1058 *offset = next_offset;
1059 }
1060
1061 fn render_underlines(
1062 &mut self,
1063 underlines: &[Underline],
1064 scale_factor: f32,
1065 offset: &mut usize,
1066 drawable_size: Vector2F,
1067 command_encoder: &metal::RenderCommandEncoderRef,
1068 ) {
1069 if underlines.is_empty() {
1070 return;
1071 }
1072 align_offset(offset);
1073 let next_offset = *offset + underlines.len() * mem::size_of::<shaders::GPUIUnderline>();
1074 assert!(
1075 next_offset <= INSTANCE_BUFFER_SIZE,
1076 "instance buffer exhausted"
1077 );
1078
1079 command_encoder.set_render_pipeline_state(&self.underline_pipeline_state);
1080 command_encoder.set_vertex_buffer(
1081 shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexVertices as u64,
1082 Some(&self.unit_vertices),
1083 0,
1084 );
1085 command_encoder.set_vertex_buffer(
1086 shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexUnderlines as u64,
1087 Some(&self.instances),
1088 *offset as u64,
1089 );
1090 command_encoder.set_vertex_bytes(
1091 shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexUniforms as u64,
1092 mem::size_of::<shaders::GPUIUniforms>() as u64,
1093 [shaders::GPUIUniforms {
1094 viewport_size: drawable_size.to_float2(),
1095 }]
1096 .as_ptr() as *const c_void,
1097 );
1098
1099 let buffer_contents = unsafe {
1100 (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIUnderline
1101 };
1102 for (ix, underline) in underlines.iter().enumerate() {
1103 let origin = underline.origin * scale_factor;
1104 let mut height = underline.thickness;
1105 if underline.squiggly {
1106 height *= 3.;
1107 }
1108 let size = vec2f(underline.width, height) * scale_factor;
1109 let shader_underline = shaders::GPUIUnderline {
1110 origin: origin.round().to_float2(),
1111 size: size.round().to_float2(),
1112 thickness: underline.thickness * scale_factor,
1113 color: underline.color.to_uchar4(),
1114 squiggly: underline.squiggly as u8,
1115 };
1116 unsafe {
1117 *(buffer_contents.add(ix)) = shader_underline;
1118 }
1119 }
1120
1121 command_encoder.draw_primitives_instanced(
1122 metal::MTLPrimitiveType::Triangle,
1123 0,
1124 6,
1125 underlines.len() as u64,
1126 );
1127 *offset = next_offset;
1128 }
1129}
1130
1131fn build_path_atlas_texture_descriptor() -> metal::TextureDescriptor {
1132 let texture_descriptor = metal::TextureDescriptor::new();
1133 texture_descriptor.set_width(2048);
1134 texture_descriptor.set_height(2048);
1135 texture_descriptor.set_pixel_format(MTLPixelFormat::R16Float);
1136 texture_descriptor
1137 .set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead);
1138 texture_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
1139 texture_descriptor
1140}
1141
1142fn align_offset(offset: &mut usize) {
1143 let r = *offset % 256;
1144 if r > 0 {
1145 *offset += 256 - r; // Align to a multiple of 256 to make Metal happy
1146 }
1147}
1148
1149fn build_pipeline_state(
1150 device: &metal::DeviceRef,
1151 library: &metal::LibraryRef,
1152 label: &str,
1153 vertex_fn_name: &str,
1154 fragment_fn_name: &str,
1155 pixel_format: metal::MTLPixelFormat,
1156) -> metal::RenderPipelineState {
1157 let vertex_fn = library
1158 .get_function(vertex_fn_name, None)
1159 .expect("error locating vertex function");
1160 let fragment_fn = library
1161 .get_function(fragment_fn_name, None)
1162 .expect("error locating fragment function");
1163
1164 let descriptor = metal::RenderPipelineDescriptor::new();
1165 descriptor.set_label(label);
1166 descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
1167 descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
1168 let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
1169 color_attachment.set_pixel_format(pixel_format);
1170 color_attachment.set_blending_enabled(true);
1171 color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
1172 color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
1173 color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::SourceAlpha);
1174 color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
1175 color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
1176 color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
1177
1178 device
1179 .new_render_pipeline_state(&descriptor)
1180 .expect("could not create render pipeline state")
1181}
1182
1183fn build_path_atlas_pipeline_state(
1184 device: &metal::DeviceRef,
1185 library: &metal::LibraryRef,
1186 label: &str,
1187 vertex_fn_name: &str,
1188 fragment_fn_name: &str,
1189 pixel_format: metal::MTLPixelFormat,
1190) -> metal::RenderPipelineState {
1191 let vertex_fn = library
1192 .get_function(vertex_fn_name, None)
1193 .expect("error locating vertex function");
1194 let fragment_fn = library
1195 .get_function(fragment_fn_name, None)
1196 .expect("error locating fragment function");
1197
1198 let descriptor = metal::RenderPipelineDescriptor::new();
1199 descriptor.set_label(label);
1200 descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
1201 descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
1202 let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
1203 color_attachment.set_pixel_format(pixel_format);
1204 color_attachment.set_blending_enabled(true);
1205 color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
1206 color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
1207 color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
1208 color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
1209 color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::One);
1210 color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
1211
1212 device
1213 .new_render_pipeline_state(&descriptor)
1214 .expect("could not create render pipeline state")
1215}
1216
1217mod shaders {
1218 #![allow(non_upper_case_globals)]
1219 #![allow(non_camel_case_types)]
1220 #![allow(non_snake_case)]
1221
1222 use crate::{
1223 color::Color,
1224 geometry::vector::{Vector2F, Vector2I},
1225 };
1226 use std::mem;
1227
1228 include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
1229
1230 pub trait ToFloat2 {
1231 fn to_float2(&self) -> vector_float2;
1232 }
1233
1234 impl ToFloat2 for (f32, f32) {
1235 fn to_float2(&self) -> vector_float2 {
1236 unsafe {
1237 let mut output = mem::transmute::<_, u32>(self.1.to_bits()) as vector_float2;
1238 output <<= 32;
1239 output |= mem::transmute::<_, u32>(self.0.to_bits()) as vector_float2;
1240 output
1241 }
1242 }
1243 }
1244
1245 impl ToFloat2 for Vector2F {
1246 fn to_float2(&self) -> vector_float2 {
1247 unsafe {
1248 let mut output = mem::transmute::<_, u32>(self.y().to_bits()) as vector_float2;
1249 output <<= 32;
1250 output |= mem::transmute::<_, u32>(self.x().to_bits()) as vector_float2;
1251 output
1252 }
1253 }
1254 }
1255
1256 impl ToFloat2 for Vector2I {
1257 fn to_float2(&self) -> vector_float2 {
1258 self.to_f32().to_float2()
1259 }
1260 }
1261
1262 impl Color {
1263 pub fn to_uchar4(&self) -> vector_uchar4 {
1264 let mut vec = self.a as vector_uchar4;
1265 vec <<= 8;
1266 vec |= self.b as vector_uchar4;
1267 vec <<= 8;
1268 vec |= self.g as vector_uchar4;
1269 vec <<= 8;
1270 vec |= self.r as vector_uchar4;
1271 vec
1272 }
1273 }
1274}