renderer.rs

  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}