1use super::{atlas::AtlasAllocator, sprite_cache::SpriteCache};
2use crate::{
3 color::ColorU,
4 geometry::{
5 rect::RectF,
6 vector::{vec2f, vec2i, Vector2F, Vector2I},
7 },
8 platform,
9 scene::Layer,
10 Scene,
11};
12use anyhow::{anyhow, Result};
13use cocoa::foundation::NSUInteger;
14use metal::{MTLPixelFormat, MTLResourceOptions, NSRange};
15use shaders::{ToFloat2 as _, ToUchar4 as _};
16use std::{collections::HashMap, ffi::c_void, mem, sync::Arc};
17
18const SHADERS_METALLIB: &'static [u8] =
19 include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
20const INSTANCE_BUFFER_SIZE: usize = 1024 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
21
22pub struct Renderer {
23 device: metal::Device,
24 sprite_cache: SpriteCache,
25 path_stencils: AtlasAllocator,
26 quad_pipeline_state: metal::RenderPipelineState,
27 shadow_pipeline_state: metal::RenderPipelineState,
28 sprite_pipeline_state: metal::RenderPipelineState,
29 path_stencil_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 sprite: shaders::GPUISprite,
38}
39
40impl Renderer {
41 pub fn new(
42 device: metal::Device,
43 pixel_format: metal::MTLPixelFormat,
44 fonts: Arc<dyn platform::FontSystem>,
45 ) -> Result<Self> {
46 let library = device
47 .new_library_with_data(SHADERS_METALLIB)
48 .map_err(|message| anyhow!("error building metal library: {}", message))?;
49
50 let unit_vertices = [
51 (0., 0.).to_float2(),
52 (1., 0.).to_float2(),
53 (0., 1.).to_float2(),
54 (0., 1.).to_float2(),
55 (1., 0.).to_float2(),
56 (1., 1.).to_float2(),
57 ];
58 let unit_vertices = device.new_buffer_with_data(
59 unit_vertices.as_ptr() as *const c_void,
60 (unit_vertices.len() * mem::size_of::<shaders::vector_float2>()) as u64,
61 MTLResourceOptions::StorageModeManaged,
62 );
63 let instances = device.new_buffer(
64 INSTANCE_BUFFER_SIZE as u64,
65 MTLResourceOptions::StorageModeManaged,
66 );
67
68 let path_stencil_descriptor = metal::TextureDescriptor::new();
69 path_stencil_descriptor.set_width(64);
70 path_stencil_descriptor.set_height(64);
71 path_stencil_descriptor.set_pixel_format(pixel_format);
72 path_stencil_descriptor
73 .set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead);
74 path_stencil_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
75
76 let sprite_cache = SpriteCache::new(device.clone(), vec2i(1024, 768), fonts);
77 let path_stencils = AtlasAllocator::new(device.clone(), path_stencil_descriptor);
78 let quad_pipeline_state = build_pipeline_state(
79 &device,
80 &library,
81 "quad",
82 "quad_vertex",
83 "quad_fragment",
84 pixel_format,
85 )?;
86 let shadow_pipeline_state = build_pipeline_state(
87 &device,
88 &library,
89 "shadow",
90 "shadow_vertex",
91 "shadow_fragment",
92 pixel_format,
93 )?;
94 let sprite_pipeline_state = build_pipeline_state(
95 &device,
96 &library,
97 "sprite",
98 "sprite_vertex",
99 "sprite_fragment",
100 pixel_format,
101 )?;
102 let path_stencil_pipeline_state = build_stencil_pipeline_state(
103 &device,
104 &library,
105 "path_winding",
106 "path_winding_vertex",
107 "path_winding_fragment",
108 pixel_format,
109 )?;
110 Ok(Self {
111 device,
112 sprite_cache,
113 path_stencils,
114 quad_pipeline_state,
115 shadow_pipeline_state,
116 sprite_pipeline_state,
117 path_stencil_pipeline_state,
118 unit_vertices,
119 instances,
120 })
121 }
122
123 pub fn render(
124 &mut self,
125 scene: &Scene,
126 drawable_size: Vector2F,
127 command_buffer: &metal::CommandBufferRef,
128 output: &metal::TextureRef,
129 ) {
130 let mut offset = 0;
131 let stencils = self.render_path_stencils(scene, &mut offset, command_buffer);
132 self.render_layers(
133 scene,
134 stencils,
135 &mut offset,
136 drawable_size,
137 command_buffer,
138 output,
139 );
140 }
141
142 fn render_path_stencils(
143 &mut self,
144 scene: &Scene,
145 offset: &mut usize,
146 command_buffer: &metal::CommandBufferRef,
147 ) -> Vec<PathSprite> {
148 let mut stencils = Vec::new();
149 let mut vertices = Vec::<shaders::GPUIPathVertex>::new();
150 let mut current_atlas_id = None;
151 for (layer_id, layer) in scene.layers().iter().enumerate() {
152 for path in layer.paths() {
153 // Push a PathStencil struct for use later when sampling from the atlas as we draw the content of the layers
154 let origin = path.bounds.origin() * scene.scale_factor();
155 let size = (path.bounds.size() * scene.scale_factor()).ceil();
156 let (atlas_id, atlas_origin) =
157 self.path_stencils.allocate(size.ceil().to_i32()).unwrap();
158 let atlas_origin = atlas_origin.to_f32();
159 stencils.push(PathSprite {
160 layer_id,
161 atlas_id,
162 sprite: shaders::GPUISprite {
163 origin: origin.floor().to_float2(),
164 size: size.to_float2(),
165 atlas_origin: atlas_origin.to_float2(),
166 color: path.color.to_uchar4(),
167 compute_winding: 1,
168 },
169 });
170
171 if current_atlas_id.map_or(false, |current_atlas_id| atlas_id != current_atlas_id) {
172 self.render_path_stencils_for_atlas(
173 offset,
174 &vertices,
175 atlas_id,
176 command_buffer,
177 );
178 vertices.clear();
179 }
180
181 current_atlas_id = Some(atlas_id);
182
183 // Populate the vertices by translating them to their appropriate location in the atlas.
184 for vertex in &path.vertices {
185 let xy_position =
186 (vertex.xy_position - path.bounds.origin()) * scene.scale_factor();
187 vertices.push(shaders::GPUIPathVertex {
188 xy_position: (atlas_origin + xy_position).to_float2(),
189 st_position: vertex.st_position.to_float2(),
190 });
191 }
192 }
193 }
194
195 if let Some(atlas_id) = current_atlas_id {
196 self.render_path_stencils_for_atlas(offset, &vertices, atlas_id, command_buffer);
197 }
198
199 stencils
200 }
201
202 fn render_path_stencils_for_atlas(
203 &mut self,
204 offset: &mut usize,
205 vertices: &[shaders::GPUIPathVertex],
206 atlas_id: usize,
207 command_buffer: &metal::CommandBufferRef,
208 ) {
209 align_offset(offset);
210 let next_offset = *offset + vertices.len() * mem::size_of::<shaders::GPUIPathVertex>();
211 assert!(
212 next_offset <= INSTANCE_BUFFER_SIZE,
213 "instance buffer exhausted"
214 );
215
216 let render_pass_descriptor = metal::RenderPassDescriptor::new();
217 let color_attachment = render_pass_descriptor
218 .color_attachments()
219 .object_at(0)
220 .unwrap();
221 let stencil_texture = self.path_stencils.texture(atlas_id).unwrap();
222 color_attachment.set_texture(Some(stencil_texture));
223 color_attachment.set_load_action(metal::MTLLoadAction::Clear);
224 color_attachment.set_store_action(metal::MTLStoreAction::Store);
225 color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 1.));
226 // let stencil_attachment = render_pass_descriptor.stencil_attachment().unwrap();
227 // let stencil_texture = self.path_stencils.texture(atlas_id).unwrap();
228 // stencil_attachment.set_texture(Some(stencil_texture));
229 // stencil_attachment.set_load_action(metal::MTLLoadAction::Clear);
230 // stencil_attachment.set_store_action(metal::MTLStoreAction::Store);
231
232 // let stencil_descriptor = metal::DepthStencilDescriptor::new();
233 // let front_face_stencil = stencil_descriptor.front_face_stencil().unwrap();
234 // front_face_stencil.set_depth_stencil_pass_operation(metal::MTLStencilOperation::Invert);
235 // front_face_stencil.set_depth_failure_operation(metal::MTLStencilOperation::Keep);
236 // front_face_stencil.set_stencil_compare_function(metal::MTLCompareFunction::Always);
237 // front_face_stencil.set_read_mask(0x1);
238 // front_face_stencil.set_write_mask(0x1);
239 // let depth_stencil_state = self.device.new_depth_stencil_state(&stencil_descriptor);
240
241 let winding_command_encoder =
242 command_buffer.new_render_command_encoder(render_pass_descriptor);
243 winding_command_encoder.set_render_pipeline_state(&self.path_stencil_pipeline_state);
244 winding_command_encoder.set_vertex_buffer(
245 shaders::GPUIPathWindingVertexInputIndex_GPUIPathWindingVertexInputIndexVertices as u64,
246 Some(&self.instances),
247 *offset as u64,
248 );
249 winding_command_encoder.set_vertex_bytes(
250 shaders::GPUIPathWindingVertexInputIndex_GPUIPathWindingVertexInputIndexAtlasSize
251 as u64,
252 mem::size_of::<shaders::vector_float2>() as u64,
253 [self.path_stencils.default_atlas_size().to_float2()].as_ptr() as *const c_void,
254 );
255
256 let buffer_contents = unsafe {
257 (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIPathVertex
258 };
259
260 for (ix, vertex) in vertices.iter().enumerate() {
261 unsafe {
262 *buffer_contents.add(ix) = *vertex;
263 }
264 }
265
266 self.instances.did_modify_range(NSRange {
267 location: *offset as u64,
268 length: (next_offset - *offset) as u64,
269 });
270 *offset = next_offset;
271
272 winding_command_encoder.draw_primitives(
273 metal::MTLPrimitiveType::Triangle,
274 0,
275 vertices.len() as u64,
276 );
277 winding_command_encoder.end_encoding();
278 }
279
280 fn render_layers(
281 &mut self,
282 scene: &Scene,
283 path_sprites: Vec<PathSprite>,
284 offset: &mut usize,
285 drawable_size: Vector2F,
286 command_buffer: &metal::CommandBufferRef,
287 output: &metal::TextureRef,
288 ) {
289 let render_pass_descriptor = metal::RenderPassDescriptor::new();
290 let color_attachment = render_pass_descriptor
291 .color_attachments()
292 .object_at(0)
293 .unwrap();
294 color_attachment.set_texture(Some(output));
295 color_attachment.set_load_action(metal::MTLLoadAction::Clear);
296 color_attachment.set_store_action(metal::MTLStoreAction::Store);
297 color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 1.));
298 let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
299
300 command_encoder.set_viewport(metal::MTLViewport {
301 originX: 0.0,
302 originY: 0.0,
303 width: drawable_size.x() as f64,
304 height: drawable_size.y() as f64,
305 znear: 0.0,
306 zfar: 1.0,
307 });
308
309 for (layer_id, layer) in scene.layers().iter().enumerate() {
310 self.clip(scene, layer, drawable_size, command_encoder);
311 self.render_shadows(scene, layer, offset, drawable_size, command_encoder);
312 self.render_quads(scene, layer, offset, drawable_size, command_encoder);
313 // TODO: Pass sprites relevant to this layer in a more efficient manner.
314 self.render_path_sprites(
315 scene,
316 layer,
317 path_sprites.iter().filter(|s| s.layer_id == layer_id),
318 offset,
319 drawable_size,
320 command_encoder,
321 );
322 self.render_glyph_sprites(scene, layer, offset, drawable_size, command_encoder);
323 }
324
325 command_encoder.end_encoding();
326 }
327
328 fn clip(
329 &mut self,
330 scene: &Scene,
331 layer: &Layer,
332 drawable_size: Vector2F,
333 command_encoder: &metal::RenderCommandEncoderRef,
334 ) {
335 let clip_bounds = layer.clip_bounds().unwrap_or(RectF::new(
336 vec2f(0., 0.),
337 drawable_size / scene.scale_factor(),
338 )) * scene.scale_factor();
339 command_encoder.set_scissor_rect(metal::MTLScissorRect {
340 x: clip_bounds.origin_x() as NSUInteger,
341 y: clip_bounds.origin_y() as NSUInteger,
342 width: clip_bounds.width() as NSUInteger,
343 height: clip_bounds.height() as NSUInteger,
344 });
345 }
346
347 fn render_shadows(
348 &mut self,
349 scene: &Scene,
350 layer: &Layer,
351 offset: &mut usize,
352 drawable_size: Vector2F,
353 command_encoder: &metal::RenderCommandEncoderRef,
354 ) {
355 if layer.shadows().is_empty() {
356 return;
357 }
358
359 align_offset(offset);
360 let next_offset = *offset + layer.shadows().len() * mem::size_of::<shaders::GPUIShadow>();
361 assert!(
362 next_offset <= INSTANCE_BUFFER_SIZE,
363 "instance buffer exhausted"
364 );
365
366 command_encoder.set_render_pipeline_state(&self.shadow_pipeline_state);
367 command_encoder.set_vertex_buffer(
368 shaders::GPUIShadowInputIndex_GPUIShadowInputIndexVertices as u64,
369 Some(&self.unit_vertices),
370 0,
371 );
372 command_encoder.set_vertex_buffer(
373 shaders::GPUIShadowInputIndex_GPUIShadowInputIndexShadows as u64,
374 Some(&self.instances),
375 *offset as u64,
376 );
377 command_encoder.set_vertex_bytes(
378 shaders::GPUIShadowInputIndex_GPUIShadowInputIndexUniforms as u64,
379 mem::size_of::<shaders::GPUIUniforms>() as u64,
380 [shaders::GPUIUniforms {
381 viewport_size: drawable_size.to_float2(),
382 }]
383 .as_ptr() as *const c_void,
384 );
385
386 let buffer_contents = unsafe {
387 (self.instances.contents() as *mut u8).offset(*offset as isize)
388 as *mut shaders::GPUIShadow
389 };
390 for (ix, shadow) in layer.shadows().iter().enumerate() {
391 let shape_bounds = shadow.bounds * scene.scale_factor();
392 let shader_shadow = shaders::GPUIShadow {
393 origin: shape_bounds.origin().to_float2(),
394 size: shape_bounds.size().to_float2(),
395 corner_radius: shadow.corner_radius * scene.scale_factor(),
396 sigma: shadow.sigma,
397 color: shadow.color.to_uchar4(),
398 };
399 unsafe {
400 *(buffer_contents.offset(ix as isize)) = shader_shadow;
401 }
402 }
403
404 self.instances.did_modify_range(NSRange {
405 location: *offset as u64,
406 length: (next_offset - *offset) as u64,
407 });
408 *offset = next_offset;
409
410 command_encoder.draw_primitives_instanced(
411 metal::MTLPrimitiveType::Triangle,
412 0,
413 6,
414 layer.shadows().len() as u64,
415 );
416 }
417
418 fn render_quads(
419 &mut self,
420 scene: &Scene,
421 layer: &Layer,
422 offset: &mut usize,
423 drawable_size: Vector2F,
424 command_encoder: &metal::RenderCommandEncoderRef,
425 ) {
426 if layer.quads().is_empty() {
427 return;
428 }
429 align_offset(offset);
430 let next_offset = *offset + layer.quads().len() * mem::size_of::<shaders::GPUIQuad>();
431 assert!(
432 next_offset <= INSTANCE_BUFFER_SIZE,
433 "instance buffer exhausted"
434 );
435
436 command_encoder.set_render_pipeline_state(&self.quad_pipeline_state);
437 command_encoder.set_vertex_buffer(
438 shaders::GPUIQuadInputIndex_GPUIQuadInputIndexVertices as u64,
439 Some(&self.unit_vertices),
440 0,
441 );
442 command_encoder.set_vertex_buffer(
443 shaders::GPUIQuadInputIndex_GPUIQuadInputIndexQuads as u64,
444 Some(&self.instances),
445 *offset as u64,
446 );
447 command_encoder.set_vertex_bytes(
448 shaders::GPUIQuadInputIndex_GPUIQuadInputIndexUniforms as u64,
449 mem::size_of::<shaders::GPUIUniforms>() as u64,
450 [shaders::GPUIUniforms {
451 viewport_size: drawable_size.to_float2(),
452 }]
453 .as_ptr() as *const c_void,
454 );
455
456 let buffer_contents = unsafe {
457 (self.instances.contents() as *mut u8).offset(*offset as isize)
458 as *mut shaders::GPUIQuad
459 };
460 for (ix, quad) in layer.quads().iter().enumerate() {
461 let bounds = quad.bounds * scene.scale_factor();
462 let border_width = quad.border.width * scene.scale_factor();
463 let shader_quad = shaders::GPUIQuad {
464 origin: bounds.origin().to_float2(),
465 size: bounds.size().to_float2(),
466 background_color: quad
467 .background
468 .unwrap_or(ColorU::transparent_black())
469 .to_uchar4(),
470 border_top: border_width * (quad.border.top as usize as f32),
471 border_right: border_width * (quad.border.right as usize as f32),
472 border_bottom: border_width * (quad.border.bottom as usize as f32),
473 border_left: border_width * (quad.border.left as usize as f32),
474 border_color: quad
475 .border
476 .color
477 .unwrap_or(ColorU::transparent_black())
478 .to_uchar4(),
479 corner_radius: quad.corner_radius * scene.scale_factor(),
480 };
481 unsafe {
482 *(buffer_contents.offset(ix as isize)) = shader_quad;
483 }
484 }
485
486 self.instances.did_modify_range(NSRange {
487 location: *offset as u64,
488 length: (next_offset - *offset) as u64,
489 });
490 *offset = next_offset;
491
492 command_encoder.draw_primitives_instanced(
493 metal::MTLPrimitiveType::Triangle,
494 0,
495 6,
496 layer.quads().len() as u64,
497 );
498 }
499
500 fn render_glyph_sprites(
501 &mut self,
502 scene: &Scene,
503 layer: &Layer,
504 offset: &mut usize,
505 drawable_size: Vector2F,
506 command_encoder: &metal::RenderCommandEncoderRef,
507 ) {
508 if layer.glyphs().is_empty() {
509 return;
510 }
511
512 let mut sprites_by_atlas = HashMap::new();
513 for glyph in layer.glyphs() {
514 if let Some(sprite) = self.sprite_cache.render_glyph(
515 glyph.font_id,
516 glyph.font_size,
517 glyph.id,
518 glyph.origin,
519 scene.scale_factor(),
520 ) {
521 // Snap sprite to pixel grid.
522 let origin = (glyph.origin * scene.scale_factor()).floor() + sprite.offset.to_f32();
523 sprites_by_atlas
524 .entry(sprite.atlas_id)
525 .or_insert_with(Vec::new)
526 .push(shaders::GPUISprite {
527 origin: origin.to_float2(),
528 size: sprite.size.to_float2(),
529 atlas_origin: sprite.atlas_origin.to_float2(),
530 color: glyph.color.to_uchar4(),
531 compute_winding: 0,
532 });
533 }
534 }
535
536 command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
537 command_encoder.set_vertex_buffer(
538 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
539 Some(&self.unit_vertices),
540 0,
541 );
542 command_encoder.set_vertex_bytes(
543 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64,
544 mem::size_of::<shaders::vector_float2>() as u64,
545 [drawable_size.to_float2()].as_ptr() as *const c_void,
546 );
547 command_encoder.set_vertex_bytes(
548 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
549 mem::size_of::<shaders::vector_float2>() as u64,
550 [self.sprite_cache.atlas_size().to_float2()].as_ptr() as *const c_void,
551 );
552
553 for (atlas_id, sprites) in sprites_by_atlas {
554 align_offset(offset);
555 let next_offset = *offset + sprites.len() * mem::size_of::<shaders::GPUISprite>();
556 assert!(
557 next_offset <= INSTANCE_BUFFER_SIZE,
558 "instance buffer exhausted"
559 );
560
561 command_encoder.set_vertex_buffer(
562 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
563 Some(&self.instances),
564 *offset as u64,
565 );
566
567 let texture = self.sprite_cache.atlas_texture(atlas_id).unwrap();
568 command_encoder.set_fragment_texture(
569 shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
570 Some(texture),
571 );
572
573 unsafe {
574 let buffer_contents = (self.instances.contents() as *mut u8)
575 .offset(*offset as isize)
576 as *mut shaders::GPUISprite;
577 std::ptr::copy_nonoverlapping(sprites.as_ptr(), buffer_contents, sprites.len());
578 }
579 self.instances.did_modify_range(NSRange {
580 location: *offset as u64,
581 length: (next_offset - *offset) as u64,
582 });
583 *offset = next_offset;
584
585 command_encoder.draw_primitives_instanced(
586 metal::MTLPrimitiveType::Triangle,
587 0,
588 6,
589 sprites.len() as u64,
590 );
591 }
592 }
593
594 fn render_path_sprites<'a>(
595 &mut self,
596 scene: &Scene,
597 layer: &Layer,
598 sprites: impl Iterator<Item = &'a PathSprite>,
599 offset: &mut usize,
600 drawable_size: Vector2F,
601 command_encoder: &metal::RenderCommandEncoderRef,
602 ) {
603 let mut sprites = sprites.peekable();
604 if sprites.peek().is_none() {
605 return;
606 }
607
608 let mut sprites_by_atlas = HashMap::new();
609 for sprite in sprites {
610 sprites_by_atlas
611 .entry(sprite.atlas_id)
612 .or_insert_with(Vec::new)
613 .push(sprite.sprite);
614 }
615
616 command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
617 command_encoder.set_vertex_buffer(
618 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
619 Some(&self.unit_vertices),
620 0,
621 );
622 command_encoder.set_vertex_bytes(
623 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64,
624 mem::size_of::<shaders::vector_float2>() as u64,
625 [drawable_size.to_float2()].as_ptr() as *const c_void,
626 );
627
628 for (atlas_id, sprites) in sprites_by_atlas {
629 align_offset(offset);
630 let next_offset = *offset + sprites.len() * mem::size_of::<shaders::GPUISprite>();
631 assert!(
632 next_offset <= INSTANCE_BUFFER_SIZE,
633 "instance buffer exhausted"
634 );
635
636 command_encoder.set_vertex_buffer(
637 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
638 Some(&self.instances),
639 *offset as u64,
640 );
641
642 let texture = self.path_stencils.texture(atlas_id).unwrap();
643 command_encoder.set_vertex_bytes(
644 shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
645 mem::size_of::<shaders::vector_float2>() as u64,
646 [vec2i(texture.width() as i32, texture.height() as i32).to_float2()].as_ptr()
647 as *const c_void,
648 );
649 command_encoder.set_fragment_texture(
650 shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
651 Some(texture),
652 );
653
654 unsafe {
655 let buffer_contents = (self.instances.contents() as *mut u8)
656 .offset(*offset as isize)
657 as *mut shaders::GPUISprite;
658 std::ptr::copy_nonoverlapping(sprites.as_ptr(), buffer_contents, sprites.len());
659 }
660 self.instances.did_modify_range(NSRange {
661 location: *offset as u64,
662 length: (next_offset - *offset) as u64,
663 });
664 *offset = next_offset;
665
666 command_encoder.draw_primitives_instanced(
667 metal::MTLPrimitiveType::Triangle,
668 0,
669 6,
670 sprites.len() as u64,
671 );
672 }
673 }
674}
675
676fn align_offset(offset: &mut usize) {
677 let r = *offset % 256;
678 if r > 0 {
679 *offset += 256 - r; // Align to a multiple of 256 to make Metal happy
680 }
681}
682
683fn build_pipeline_state(
684 device: &metal::DeviceRef,
685 library: &metal::LibraryRef,
686 label: &str,
687 vertex_fn_name: &str,
688 fragment_fn_name: &str,
689 pixel_format: metal::MTLPixelFormat,
690) -> Result<metal::RenderPipelineState> {
691 let vertex_fn = library
692 .get_function(vertex_fn_name, None)
693 .map_err(|message| anyhow!("error locating vertex function: {}", message))?;
694 let fragment_fn = library
695 .get_function(fragment_fn_name, None)
696 .map_err(|message| anyhow!("error locating fragment function: {}", message))?;
697
698 let descriptor = metal::RenderPipelineDescriptor::new();
699 descriptor.set_label(label);
700 descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
701 descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
702 let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
703 color_attachment.set_pixel_format(pixel_format);
704 color_attachment.set_blending_enabled(true);
705 color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
706 color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
707 color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::SourceAlpha);
708 color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::SourceAlpha);
709 color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
710 color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
711
712 device
713 .new_render_pipeline_state(&descriptor)
714 .map_err(|message| anyhow!("could not create render pipeline state: {}", message))
715}
716
717fn build_stencil_pipeline_state(
718 device: &metal::DeviceRef,
719 library: &metal::LibraryRef,
720 label: &str,
721 vertex_fn_name: &str,
722 fragment_fn_name: &str,
723 pixel_format: metal::MTLPixelFormat,
724) -> Result<metal::RenderPipelineState> {
725 let vertex_fn = library
726 .get_function(vertex_fn_name, None)
727 .map_err(|message| anyhow!("error locating vertex function: {}", message))?;
728 let fragment_fn = library
729 .get_function(fragment_fn_name, None)
730 .map_err(|message| anyhow!("error locating fragment function: {}", message))?;
731
732 let descriptor = metal::RenderPipelineDescriptor::new();
733 descriptor.set_label(label);
734 descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
735 descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
736 let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
737 color_attachment.set_pixel_format(pixel_format);
738 color_attachment.set_blending_enabled(true);
739 color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
740 color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
741 color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
742 color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
743 color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::One);
744 color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
745
746 device
747 .new_render_pipeline_state(&descriptor)
748 .map_err(|message| anyhow!("could not create render pipeline state: {}", message))
749}
750
751// fn build_stencil_pipeline_state(
752// device: &metal::DeviceRef,
753// library: &metal::LibraryRef,
754// label: &str,
755// vertex_fn_name: &str,
756// fragment_fn_name: &str,
757// pixel_format: metal::MTLPixelFormat,
758// ) -> Result<metal::RenderPipelineState> {
759// let vertex_fn = library
760// .get_function(vertex_fn_name, None)
761// .map_err(|message| anyhow!("error locating vertex function: {}", message))?;
762// let fragment_fn = library
763// .get_function(fragment_fn_name, None)
764// .map_err(|message| anyhow!("error locating fragment function: {}", message))?;
765
766// let descriptor = metal::RenderPipelineDescriptor::new();
767// descriptor.set_label(label);
768// descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
769// descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
770// descriptor.set_stencil_attachment_pixel_format(pixel_format);
771
772// device
773// .new_render_pipeline_state(&descriptor)
774// .map_err(|message| anyhow!("could not create render pipeline state: {}", message))
775// }
776
777mod shaders {
778 #![allow(non_upper_case_globals)]
779 #![allow(non_camel_case_types)]
780 #![allow(non_snake_case)]
781
782 use pathfinder_geometry::vector::Vector2I;
783
784 use crate::{color::ColorU, geometry::vector::Vector2F};
785 use std::mem;
786
787 include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
788
789 pub trait ToFloat2 {
790 fn to_float2(&self) -> vector_float2;
791 }
792
793 pub trait ToUchar4 {
794 fn to_uchar4(&self) -> vector_uchar4;
795 }
796
797 impl ToFloat2 for (f32, f32) {
798 fn to_float2(&self) -> vector_float2 {
799 unsafe {
800 let mut output = mem::transmute::<_, u32>(self.1.to_bits()) as vector_float2;
801 output <<= 32;
802 output |= mem::transmute::<_, u32>(self.0.to_bits()) as vector_float2;
803 output
804 }
805 }
806 }
807
808 impl ToFloat2 for Vector2F {
809 fn to_float2(&self) -> vector_float2 {
810 unsafe {
811 let mut output = mem::transmute::<_, u32>(self.y().to_bits()) as vector_float2;
812 output <<= 32;
813 output |= mem::transmute::<_, u32>(self.x().to_bits()) as vector_float2;
814 output
815 }
816 }
817 }
818
819 impl ToFloat2 for Vector2I {
820 fn to_float2(&self) -> vector_float2 {
821 self.to_f32().to_float2()
822 }
823 }
824
825 impl ToUchar4 for ColorU {
826 fn to_uchar4(&self) -> vector_uchar4 {
827 let mut vec = self.a as vector_uchar4;
828 vec <<= 8;
829 vec |= self.b as vector_uchar4;
830 vec <<= 8;
831 vec |= self.g as vector_uchar4;
832 vec <<= 8;
833 vec |= self.r as vector_uchar4;
834 vec
835 }
836 }
837}