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