metal_renderer.rs

   1use super::metal_atlas::MetalAtlas;
   2use crate::{
   3    AtlasTextureId, Background, Bounds, ContentMask, DevicePixels, MonochromeSprite, PaintSurface,
   4    Path, Point, PolychromeSprite, PrimitiveBatch, Quad, ScaledPixels, Scene, Shadow, Size,
   5    Surface, Underline, point, size,
   6};
   7use anyhow::Result;
   8use block::ConcreteBlock;
   9use cocoa::{
  10    base::{NO, YES},
  11    foundation::{NSSize, NSUInteger},
  12    quartzcore::AutoresizingMask,
  13};
  14
  15use core_foundation::base::TCFType;
  16use core_video::{
  17    metal_texture::CVMetalTextureGetTexture, metal_texture_cache::CVMetalTextureCache,
  18    pixel_buffer::kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
  19};
  20use foreign_types::{ForeignType, ForeignTypeRef};
  21use metal::{
  22    CAMetalLayer, CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange,
  23    RenderPassColorAttachmentDescriptorRef,
  24};
  25use objc::{self, msg_send, sel, sel_impl};
  26use parking_lot::Mutex;
  27
  28use std::{cell::Cell, ffi::c_void, mem, ptr, sync::Arc};
  29
  30// Exported to metal
  31pub(crate) type PointF = crate::Point<f32>;
  32
  33#[cfg(not(feature = "runtime_shaders"))]
  34const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
  35#[cfg(feature = "runtime_shaders")]
  36const SHADERS_SOURCE_FILE: &str = include_str!(concat!(env!("OUT_DIR"), "/stitched_shaders.metal"));
  37// Use 4x MSAA, all devices support it.
  38// https://developer.apple.com/documentation/metal/mtldevice/1433355-supportstexturesamplecount
  39const PATH_SAMPLE_COUNT: u32 = 4;
  40
  41pub type Context = Arc<Mutex<InstanceBufferPool>>;
  42pub type Renderer = MetalRenderer;
  43
  44pub unsafe fn new_renderer(
  45    context: self::Context,
  46    _native_window: *mut c_void,
  47    _native_view: *mut c_void,
  48    _bounds: crate::Size<f32>,
  49    _transparent: bool,
  50) -> Renderer {
  51    MetalRenderer::new(context)
  52}
  53
  54pub(crate) struct InstanceBufferPool {
  55    buffer_size: usize,
  56    buffers: Vec<metal::Buffer>,
  57}
  58
  59impl Default for InstanceBufferPool {
  60    fn default() -> Self {
  61        Self {
  62            buffer_size: 2 * 1024 * 1024,
  63            buffers: Vec::new(),
  64        }
  65    }
  66}
  67
  68pub(crate) struct InstanceBuffer {
  69    metal_buffer: metal::Buffer,
  70    size: usize,
  71}
  72
  73impl InstanceBufferPool {
  74    pub(crate) fn reset(&mut self, buffer_size: usize) {
  75        self.buffer_size = buffer_size;
  76        self.buffers.clear();
  77    }
  78
  79    pub(crate) fn acquire(&mut self, device: &metal::Device) -> InstanceBuffer {
  80        let buffer = self.buffers.pop().unwrap_or_else(|| {
  81            device.new_buffer(
  82                self.buffer_size as u64,
  83                MTLResourceOptions::StorageModeManaged,
  84            )
  85        });
  86        InstanceBuffer {
  87            metal_buffer: buffer,
  88            size: self.buffer_size,
  89        }
  90    }
  91
  92    pub(crate) fn release(&mut self, buffer: InstanceBuffer) {
  93        if buffer.size == self.buffer_size {
  94            self.buffers.push(buffer.metal_buffer)
  95        }
  96    }
  97}
  98
  99pub(crate) struct MetalRenderer {
 100    device: metal::Device,
 101    layer: metal::MetalLayer,
 102    presents_with_transaction: bool,
 103    command_queue: CommandQueue,
 104    paths_rasterization_pipeline_state: metal::RenderPipelineState,
 105    path_sprites_pipeline_state: metal::RenderPipelineState,
 106    shadows_pipeline_state: metal::RenderPipelineState,
 107    quads_pipeline_state: metal::RenderPipelineState,
 108    underlines_pipeline_state: metal::RenderPipelineState,
 109    monochrome_sprites_pipeline_state: metal::RenderPipelineState,
 110    polychrome_sprites_pipeline_state: metal::RenderPipelineState,
 111    surfaces_pipeline_state: metal::RenderPipelineState,
 112    unit_vertices: metal::Buffer,
 113    #[allow(clippy::arc_with_non_send_sync)]
 114    instance_buffer_pool: Arc<Mutex<InstanceBufferPool>>,
 115    sprite_atlas: Arc<MetalAtlas>,
 116    core_video_texture_cache: core_video::metal_texture_cache::CVMetalTextureCache,
 117    path_intermediate_texture: Option<metal::Texture>,
 118    path_intermediate_msaa_texture: Option<metal::Texture>,
 119    path_sample_count: u32,
 120}
 121
 122#[repr(C)]
 123pub struct PathRasterizationVertex {
 124    pub xy_position: Point<ScaledPixels>,
 125    pub st_position: Point<f32>,
 126    pub color: Background,
 127    pub bounds: Bounds<ScaledPixels>,
 128}
 129
 130impl MetalRenderer {
 131    pub fn new(instance_buffer_pool: Arc<Mutex<InstanceBufferPool>>) -> Self {
 132        // Prefer low‐power integrated GPUs on Intel Mac. On Apple
 133        // Silicon, there is only ever one GPU, so this is equivalent to
 134        // `metal::Device::system_default()`.
 135        let mut devices = metal::Device::all();
 136        devices.sort_by_key(|device| (device.is_removable(), device.is_low_power()));
 137        let Some(device) = devices.pop() else {
 138            log::error!("unable to access a compatible graphics device");
 139            std::process::exit(1);
 140        };
 141
 142        let layer = metal::MetalLayer::new();
 143        layer.set_device(&device);
 144        layer.set_pixel_format(MTLPixelFormat::BGRA8Unorm);
 145        layer.set_opaque(false);
 146        layer.set_maximum_drawable_count(3);
 147        unsafe {
 148            let _: () = msg_send![&*layer, setAllowsNextDrawableTimeout: NO];
 149            let _: () = msg_send![&*layer, setNeedsDisplayOnBoundsChange: YES];
 150            let _: () = msg_send![
 151                &*layer,
 152                setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE
 153                    | AutoresizingMask::HEIGHT_SIZABLE
 154            ];
 155        }
 156        #[cfg(feature = "runtime_shaders")]
 157        let library = device
 158            .new_library_with_source(&SHADERS_SOURCE_FILE, &metal::CompileOptions::new())
 159            .expect("error building metal library");
 160        #[cfg(not(feature = "runtime_shaders"))]
 161        let library = device
 162            .new_library_with_data(SHADERS_METALLIB)
 163            .expect("error building metal library");
 164
 165        fn to_float2_bits(point: PointF) -> u64 {
 166            let mut output = point.y.to_bits() as u64;
 167            output <<= 32;
 168            output |= point.x.to_bits() as u64;
 169            output
 170        }
 171
 172        let unit_vertices = [
 173            to_float2_bits(point(0., 0.)),
 174            to_float2_bits(point(1., 0.)),
 175            to_float2_bits(point(0., 1.)),
 176            to_float2_bits(point(0., 1.)),
 177            to_float2_bits(point(1., 0.)),
 178            to_float2_bits(point(1., 1.)),
 179        ];
 180        let unit_vertices = device.new_buffer_with_data(
 181            unit_vertices.as_ptr() as *const c_void,
 182            mem::size_of_val(&unit_vertices) as u64,
 183            MTLResourceOptions::StorageModeManaged,
 184        );
 185
 186        let paths_rasterization_pipeline_state = build_path_rasterization_pipeline_state(
 187            &device,
 188            &library,
 189            "paths_rasterization",
 190            "path_rasterization_vertex",
 191            "path_rasterization_fragment",
 192            MTLPixelFormat::BGRA8Unorm,
 193            PATH_SAMPLE_COUNT,
 194        );
 195        let path_sprites_pipeline_state = build_path_sprite_pipeline_state(
 196            &device,
 197            &library,
 198            "path_sprites",
 199            "path_sprite_vertex",
 200            "path_sprite_fragment",
 201            MTLPixelFormat::BGRA8Unorm,
 202        );
 203        let shadows_pipeline_state = build_pipeline_state(
 204            &device,
 205            &library,
 206            "shadows",
 207            "shadow_vertex",
 208            "shadow_fragment",
 209            MTLPixelFormat::BGRA8Unorm,
 210        );
 211        let quads_pipeline_state = build_pipeline_state(
 212            &device,
 213            &library,
 214            "quads",
 215            "quad_vertex",
 216            "quad_fragment",
 217            MTLPixelFormat::BGRA8Unorm,
 218        );
 219        let underlines_pipeline_state = build_pipeline_state(
 220            &device,
 221            &library,
 222            "underlines",
 223            "underline_vertex",
 224            "underline_fragment",
 225            MTLPixelFormat::BGRA8Unorm,
 226        );
 227        let monochrome_sprites_pipeline_state = build_pipeline_state(
 228            &device,
 229            &library,
 230            "monochrome_sprites",
 231            "monochrome_sprite_vertex",
 232            "monochrome_sprite_fragment",
 233            MTLPixelFormat::BGRA8Unorm,
 234        );
 235        let polychrome_sprites_pipeline_state = build_pipeline_state(
 236            &device,
 237            &library,
 238            "polychrome_sprites",
 239            "polychrome_sprite_vertex",
 240            "polychrome_sprite_fragment",
 241            MTLPixelFormat::BGRA8Unorm,
 242        );
 243        let surfaces_pipeline_state = build_pipeline_state(
 244            &device,
 245            &library,
 246            "surfaces",
 247            "surface_vertex",
 248            "surface_fragment",
 249            MTLPixelFormat::BGRA8Unorm,
 250        );
 251
 252        let command_queue = device.new_command_queue();
 253        let sprite_atlas = Arc::new(MetalAtlas::new(device.clone()));
 254        let core_video_texture_cache =
 255            CVMetalTextureCache::new(None, device.clone(), None).unwrap();
 256
 257        Self {
 258            device,
 259            layer,
 260            presents_with_transaction: false,
 261            command_queue,
 262            paths_rasterization_pipeline_state,
 263            path_sprites_pipeline_state,
 264            shadows_pipeline_state,
 265            quads_pipeline_state,
 266            underlines_pipeline_state,
 267            monochrome_sprites_pipeline_state,
 268            polychrome_sprites_pipeline_state,
 269            surfaces_pipeline_state,
 270            unit_vertices,
 271            instance_buffer_pool,
 272            sprite_atlas,
 273            core_video_texture_cache,
 274            path_intermediate_texture: None,
 275            path_intermediate_msaa_texture: None,
 276            path_sample_count: PATH_SAMPLE_COUNT,
 277        }
 278    }
 279
 280    pub fn layer(&self) -> &metal::MetalLayerRef {
 281        &self.layer
 282    }
 283
 284    pub fn layer_ptr(&self) -> *mut CAMetalLayer {
 285        self.layer.as_ptr()
 286    }
 287
 288    pub fn sprite_atlas(&self) -> &Arc<MetalAtlas> {
 289        &self.sprite_atlas
 290    }
 291
 292    pub fn set_presents_with_transaction(&mut self, presents_with_transaction: bool) {
 293        self.presents_with_transaction = presents_with_transaction;
 294        self.layer
 295            .set_presents_with_transaction(presents_with_transaction);
 296    }
 297
 298    pub fn update_drawable_size(&mut self, size: Size<DevicePixels>) {
 299        let size = NSSize {
 300            width: size.width.0 as f64,
 301            height: size.height.0 as f64,
 302        };
 303        unsafe {
 304            let _: () = msg_send![
 305                self.layer(),
 306                setDrawableSize: size
 307            ];
 308        }
 309        let device_pixels_size = Size {
 310            width: DevicePixels(size.width as i32),
 311            height: DevicePixels(size.height as i32),
 312        };
 313        self.update_path_intermediate_textures(device_pixels_size);
 314    }
 315
 316    fn update_path_intermediate_textures(&mut self, size: Size<DevicePixels>) {
 317        let texture_descriptor = metal::TextureDescriptor::new();
 318        texture_descriptor.set_width(size.width.0 as u64);
 319        texture_descriptor.set_height(size.height.0 as u64);
 320        texture_descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm);
 321        texture_descriptor
 322            .set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead);
 323        self.path_intermediate_texture = Some(self.device.new_texture(&texture_descriptor));
 324
 325        if self.path_sample_count > 1 {
 326            let mut msaa_descriptor = texture_descriptor.clone();
 327            msaa_descriptor.set_texture_type(metal::MTLTextureType::D2Multisample);
 328            msaa_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
 329            msaa_descriptor.set_sample_count(self.path_sample_count as _);
 330            self.path_intermediate_msaa_texture = Some(self.device.new_texture(&msaa_descriptor));
 331        } else {
 332            self.path_intermediate_msaa_texture = None;
 333        }
 334    }
 335
 336    pub fn update_transparency(&self, _transparent: bool) {
 337        // todo(mac)?
 338    }
 339
 340    pub fn destroy(&self) {
 341        // nothing to do
 342    }
 343
 344    pub fn draw(&mut self, scene: &Scene) {
 345        let layer = self.layer.clone();
 346        let viewport_size = layer.drawable_size();
 347        let viewport_size: Size<DevicePixels> = size(
 348            (viewport_size.width.ceil() as i32).into(),
 349            (viewport_size.height.ceil() as i32).into(),
 350        );
 351        let drawable = if let Some(drawable) = layer.next_drawable() {
 352            drawable
 353        } else {
 354            log::error!(
 355                "failed to retrieve next drawable, drawable size: {:?}",
 356                viewport_size
 357            );
 358            return;
 359        };
 360
 361        loop {
 362            let mut instance_buffer = self.instance_buffer_pool.lock().acquire(&self.device);
 363
 364            let command_buffer =
 365                self.draw_primitives(scene, &mut instance_buffer, drawable, viewport_size);
 366
 367            match command_buffer {
 368                Ok(command_buffer) => {
 369                    let instance_buffer_pool = self.instance_buffer_pool.clone();
 370                    let instance_buffer = Cell::new(Some(instance_buffer));
 371                    let block = ConcreteBlock::new(move |_| {
 372                        if let Some(instance_buffer) = instance_buffer.take() {
 373                            instance_buffer_pool.lock().release(instance_buffer);
 374                        }
 375                    });
 376                    let block = block.copy();
 377                    command_buffer.add_completed_handler(&block);
 378
 379                    if self.presents_with_transaction {
 380                        command_buffer.commit();
 381                        command_buffer.wait_until_scheduled();
 382                        drawable.present();
 383                    } else {
 384                        command_buffer.present_drawable(drawable);
 385                        command_buffer.commit();
 386                    }
 387                    return;
 388                }
 389                Err(err) => {
 390                    log::error!(
 391                        "failed to render: {}. retrying with larger instance buffer size",
 392                        err
 393                    );
 394                    let mut instance_buffer_pool = self.instance_buffer_pool.lock();
 395                    let buffer_size = instance_buffer_pool.buffer_size;
 396                    if buffer_size >= 256 * 1024 * 1024 {
 397                        log::error!("instance buffer size grew too large: {}", buffer_size);
 398                        break;
 399                    }
 400                    instance_buffer_pool.reset(buffer_size * 2);
 401                    log::info!(
 402                        "increased instance buffer size to {}",
 403                        instance_buffer_pool.buffer_size
 404                    );
 405                }
 406            }
 407        }
 408    }
 409
 410    fn draw_primitives(
 411        &mut self,
 412        scene: &Scene,
 413        instance_buffer: &mut InstanceBuffer,
 414        drawable: &metal::MetalDrawableRef,
 415        viewport_size: Size<DevicePixels>,
 416    ) -> Result<metal::CommandBuffer> {
 417        let command_queue = self.command_queue.clone();
 418        let command_buffer = command_queue.new_command_buffer();
 419        let alpha = if self.layer.is_opaque() { 1. } else { 0. };
 420        let mut instance_offset = 0;
 421
 422        let mut command_encoder = new_command_encoder(
 423            command_buffer,
 424            drawable,
 425            viewport_size,
 426            |color_attachment| {
 427                color_attachment.set_load_action(metal::MTLLoadAction::Clear);
 428                color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., alpha));
 429            },
 430        );
 431
 432        for batch in scene.batches() {
 433            let ok = match batch {
 434                PrimitiveBatch::Shadows(shadows) => self.draw_shadows(
 435                    shadows,
 436                    instance_buffer,
 437                    &mut instance_offset,
 438                    viewport_size,
 439                    &command_encoder,
 440                ),
 441                PrimitiveBatch::Quads(quads) => self.draw_quads(
 442                    quads,
 443                    instance_buffer,
 444                    &mut instance_offset,
 445                    viewport_size,
 446                    &command_encoder,
 447                ),
 448                PrimitiveBatch::Paths(paths) => {
 449                    command_encoder.end_encoding();
 450
 451                    let did_draw = self.draw_paths_to_intermediate(
 452                        paths,
 453                        instance_buffer,
 454                        &mut instance_offset,
 455                        viewport_size,
 456                        command_buffer,
 457                    );
 458
 459                    command_encoder = new_command_encoder(
 460                        command_buffer,
 461                        drawable,
 462                        viewport_size,
 463                        |color_attachment| {
 464                            color_attachment.set_load_action(metal::MTLLoadAction::Load);
 465                        },
 466                    );
 467
 468                    if did_draw {
 469                        self.draw_paths_from_intermediate(
 470                            paths,
 471                            instance_buffer,
 472                            &mut instance_offset,
 473                            viewport_size,
 474                            &command_encoder,
 475                        )
 476                    } else {
 477                        false
 478                    }
 479                }
 480                PrimitiveBatch::Underlines(underlines) => self.draw_underlines(
 481                    underlines,
 482                    instance_buffer,
 483                    &mut instance_offset,
 484                    viewport_size,
 485                    &command_encoder,
 486                ),
 487                PrimitiveBatch::MonochromeSprites {
 488                    texture_id,
 489                    sprites,
 490                } => self.draw_monochrome_sprites(
 491                    texture_id,
 492                    sprites,
 493                    instance_buffer,
 494                    &mut instance_offset,
 495                    viewport_size,
 496                    &command_encoder,
 497                ),
 498                PrimitiveBatch::PolychromeSprites {
 499                    texture_id,
 500                    sprites,
 501                } => self.draw_polychrome_sprites(
 502                    texture_id,
 503                    sprites,
 504                    instance_buffer,
 505                    &mut instance_offset,
 506                    viewport_size,
 507                    &command_encoder,
 508                ),
 509                PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(
 510                    surfaces,
 511                    instance_buffer,
 512                    &mut instance_offset,
 513                    viewport_size,
 514                    &command_encoder,
 515                ),
 516            };
 517            if !ok {
 518                command_encoder.end_encoding();
 519                anyhow::bail!(
 520                    "scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
 521                    scene.paths.len(),
 522                    scene.shadows.len(),
 523                    scene.quads.len(),
 524                    scene.underlines.len(),
 525                    scene.monochrome_sprites.len(),
 526                    scene.polychrome_sprites.len(),
 527                    scene.surfaces.len(),
 528                );
 529            }
 530        }
 531
 532        command_encoder.end_encoding();
 533
 534        instance_buffer.metal_buffer.did_modify_range(NSRange {
 535            location: 0,
 536            length: instance_offset as NSUInteger,
 537        });
 538        Ok(command_buffer.to_owned())
 539    }
 540
 541    fn draw_paths_to_intermediate(
 542        &self,
 543        paths: &[Path<ScaledPixels>],
 544        instance_buffer: &mut InstanceBuffer,
 545        instance_offset: &mut usize,
 546        viewport_size: Size<DevicePixels>,
 547        command_buffer: &metal::CommandBufferRef,
 548    ) -> bool {
 549        if paths.is_empty() {
 550            return true;
 551        }
 552        let Some(intermediate_texture) = &self.path_intermediate_texture else {
 553            return false;
 554        };
 555
 556        let render_pass_descriptor = metal::RenderPassDescriptor::new();
 557        let color_attachment = render_pass_descriptor
 558            .color_attachments()
 559            .object_at(0)
 560            .unwrap();
 561        color_attachment.set_load_action(metal::MTLLoadAction::Clear);
 562        color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 0.));
 563
 564        if let Some(msaa_texture) = &self.path_intermediate_msaa_texture {
 565            color_attachment.set_texture(Some(msaa_texture));
 566            color_attachment.set_resolve_texture(Some(intermediate_texture));
 567            color_attachment.set_store_action(metal::MTLStoreAction::MultisampleResolve);
 568        } else {
 569            color_attachment.set_texture(Some(intermediate_texture));
 570            color_attachment.set_store_action(metal::MTLStoreAction::Store);
 571        }
 572
 573        let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
 574        command_encoder.set_render_pipeline_state(&self.paths_rasterization_pipeline_state);
 575
 576        align_offset(instance_offset);
 577        let mut vertices = Vec::new();
 578        for path in paths {
 579            vertices.extend(path.vertices.iter().map(|v| PathRasterizationVertex {
 580                xy_position: v.xy_position,
 581                st_position: v.st_position,
 582                color: path.color,
 583                bounds: path.bounds.intersect(&path.content_mask.bounds),
 584            }));
 585        }
 586        let vertices_bytes_len = mem::size_of_val(vertices.as_slice());
 587        let next_offset = *instance_offset + vertices_bytes_len;
 588        if next_offset > instance_buffer.size {
 589            command_encoder.end_encoding();
 590            return false;
 591        }
 592        command_encoder.set_vertex_buffer(
 593            PathRasterizationInputIndex::Vertices as u64,
 594            Some(&instance_buffer.metal_buffer),
 595            *instance_offset as u64,
 596        );
 597        command_encoder.set_vertex_bytes(
 598            PathRasterizationInputIndex::ViewportSize as u64,
 599            mem::size_of_val(&viewport_size) as u64,
 600            &viewport_size as *const Size<DevicePixels> as *const _,
 601        );
 602        command_encoder.set_fragment_buffer(
 603            PathRasterizationInputIndex::Vertices as u64,
 604            Some(&instance_buffer.metal_buffer),
 605            *instance_offset as u64,
 606        );
 607        let buffer_contents =
 608            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 609        unsafe {
 610            ptr::copy_nonoverlapping(
 611                vertices.as_ptr() as *const u8,
 612                buffer_contents,
 613                vertices_bytes_len,
 614            );
 615        }
 616        command_encoder.draw_primitives(
 617            metal::MTLPrimitiveType::Triangle,
 618            0,
 619            vertices.len() as u64,
 620        );
 621        *instance_offset = next_offset;
 622
 623        command_encoder.end_encoding();
 624        true
 625    }
 626
 627    fn draw_shadows(
 628        &self,
 629        shadows: &[Shadow],
 630        instance_buffer: &mut InstanceBuffer,
 631        instance_offset: &mut usize,
 632        viewport_size: Size<DevicePixels>,
 633        command_encoder: &metal::RenderCommandEncoderRef,
 634    ) -> bool {
 635        if shadows.is_empty() {
 636            return true;
 637        }
 638        align_offset(instance_offset);
 639
 640        command_encoder.set_render_pipeline_state(&self.shadows_pipeline_state);
 641        command_encoder.set_vertex_buffer(
 642            ShadowInputIndex::Vertices as u64,
 643            Some(&self.unit_vertices),
 644            0,
 645        );
 646        command_encoder.set_vertex_buffer(
 647            ShadowInputIndex::Shadows as u64,
 648            Some(&instance_buffer.metal_buffer),
 649            *instance_offset as u64,
 650        );
 651        command_encoder.set_fragment_buffer(
 652            ShadowInputIndex::Shadows as u64,
 653            Some(&instance_buffer.metal_buffer),
 654            *instance_offset as u64,
 655        );
 656
 657        command_encoder.set_vertex_bytes(
 658            ShadowInputIndex::ViewportSize as u64,
 659            mem::size_of_val(&viewport_size) as u64,
 660            &viewport_size as *const Size<DevicePixels> as *const _,
 661        );
 662
 663        let shadow_bytes_len = mem::size_of_val(shadows);
 664        let buffer_contents =
 665            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 666
 667        let next_offset = *instance_offset + shadow_bytes_len;
 668        if next_offset > instance_buffer.size {
 669            return false;
 670        }
 671
 672        unsafe {
 673            ptr::copy_nonoverlapping(
 674                shadows.as_ptr() as *const u8,
 675                buffer_contents,
 676                shadow_bytes_len,
 677            );
 678        }
 679
 680        command_encoder.draw_primitives_instanced(
 681            metal::MTLPrimitiveType::Triangle,
 682            0,
 683            6,
 684            shadows.len() as u64,
 685        );
 686        *instance_offset = next_offset;
 687        true
 688    }
 689
 690    fn draw_quads(
 691        &self,
 692        quads: &[Quad],
 693        instance_buffer: &mut InstanceBuffer,
 694        instance_offset: &mut usize,
 695        viewport_size: Size<DevicePixels>,
 696        command_encoder: &metal::RenderCommandEncoderRef,
 697    ) -> bool {
 698        if quads.is_empty() {
 699            return true;
 700        }
 701        align_offset(instance_offset);
 702
 703        command_encoder.set_render_pipeline_state(&self.quads_pipeline_state);
 704        command_encoder.set_vertex_buffer(
 705            QuadInputIndex::Vertices as u64,
 706            Some(&self.unit_vertices),
 707            0,
 708        );
 709        command_encoder.set_vertex_buffer(
 710            QuadInputIndex::Quads as u64,
 711            Some(&instance_buffer.metal_buffer),
 712            *instance_offset as u64,
 713        );
 714        command_encoder.set_fragment_buffer(
 715            QuadInputIndex::Quads as u64,
 716            Some(&instance_buffer.metal_buffer),
 717            *instance_offset as u64,
 718        );
 719
 720        command_encoder.set_vertex_bytes(
 721            QuadInputIndex::ViewportSize as u64,
 722            mem::size_of_val(&viewport_size) as u64,
 723            &viewport_size as *const Size<DevicePixels> as *const _,
 724        );
 725
 726        let quad_bytes_len = mem::size_of_val(quads);
 727        let buffer_contents =
 728            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 729
 730        let next_offset = *instance_offset + quad_bytes_len;
 731        if next_offset > instance_buffer.size {
 732            return false;
 733        }
 734
 735        unsafe {
 736            ptr::copy_nonoverlapping(quads.as_ptr() as *const u8, buffer_contents, quad_bytes_len);
 737        }
 738
 739        command_encoder.draw_primitives_instanced(
 740            metal::MTLPrimitiveType::Triangle,
 741            0,
 742            6,
 743            quads.len() as u64,
 744        );
 745        *instance_offset = next_offset;
 746        true
 747    }
 748
 749    fn draw_paths_from_intermediate(
 750        &self,
 751        paths: &[Path<ScaledPixels>],
 752        instance_buffer: &mut InstanceBuffer,
 753        instance_offset: &mut usize,
 754        viewport_size: Size<DevicePixels>,
 755        command_encoder: &metal::RenderCommandEncoderRef,
 756    ) -> bool {
 757        let Some(ref first_path) = paths.first() else {
 758            return true;
 759        };
 760
 761        let Some(ref intermediate_texture) = self.path_intermediate_texture else {
 762            return false;
 763        };
 764
 765        command_encoder.set_render_pipeline_state(&self.path_sprites_pipeline_state);
 766        command_encoder.set_vertex_buffer(
 767            SpriteInputIndex::Vertices as u64,
 768            Some(&self.unit_vertices),
 769            0,
 770        );
 771        command_encoder.set_vertex_bytes(
 772            SpriteInputIndex::ViewportSize as u64,
 773            mem::size_of_val(&viewport_size) as u64,
 774            &viewport_size as *const Size<DevicePixels> as *const _,
 775        );
 776
 777        command_encoder.set_fragment_texture(
 778            SpriteInputIndex::AtlasTexture as u64,
 779            Some(intermediate_texture),
 780        );
 781
 782        // When copying paths from the intermediate texture to the drawable,
 783        // each pixel must only be copied once, in case of transparent paths.
 784        //
 785        // If all paths have the same draw order, then their bounds are all
 786        // disjoint, so we can copy each path's bounds individually. If this
 787        // batch combines different draw orders, we perform a single copy
 788        // for a minimal spanning rect.
 789        let sprites;
 790        if paths.last().unwrap().order == first_path.order {
 791            sprites = paths
 792                .iter()
 793                .map(|path| PathSprite {
 794                    bounds: path.clipped_bounds(),
 795                })
 796                .collect();
 797        } else {
 798            let mut bounds = first_path.clipped_bounds();
 799            for path in paths.iter().skip(1) {
 800                bounds = bounds.union(&path.clipped_bounds());
 801            }
 802            sprites = vec![PathSprite { bounds }];
 803        }
 804
 805        align_offset(instance_offset);
 806        let sprite_bytes_len = mem::size_of_val(sprites.as_slice());
 807        let next_offset = *instance_offset + sprite_bytes_len;
 808        if next_offset > instance_buffer.size {
 809            return false;
 810        }
 811
 812        command_encoder.set_vertex_buffer(
 813            SpriteInputIndex::Sprites as u64,
 814            Some(&instance_buffer.metal_buffer),
 815            *instance_offset as u64,
 816        );
 817
 818        let buffer_contents =
 819            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 820        unsafe {
 821            ptr::copy_nonoverlapping(
 822                sprites.as_ptr() as *const u8,
 823                buffer_contents,
 824                sprite_bytes_len,
 825            );
 826        }
 827
 828        command_encoder.draw_primitives_instanced(
 829            metal::MTLPrimitiveType::Triangle,
 830            0,
 831            6,
 832            sprites.len() as u64,
 833        );
 834        *instance_offset = next_offset;
 835
 836        true
 837    }
 838
 839    fn draw_underlines(
 840        &self,
 841        underlines: &[Underline],
 842        instance_buffer: &mut InstanceBuffer,
 843        instance_offset: &mut usize,
 844        viewport_size: Size<DevicePixels>,
 845        command_encoder: &metal::RenderCommandEncoderRef,
 846    ) -> bool {
 847        if underlines.is_empty() {
 848            return true;
 849        }
 850        align_offset(instance_offset);
 851
 852        command_encoder.set_render_pipeline_state(&self.underlines_pipeline_state);
 853        command_encoder.set_vertex_buffer(
 854            UnderlineInputIndex::Vertices as u64,
 855            Some(&self.unit_vertices),
 856            0,
 857        );
 858        command_encoder.set_vertex_buffer(
 859            UnderlineInputIndex::Underlines as u64,
 860            Some(&instance_buffer.metal_buffer),
 861            *instance_offset as u64,
 862        );
 863        command_encoder.set_fragment_buffer(
 864            UnderlineInputIndex::Underlines as u64,
 865            Some(&instance_buffer.metal_buffer),
 866            *instance_offset as u64,
 867        );
 868
 869        command_encoder.set_vertex_bytes(
 870            UnderlineInputIndex::ViewportSize as u64,
 871            mem::size_of_val(&viewport_size) as u64,
 872            &viewport_size as *const Size<DevicePixels> as *const _,
 873        );
 874
 875        let underline_bytes_len = mem::size_of_val(underlines);
 876        let buffer_contents =
 877            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 878
 879        let next_offset = *instance_offset + underline_bytes_len;
 880        if next_offset > instance_buffer.size {
 881            return false;
 882        }
 883
 884        unsafe {
 885            ptr::copy_nonoverlapping(
 886                underlines.as_ptr() as *const u8,
 887                buffer_contents,
 888                underline_bytes_len,
 889            );
 890        }
 891
 892        command_encoder.draw_primitives_instanced(
 893            metal::MTLPrimitiveType::Triangle,
 894            0,
 895            6,
 896            underlines.len() as u64,
 897        );
 898        *instance_offset = next_offset;
 899        true
 900    }
 901
 902    fn draw_monochrome_sprites(
 903        &self,
 904        texture_id: AtlasTextureId,
 905        sprites: &[MonochromeSprite],
 906        instance_buffer: &mut InstanceBuffer,
 907        instance_offset: &mut usize,
 908        viewport_size: Size<DevicePixels>,
 909        command_encoder: &metal::RenderCommandEncoderRef,
 910    ) -> bool {
 911        if sprites.is_empty() {
 912            return true;
 913        }
 914        align_offset(instance_offset);
 915
 916        let sprite_bytes_len = mem::size_of_val(sprites);
 917        let buffer_contents =
 918            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 919
 920        let next_offset = *instance_offset + sprite_bytes_len;
 921        if next_offset > instance_buffer.size {
 922            return false;
 923        }
 924
 925        let texture = self.sprite_atlas.metal_texture(texture_id);
 926        let texture_size = size(
 927            DevicePixels(texture.width() as i32),
 928            DevicePixels(texture.height() as i32),
 929        );
 930        command_encoder.set_render_pipeline_state(&self.monochrome_sprites_pipeline_state);
 931        command_encoder.set_vertex_buffer(
 932            SpriteInputIndex::Vertices as u64,
 933            Some(&self.unit_vertices),
 934            0,
 935        );
 936        command_encoder.set_vertex_buffer(
 937            SpriteInputIndex::Sprites as u64,
 938            Some(&instance_buffer.metal_buffer),
 939            *instance_offset as u64,
 940        );
 941        command_encoder.set_vertex_bytes(
 942            SpriteInputIndex::ViewportSize as u64,
 943            mem::size_of_val(&viewport_size) as u64,
 944            &viewport_size as *const Size<DevicePixels> as *const _,
 945        );
 946        command_encoder.set_vertex_bytes(
 947            SpriteInputIndex::AtlasTextureSize as u64,
 948            mem::size_of_val(&texture_size) as u64,
 949            &texture_size as *const Size<DevicePixels> as *const _,
 950        );
 951        command_encoder.set_fragment_buffer(
 952            SpriteInputIndex::Sprites as u64,
 953            Some(&instance_buffer.metal_buffer),
 954            *instance_offset as u64,
 955        );
 956        command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
 957
 958        unsafe {
 959            ptr::copy_nonoverlapping(
 960                sprites.as_ptr() as *const u8,
 961                buffer_contents,
 962                sprite_bytes_len,
 963            );
 964        }
 965
 966        command_encoder.draw_primitives_instanced(
 967            metal::MTLPrimitiveType::Triangle,
 968            0,
 969            6,
 970            sprites.len() as u64,
 971        );
 972        *instance_offset = next_offset;
 973        true
 974    }
 975
 976    fn draw_polychrome_sprites(
 977        &self,
 978        texture_id: AtlasTextureId,
 979        sprites: &[PolychromeSprite],
 980        instance_buffer: &mut InstanceBuffer,
 981        instance_offset: &mut usize,
 982        viewport_size: Size<DevicePixels>,
 983        command_encoder: &metal::RenderCommandEncoderRef,
 984    ) -> bool {
 985        if sprites.is_empty() {
 986            return true;
 987        }
 988        align_offset(instance_offset);
 989
 990        let texture = self.sprite_atlas.metal_texture(texture_id);
 991        let texture_size = size(
 992            DevicePixels(texture.width() as i32),
 993            DevicePixels(texture.height() as i32),
 994        );
 995        command_encoder.set_render_pipeline_state(&self.polychrome_sprites_pipeline_state);
 996        command_encoder.set_vertex_buffer(
 997            SpriteInputIndex::Vertices as u64,
 998            Some(&self.unit_vertices),
 999            0,
1000        );
1001        command_encoder.set_vertex_buffer(
1002            SpriteInputIndex::Sprites as u64,
1003            Some(&instance_buffer.metal_buffer),
1004            *instance_offset as u64,
1005        );
1006        command_encoder.set_vertex_bytes(
1007            SpriteInputIndex::ViewportSize as u64,
1008            mem::size_of_val(&viewport_size) as u64,
1009            &viewport_size as *const Size<DevicePixels> as *const _,
1010        );
1011        command_encoder.set_vertex_bytes(
1012            SpriteInputIndex::AtlasTextureSize as u64,
1013            mem::size_of_val(&texture_size) as u64,
1014            &texture_size as *const Size<DevicePixels> as *const _,
1015        );
1016        command_encoder.set_fragment_buffer(
1017            SpriteInputIndex::Sprites as u64,
1018            Some(&instance_buffer.metal_buffer),
1019            *instance_offset as u64,
1020        );
1021        command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
1022
1023        let sprite_bytes_len = mem::size_of_val(sprites);
1024        let buffer_contents =
1025            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
1026
1027        let next_offset = *instance_offset + sprite_bytes_len;
1028        if next_offset > instance_buffer.size {
1029            return false;
1030        }
1031
1032        unsafe {
1033            ptr::copy_nonoverlapping(
1034                sprites.as_ptr() as *const u8,
1035                buffer_contents,
1036                sprite_bytes_len,
1037            );
1038        }
1039
1040        command_encoder.draw_primitives_instanced(
1041            metal::MTLPrimitiveType::Triangle,
1042            0,
1043            6,
1044            sprites.len() as u64,
1045        );
1046        *instance_offset = next_offset;
1047        true
1048    }
1049
1050    fn draw_surfaces(
1051        &mut self,
1052        surfaces: &[PaintSurface],
1053        instance_buffer: &mut InstanceBuffer,
1054        instance_offset: &mut usize,
1055        viewport_size: Size<DevicePixels>,
1056        command_encoder: &metal::RenderCommandEncoderRef,
1057    ) -> bool {
1058        command_encoder.set_render_pipeline_state(&self.surfaces_pipeline_state);
1059        command_encoder.set_vertex_buffer(
1060            SurfaceInputIndex::Vertices as u64,
1061            Some(&self.unit_vertices),
1062            0,
1063        );
1064        command_encoder.set_vertex_bytes(
1065            SurfaceInputIndex::ViewportSize as u64,
1066            mem::size_of_val(&viewport_size) as u64,
1067            &viewport_size as *const Size<DevicePixels> as *const _,
1068        );
1069
1070        for surface in surfaces {
1071            let texture_size = size(
1072                DevicePixels::from(surface.image_buffer.get_width() as i32),
1073                DevicePixels::from(surface.image_buffer.get_height() as i32),
1074            );
1075
1076            assert_eq!(
1077                surface.image_buffer.get_pixel_format(),
1078                kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
1079            );
1080
1081            let y_texture = self
1082                .core_video_texture_cache
1083                .create_texture_from_image(
1084                    surface.image_buffer.as_concrete_TypeRef(),
1085                    None,
1086                    MTLPixelFormat::R8Unorm,
1087                    surface.image_buffer.get_width_of_plane(0),
1088                    surface.image_buffer.get_height_of_plane(0),
1089                    0,
1090                )
1091                .unwrap();
1092            let cb_cr_texture = self
1093                .core_video_texture_cache
1094                .create_texture_from_image(
1095                    surface.image_buffer.as_concrete_TypeRef(),
1096                    None,
1097                    MTLPixelFormat::RG8Unorm,
1098                    surface.image_buffer.get_width_of_plane(1),
1099                    surface.image_buffer.get_height_of_plane(1),
1100                    1,
1101                )
1102                .unwrap();
1103
1104            align_offset(instance_offset);
1105            let next_offset = *instance_offset + mem::size_of::<Surface>();
1106            if next_offset > instance_buffer.size {
1107                return false;
1108            }
1109
1110            command_encoder.set_vertex_buffer(
1111                SurfaceInputIndex::Surfaces as u64,
1112                Some(&instance_buffer.metal_buffer),
1113                *instance_offset as u64,
1114            );
1115            command_encoder.set_vertex_bytes(
1116                SurfaceInputIndex::TextureSize as u64,
1117                mem::size_of_val(&texture_size) as u64,
1118                &texture_size as *const Size<DevicePixels> as *const _,
1119            );
1120            // let y_texture = y_texture.get_texture().unwrap().
1121            command_encoder.set_fragment_texture(SurfaceInputIndex::YTexture as u64, unsafe {
1122                let texture = CVMetalTextureGetTexture(y_texture.as_concrete_TypeRef());
1123                Some(metal::TextureRef::from_ptr(texture as *mut _))
1124            });
1125            command_encoder.set_fragment_texture(SurfaceInputIndex::CbCrTexture as u64, unsafe {
1126                let texture = CVMetalTextureGetTexture(cb_cr_texture.as_concrete_TypeRef());
1127                Some(metal::TextureRef::from_ptr(texture as *mut _))
1128            });
1129
1130            unsafe {
1131                let buffer_contents = (instance_buffer.metal_buffer.contents() as *mut u8)
1132                    .add(*instance_offset)
1133                    as *mut SurfaceBounds;
1134                ptr::write(
1135                    buffer_contents,
1136                    SurfaceBounds {
1137                        bounds: surface.bounds,
1138                        content_mask: surface.content_mask.clone(),
1139                    },
1140                );
1141            }
1142
1143            command_encoder.draw_primitives(metal::MTLPrimitiveType::Triangle, 0, 6);
1144            *instance_offset = next_offset;
1145        }
1146        true
1147    }
1148}
1149
1150fn new_command_encoder<'a>(
1151    command_buffer: &'a metal::CommandBufferRef,
1152    drawable: &'a metal::MetalDrawableRef,
1153    viewport_size: Size<DevicePixels>,
1154    configure_color_attachment: impl Fn(&RenderPassColorAttachmentDescriptorRef),
1155) -> &'a metal::RenderCommandEncoderRef {
1156    let render_pass_descriptor = metal::RenderPassDescriptor::new();
1157    let color_attachment = render_pass_descriptor
1158        .color_attachments()
1159        .object_at(0)
1160        .unwrap();
1161    color_attachment.set_texture(Some(drawable.texture()));
1162    color_attachment.set_store_action(metal::MTLStoreAction::Store);
1163    configure_color_attachment(color_attachment);
1164
1165    let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
1166    command_encoder.set_viewport(metal::MTLViewport {
1167        originX: 0.0,
1168        originY: 0.0,
1169        width: i32::from(viewport_size.width) as f64,
1170        height: i32::from(viewport_size.height) as f64,
1171        znear: 0.0,
1172        zfar: 1.0,
1173    });
1174    command_encoder
1175}
1176
1177fn build_pipeline_state(
1178    device: &metal::DeviceRef,
1179    library: &metal::LibraryRef,
1180    label: &str,
1181    vertex_fn_name: &str,
1182    fragment_fn_name: &str,
1183    pixel_format: metal::MTLPixelFormat,
1184) -> metal::RenderPipelineState {
1185    let vertex_fn = library
1186        .get_function(vertex_fn_name, None)
1187        .expect("error locating vertex function");
1188    let fragment_fn = library
1189        .get_function(fragment_fn_name, None)
1190        .expect("error locating fragment function");
1191
1192    let descriptor = metal::RenderPipelineDescriptor::new();
1193    descriptor.set_label(label);
1194    descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
1195    descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
1196    let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
1197    color_attachment.set_pixel_format(pixel_format);
1198    color_attachment.set_blending_enabled(true);
1199    color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
1200    color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
1201    color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::SourceAlpha);
1202    color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
1203    color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
1204    color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
1205
1206    device
1207        .new_render_pipeline_state(&descriptor)
1208        .expect("could not create render pipeline state")
1209}
1210
1211fn build_path_sprite_pipeline_state(
1212    device: &metal::DeviceRef,
1213    library: &metal::LibraryRef,
1214    label: &str,
1215    vertex_fn_name: &str,
1216    fragment_fn_name: &str,
1217    pixel_format: metal::MTLPixelFormat,
1218) -> metal::RenderPipelineState {
1219    let vertex_fn = library
1220        .get_function(vertex_fn_name, None)
1221        .expect("error locating vertex function");
1222    let fragment_fn = library
1223        .get_function(fragment_fn_name, None)
1224        .expect("error locating fragment function");
1225
1226    let descriptor = metal::RenderPipelineDescriptor::new();
1227    descriptor.set_label(label);
1228    descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
1229    descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
1230    let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
1231    color_attachment.set_pixel_format(pixel_format);
1232    color_attachment.set_blending_enabled(true);
1233    color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
1234    color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
1235    color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
1236    color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
1237    color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
1238    color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
1239
1240    device
1241        .new_render_pipeline_state(&descriptor)
1242        .expect("could not create render pipeline state")
1243}
1244
1245fn build_path_rasterization_pipeline_state(
1246    device: &metal::DeviceRef,
1247    library: &metal::LibraryRef,
1248    label: &str,
1249    vertex_fn_name: &str,
1250    fragment_fn_name: &str,
1251    pixel_format: metal::MTLPixelFormat,
1252    path_sample_count: u32,
1253) -> metal::RenderPipelineState {
1254    let vertex_fn = library
1255        .get_function(vertex_fn_name, None)
1256        .expect("error locating vertex function");
1257    let fragment_fn = library
1258        .get_function(fragment_fn_name, None)
1259        .expect("error locating fragment function");
1260
1261    let descriptor = metal::RenderPipelineDescriptor::new();
1262    descriptor.set_label(label);
1263    descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
1264    descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
1265    if path_sample_count > 1 {
1266        descriptor.set_raster_sample_count(path_sample_count as _);
1267        descriptor.set_alpha_to_coverage_enabled(false);
1268    }
1269    let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
1270    color_attachment.set_pixel_format(pixel_format);
1271    color_attachment.set_blending_enabled(true);
1272    color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
1273    color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
1274    color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
1275    color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
1276    color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
1277    color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
1278
1279    device
1280        .new_render_pipeline_state(&descriptor)
1281        .expect("could not create render pipeline state")
1282}
1283
1284// Align to multiples of 256 make Metal happy.
1285fn align_offset(offset: &mut usize) {
1286    *offset = (*offset).div_ceil(256) * 256;
1287}
1288
1289#[repr(C)]
1290enum ShadowInputIndex {
1291    Vertices = 0,
1292    Shadows = 1,
1293    ViewportSize = 2,
1294}
1295
1296#[repr(C)]
1297enum QuadInputIndex {
1298    Vertices = 0,
1299    Quads = 1,
1300    ViewportSize = 2,
1301}
1302
1303#[repr(C)]
1304enum UnderlineInputIndex {
1305    Vertices = 0,
1306    Underlines = 1,
1307    ViewportSize = 2,
1308}
1309
1310#[repr(C)]
1311enum SpriteInputIndex {
1312    Vertices = 0,
1313    Sprites = 1,
1314    ViewportSize = 2,
1315    AtlasTextureSize = 3,
1316    AtlasTexture = 4,
1317}
1318
1319#[repr(C)]
1320enum SurfaceInputIndex {
1321    Vertices = 0,
1322    Surfaces = 1,
1323    ViewportSize = 2,
1324    TextureSize = 3,
1325    YTexture = 4,
1326    CbCrTexture = 5,
1327}
1328
1329#[repr(C)]
1330enum PathRasterizationInputIndex {
1331    Vertices = 0,
1332    ViewportSize = 1,
1333}
1334
1335#[derive(Clone, Debug, Eq, PartialEq)]
1336#[repr(C)]
1337pub struct PathSprite {
1338    pub bounds: Bounds<ScaledPixels>,
1339}
1340
1341#[derive(Clone, Debug, Eq, PartialEq)]
1342#[repr(C)]
1343pub struct SurfaceBounds {
1344    pub bounds: Bounds<ScaledPixels>,
1345    pub content_mask: ContentMask<ScaledPixels>,
1346}