renderer.rs

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