renderer.rs

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