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 let origin = icon.bounds.origin() * scale_factor;
565 let target_size = icon.bounds.size() * scale_factor;
566 let source_size = (target_size * 2.).ceil().to_i32();
567
568 let sprite =
569 self.sprite_cache
570 .render_icon(source_size, icon.path.clone(), icon.svg.clone());
571
572 sprites_by_atlas
573 .entry(sprite.atlas_id)
574 .or_insert_with(Vec::new)
575 .push(shaders::GPUISprite {
576 origin: origin.to_float2(),
577 target_size: target_size.to_float2(),
578 source_size: sprite.size.to_float2(),
579 atlas_origin: sprite.atlas_origin.to_float2(),
580 color: icon.color.to_uchar4(),
581 compute_winding: 0,
582 });
583 }
584
585 command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
586 command_encoder.set_vertex_buffer(
587 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
588 Some(&self.unit_vertices),
589 0,
590 );
591 command_encoder.set_vertex_bytes(
592 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64,
593 mem::size_of::<shaders::vector_float2>() as u64,
594 [drawable_size.to_float2()].as_ptr() as *const c_void,
595 );
596
597 for (atlas_id, sprites) in sprites_by_atlas {
598 align_offset(offset);
599 let next_offset = *offset + sprites.len() * mem::size_of::<shaders::GPUISprite>();
600 assert!(
601 next_offset <= INSTANCE_BUFFER_SIZE,
602 "instance buffer exhausted"
603 );
604
605 let texture = self.sprite_cache.atlas_texture(atlas_id).unwrap();
606 command_encoder.set_vertex_buffer(
607 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
608 Some(&self.instances),
609 *offset as u64,
610 );
611 command_encoder.set_vertex_bytes(
612 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
613 mem::size_of::<shaders::vector_float2>() as u64,
614 [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
615 as *const c_void,
616 );
617
618 command_encoder.set_fragment_texture(
619 shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
620 Some(texture),
621 );
622
623 unsafe {
624 let buffer_contents = (self.instances.contents() as *mut u8)
625 .offset(*offset as isize)
626 as *mut shaders::GPUISprite;
627 std::ptr::copy_nonoverlapping(sprites.as_ptr(), buffer_contents, sprites.len());
628 }
629
630 command_encoder.draw_primitives_instanced(
631 metal::MTLPrimitiveType::Triangle,
632 0,
633 6,
634 sprites.len() as u64,
635 );
636 *offset = next_offset;
637 }
638 }
639
640 fn render_images(
641 &mut self,
642 images: &[Image],
643 scale_factor: f32,
644 offset: &mut usize,
645 drawable_size: Vector2F,
646 command_encoder: &metal::RenderCommandEncoderRef,
647 ) {
648 if images.is_empty() {
649 return;
650 }
651
652 let mut images_by_atlas = HashMap::new();
653 for image in images {
654 let origin = image.bounds.origin() * scale_factor;
655 let target_size = image.bounds.size() * scale_factor;
656 let corner_radius = image.corner_radius * scale_factor;
657 let border_width = image.border.width * scale_factor;
658 let (alloc_id, atlas_bounds) = self.image_cache.render(&image.data);
659 images_by_atlas
660 .entry(alloc_id.atlas_id)
661 .or_insert_with(Vec::new)
662 .push(shaders::GPUIImage {
663 origin: origin.to_float2(),
664 target_size: target_size.to_float2(),
665 source_size: atlas_bounds.size().to_float2(),
666 atlas_origin: atlas_bounds.origin().to_float2(),
667 border_top: border_width * (image.border.top as usize as f32),
668 border_right: border_width * (image.border.right as usize as f32),
669 border_bottom: border_width * (image.border.bottom as usize as f32),
670 border_left: border_width * (image.border.left as usize as f32),
671 border_color: image.border.color.to_uchar4(),
672 corner_radius,
673 });
674 }
675
676 command_encoder.set_render_pipeline_state(&self.image_pipeline_state);
677 command_encoder.set_vertex_buffer(
678 shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexVertices as u64,
679 Some(&self.unit_vertices),
680 0,
681 );
682 command_encoder.set_vertex_bytes(
683 shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexViewportSize 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, images) in images_by_atlas {
689 align_offset(offset);
690 let next_offset = *offset + images.len() * mem::size_of::<shaders::GPUIImage>();
691 assert!(
692 next_offset <= INSTANCE_BUFFER_SIZE,
693 "instance buffer exhausted"
694 );
695
696 let texture = self.image_cache.atlas_texture(atlas_id).unwrap();
697 command_encoder.set_vertex_buffer(
698 shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexImages as u64,
699 Some(&self.instances),
700 *offset as u64,
701 );
702 command_encoder.set_vertex_bytes(
703 shaders::GPUIImageVertexInputIndex_GPUIImageVertexInputIndexAtlasSize 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 command_encoder.set_fragment_texture(
709 shaders::GPUIImageFragmentInputIndex_GPUIImageFragmentInputIndexAtlas as u64,
710 Some(texture),
711 );
712
713 unsafe {
714 let buffer_contents = (self.instances.contents() as *mut u8)
715 .offset(*offset as isize)
716 as *mut shaders::GPUIImage;
717 std::ptr::copy_nonoverlapping(images.as_ptr(), buffer_contents, images.len());
718 }
719
720 command_encoder.draw_primitives_instanced(
721 metal::MTLPrimitiveType::Triangle,
722 0,
723 6,
724 images.len() as u64,
725 );
726 *offset = next_offset;
727 }
728 }
729
730 fn render_path_sprites(
731 &mut self,
732 layer_id: usize,
733 sprites: &mut Peekable<vec::IntoIter<PathSprite>>,
734 offset: &mut usize,
735 drawable_size: Vector2F,
736 command_encoder: &metal::RenderCommandEncoderRef,
737 ) {
738 command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
739 command_encoder.set_vertex_buffer(
740 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
741 Some(&self.unit_vertices),
742 0,
743 );
744 command_encoder.set_vertex_bytes(
745 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64,
746 mem::size_of::<shaders::vector_float2>() as u64,
747 [drawable_size.to_float2()].as_ptr() as *const c_void,
748 );
749
750 let mut atlas_id = None;
751 let mut atlas_sprite_count = 0;
752 align_offset(offset);
753
754 while let Some(sprite) = sprites.peek() {
755 if sprite.layer_id != layer_id {
756 break;
757 }
758
759 let sprite = sprites.next().unwrap();
760 if let Some(atlas_id) = atlas_id.as_mut() {
761 if sprite.atlas_id != *atlas_id {
762 self.render_path_sprites_for_atlas(
763 offset,
764 *atlas_id,
765 atlas_sprite_count,
766 command_encoder,
767 );
768
769 *atlas_id = sprite.atlas_id;
770 atlas_sprite_count = 0;
771 align_offset(offset);
772 }
773 } else {
774 atlas_id = Some(sprite.atlas_id);
775 }
776
777 unsafe {
778 let buffer_contents = (self.instances.contents() as *mut u8)
779 .offset(*offset as isize)
780 as *mut shaders::GPUISprite;
781 *buffer_contents.offset(atlas_sprite_count as isize) = sprite.shader_data;
782 }
783
784 atlas_sprite_count += 1;
785 }
786
787 if let Some(atlas_id) = atlas_id {
788 self.render_path_sprites_for_atlas(
789 offset,
790 atlas_id,
791 atlas_sprite_count,
792 command_encoder,
793 );
794 }
795 }
796
797 fn render_path_sprites_for_atlas(
798 &mut self,
799 offset: &mut usize,
800 atlas_id: usize,
801 sprite_count: usize,
802 command_encoder: &metal::RenderCommandEncoderRef,
803 ) {
804 let next_offset = *offset + sprite_count * mem::size_of::<shaders::GPUISprite>();
805 assert!(
806 next_offset <= INSTANCE_BUFFER_SIZE,
807 "instance buffer exhausted"
808 );
809 command_encoder.set_vertex_buffer(
810 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
811 Some(&self.instances),
812 *offset as u64,
813 );
814 let texture = self.path_atlases.texture(atlas_id).unwrap();
815 command_encoder.set_fragment_texture(
816 shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
817 Some(texture),
818 );
819 command_encoder.set_vertex_bytes(
820 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
821 mem::size_of::<shaders::vector_float2>() as u64,
822 [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
823 as *const c_void,
824 );
825
826 command_encoder.draw_primitives_instanced(
827 metal::MTLPrimitiveType::Triangle,
828 0,
829 6,
830 sprite_count as u64,
831 );
832 *offset = next_offset;
833 }
834
835 fn render_underlines(
836 &mut self,
837 underlines: &[Underline],
838 scale_factor: f32,
839 offset: &mut usize,
840 drawable_size: Vector2F,
841 command_encoder: &metal::RenderCommandEncoderRef,
842 ) {
843 if underlines.is_empty() {
844 return;
845 }
846 align_offset(offset);
847 let next_offset = *offset + underlines.len() * mem::size_of::<shaders::GPUIUnderline>();
848 assert!(
849 next_offset <= INSTANCE_BUFFER_SIZE,
850 "instance buffer exhausted"
851 );
852
853 command_encoder.set_render_pipeline_state(&self.underline_pipeline_state);
854 command_encoder.set_vertex_buffer(
855 shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexVertices as u64,
856 Some(&self.unit_vertices),
857 0,
858 );
859 command_encoder.set_vertex_buffer(
860 shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexUnderlines as u64,
861 Some(&self.instances),
862 *offset as u64,
863 );
864 command_encoder.set_vertex_bytes(
865 shaders::GPUIUnderlineInputIndex_GPUIUnderlineInputIndexUniforms as u64,
866 mem::size_of::<shaders::GPUIUniforms>() as u64,
867 [shaders::GPUIUniforms {
868 viewport_size: drawable_size.to_float2(),
869 }]
870 .as_ptr() as *const c_void,
871 );
872
873 let buffer_contents = unsafe {
874 (self.instances.contents() as *mut u8).offset(*offset as isize)
875 as *mut shaders::GPUIUnderline
876 };
877 for (ix, underline) in underlines.iter().enumerate() {
878 let origin = underline.origin * scale_factor;
879 let mut height = underline.thickness;
880 if underline.squiggly {
881 height *= 3.;
882 }
883 let size = vec2f(underline.width, height) * scale_factor;
884 let shader_underline = shaders::GPUIUnderline {
885 origin: origin.round().to_float2(),
886 size: size.round().to_float2(),
887 thickness: underline.thickness * scale_factor,
888 color: underline.color.to_uchar4(),
889 squiggly: underline.squiggly as u8,
890 };
891 unsafe {
892 *(buffer_contents.offset(ix as isize)) = shader_underline;
893 }
894 }
895
896 command_encoder.draw_primitives_instanced(
897 metal::MTLPrimitiveType::Triangle,
898 0,
899 6,
900 underlines.len() as u64,
901 );
902 *offset = next_offset;
903 }
904}
905
906fn build_path_atlas_texture_descriptor() -> metal::TextureDescriptor {
907 let texture_descriptor = metal::TextureDescriptor::new();
908 texture_descriptor.set_width(2048);
909 texture_descriptor.set_height(2048);
910 texture_descriptor.set_pixel_format(MTLPixelFormat::R16Float);
911 texture_descriptor
912 .set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead);
913 texture_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
914 texture_descriptor
915}
916
917fn align_offset(offset: &mut usize) {
918 let r = *offset % 256;
919 if r > 0 {
920 *offset += 256 - r; // Align to a multiple of 256 to make Metal happy
921 }
922}
923
924fn build_pipeline_state(
925 device: &metal::DeviceRef,
926 library: &metal::LibraryRef,
927 label: &str,
928 vertex_fn_name: &str,
929 fragment_fn_name: &str,
930 pixel_format: metal::MTLPixelFormat,
931) -> metal::RenderPipelineState {
932 let vertex_fn = library
933 .get_function(vertex_fn_name, None)
934 .expect("error locating vertex function");
935 let fragment_fn = library
936 .get_function(fragment_fn_name, None)
937 .expect("error locating fragment function");
938
939 let descriptor = metal::RenderPipelineDescriptor::new();
940 descriptor.set_label(label);
941 descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
942 descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
943 let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
944 color_attachment.set_pixel_format(pixel_format);
945 color_attachment.set_blending_enabled(true);
946 color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
947 color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
948 color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::SourceAlpha);
949 color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
950 color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
951 color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
952
953 device
954 .new_render_pipeline_state(&descriptor)
955 .expect("could not create render pipeline state")
956}
957
958fn build_path_atlas_pipeline_state(
959 device: &metal::DeviceRef,
960 library: &metal::LibraryRef,
961 label: &str,
962 vertex_fn_name: &str,
963 fragment_fn_name: &str,
964 pixel_format: metal::MTLPixelFormat,
965) -> metal::RenderPipelineState {
966 let vertex_fn = library
967 .get_function(vertex_fn_name, None)
968 .expect("error locating vertex function");
969 let fragment_fn = library
970 .get_function(fragment_fn_name, None)
971 .expect("error locating fragment function");
972
973 let descriptor = metal::RenderPipelineDescriptor::new();
974 descriptor.set_label(label);
975 descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
976 descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
977 let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
978 color_attachment.set_pixel_format(pixel_format);
979 color_attachment.set_blending_enabled(true);
980 color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
981 color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
982 color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
983 color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
984 color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::One);
985 color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
986
987 device
988 .new_render_pipeline_state(&descriptor)
989 .expect("could not create render pipeline state")
990}
991
992mod shaders {
993 #![allow(non_upper_case_globals)]
994 #![allow(non_camel_case_types)]
995 #![allow(non_snake_case)]
996
997 use crate::{
998 color::Color,
999 geometry::vector::{Vector2F, Vector2I},
1000 };
1001 use std::mem;
1002
1003 include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
1004
1005 pub trait ToFloat2 {
1006 fn to_float2(&self) -> vector_float2;
1007 }
1008
1009 impl ToFloat2 for (f32, f32) {
1010 fn to_float2(&self) -> vector_float2 {
1011 unsafe {
1012 let mut output = mem::transmute::<_, u32>(self.1.to_bits()) as vector_float2;
1013 output <<= 32;
1014 output |= mem::transmute::<_, u32>(self.0.to_bits()) as vector_float2;
1015 output
1016 }
1017 }
1018 }
1019
1020 impl ToFloat2 for Vector2F {
1021 fn to_float2(&self) -> vector_float2 {
1022 unsafe {
1023 let mut output = mem::transmute::<_, u32>(self.y().to_bits()) as vector_float2;
1024 output <<= 32;
1025 output |= mem::transmute::<_, u32>(self.x().to_bits()) as vector_float2;
1026 output
1027 }
1028 }
1029 }
1030
1031 impl ToFloat2 for Vector2I {
1032 fn to_float2(&self) -> vector_float2 {
1033 self.to_f32().to_float2()
1034 }
1035 }
1036
1037 impl Color {
1038 pub fn to_uchar4(&self) -> vector_uchar4 {
1039 let mut vec = self.a as vector_uchar4;
1040 vec <<= 8;
1041 vec |= self.b as vector_uchar4;
1042 vec <<= 8;
1043 vec |= self.g as vector_uchar4;
1044 vec <<= 8;
1045 vec |= self.r as vector_uchar4;
1046 vec
1047 }
1048 }
1049}