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        const 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 const 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        // We are uncertain when this happens, but sometimes size can be 0 here. Most likely before
 318        // the layout pass on window creation. Zero-sized texture creation causes SIGABRT.
 319        // https://github.com/zed-industries/zed/issues/36229
 320        if size.width.0 <= 0 || size.height.0 <= 0 {
 321            self.path_intermediate_texture = None;
 322            self.path_intermediate_msaa_texture = None;
 323            return;
 324        }
 325
 326        let texture_descriptor = metal::TextureDescriptor::new();
 327        texture_descriptor.set_width(size.width.0 as u64);
 328        texture_descriptor.set_height(size.height.0 as u64);
 329        texture_descriptor.set_pixel_format(metal::MTLPixelFormat::BGRA8Unorm);
 330        texture_descriptor
 331            .set_usage(metal::MTLTextureUsage::RenderTarget | metal::MTLTextureUsage::ShaderRead);
 332        self.path_intermediate_texture = Some(self.device.new_texture(&texture_descriptor));
 333
 334        if self.path_sample_count > 1 {
 335            let mut msaa_descriptor = texture_descriptor;
 336            msaa_descriptor.set_texture_type(metal::MTLTextureType::D2Multisample);
 337            msaa_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
 338            msaa_descriptor.set_sample_count(self.path_sample_count as _);
 339            self.path_intermediate_msaa_texture = Some(self.device.new_texture(&msaa_descriptor));
 340        } else {
 341            self.path_intermediate_msaa_texture = None;
 342        }
 343    }
 344
 345    pub const fn update_transparency(&self, _transparent: bool) {
 346        // todo(mac)?
 347    }
 348
 349    pub const fn destroy(&self) {
 350        // nothing to do
 351    }
 352
 353    pub fn draw(&mut self, scene: &Scene) {
 354        let layer = self.layer.clone();
 355        let viewport_size = layer.drawable_size();
 356        let viewport_size: Size<DevicePixels> = size(
 357            (viewport_size.width.ceil() as i32).into(),
 358            (viewport_size.height.ceil() as i32).into(),
 359        );
 360        let drawable = if let Some(drawable) = layer.next_drawable() {
 361            drawable
 362        } else {
 363            log::error!(
 364                "failed to retrieve next drawable, drawable size: {:?}",
 365                viewport_size
 366            );
 367            return;
 368        };
 369
 370        loop {
 371            let mut instance_buffer = self.instance_buffer_pool.lock().acquire(&self.device);
 372
 373            let command_buffer =
 374                self.draw_primitives(scene, &mut instance_buffer, drawable, viewport_size);
 375
 376            match command_buffer {
 377                Ok(command_buffer) => {
 378                    let instance_buffer_pool = self.instance_buffer_pool.clone();
 379                    let instance_buffer = Cell::new(Some(instance_buffer));
 380                    let block = ConcreteBlock::new(move |_| {
 381                        if let Some(instance_buffer) = instance_buffer.take() {
 382                            instance_buffer_pool.lock().release(instance_buffer);
 383                        }
 384                    });
 385                    let block = block.copy();
 386                    command_buffer.add_completed_handler(&block);
 387
 388                    if self.presents_with_transaction {
 389                        command_buffer.commit();
 390                        command_buffer.wait_until_scheduled();
 391                        drawable.present();
 392                    } else {
 393                        command_buffer.present_drawable(drawable);
 394                        command_buffer.commit();
 395                    }
 396                    return;
 397                }
 398                Err(err) => {
 399                    log::error!(
 400                        "failed to render: {}. retrying with larger instance buffer size",
 401                        err
 402                    );
 403                    let mut instance_buffer_pool = self.instance_buffer_pool.lock();
 404                    let buffer_size = instance_buffer_pool.buffer_size;
 405                    if buffer_size >= 256 * 1024 * 1024 {
 406                        log::error!("instance buffer size grew too large: {}", buffer_size);
 407                        break;
 408                    }
 409                    instance_buffer_pool.reset(buffer_size * 2);
 410                    log::info!(
 411                        "increased instance buffer size to {}",
 412                        instance_buffer_pool.buffer_size
 413                    );
 414                }
 415            }
 416        }
 417    }
 418
 419    fn draw_primitives(
 420        &mut self,
 421        scene: &Scene,
 422        instance_buffer: &mut InstanceBuffer,
 423        drawable: &metal::MetalDrawableRef,
 424        viewport_size: Size<DevicePixels>,
 425    ) -> Result<metal::CommandBuffer> {
 426        let command_queue = self.command_queue.clone();
 427        let command_buffer = command_queue.new_command_buffer();
 428        let alpha = if self.layer.is_opaque() { 1. } else { 0. };
 429        let mut instance_offset = 0;
 430
 431        let mut command_encoder = new_command_encoder(
 432            command_buffer,
 433            drawable,
 434            viewport_size,
 435            |color_attachment| {
 436                color_attachment.set_load_action(metal::MTLLoadAction::Clear);
 437                color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., alpha));
 438            },
 439        );
 440
 441        for batch in scene.batches() {
 442            let ok = match batch {
 443                PrimitiveBatch::Shadows(shadows) => self.draw_shadows(
 444                    shadows,
 445                    instance_buffer,
 446                    &mut instance_offset,
 447                    viewport_size,
 448                    command_encoder,
 449                ),
 450                PrimitiveBatch::Quads(quads) => self.draw_quads(
 451                    quads,
 452                    instance_buffer,
 453                    &mut instance_offset,
 454                    viewport_size,
 455                    command_encoder,
 456                ),
 457                PrimitiveBatch::Paths(paths) => {
 458                    command_encoder.end_encoding();
 459
 460                    let did_draw = self.draw_paths_to_intermediate(
 461                        paths,
 462                        instance_buffer,
 463                        &mut instance_offset,
 464                        viewport_size,
 465                        command_buffer,
 466                    );
 467
 468                    command_encoder = new_command_encoder(
 469                        command_buffer,
 470                        drawable,
 471                        viewport_size,
 472                        |color_attachment| {
 473                            color_attachment.set_load_action(metal::MTLLoadAction::Load);
 474                        },
 475                    );
 476
 477                    if did_draw {
 478                        self.draw_paths_from_intermediate(
 479                            paths,
 480                            instance_buffer,
 481                            &mut instance_offset,
 482                            viewport_size,
 483                            command_encoder,
 484                        )
 485                    } else {
 486                        false
 487                    }
 488                }
 489                PrimitiveBatch::Underlines(underlines) => self.draw_underlines(
 490                    underlines,
 491                    instance_buffer,
 492                    &mut instance_offset,
 493                    viewport_size,
 494                    command_encoder,
 495                ),
 496                PrimitiveBatch::MonochromeSprites {
 497                    texture_id,
 498                    sprites,
 499                } => self.draw_monochrome_sprites(
 500                    texture_id,
 501                    sprites,
 502                    instance_buffer,
 503                    &mut instance_offset,
 504                    viewport_size,
 505                    command_encoder,
 506                ),
 507                PrimitiveBatch::PolychromeSprites {
 508                    texture_id,
 509                    sprites,
 510                } => self.draw_polychrome_sprites(
 511                    texture_id,
 512                    sprites,
 513                    instance_buffer,
 514                    &mut instance_offset,
 515                    viewport_size,
 516                    command_encoder,
 517                ),
 518                PrimitiveBatch::Surfaces(surfaces) => self.draw_surfaces(
 519                    surfaces,
 520                    instance_buffer,
 521                    &mut instance_offset,
 522                    viewport_size,
 523                    command_encoder,
 524                ),
 525            };
 526            if !ok {
 527                command_encoder.end_encoding();
 528                anyhow::bail!(
 529                    "scene too large: {} paths, {} shadows, {} quads, {} underlines, {} mono, {} poly, {} surfaces",
 530                    scene.paths.len(),
 531                    scene.shadows.len(),
 532                    scene.quads.len(),
 533                    scene.underlines.len(),
 534                    scene.monochrome_sprites.len(),
 535                    scene.polychrome_sprites.len(),
 536                    scene.surfaces.len(),
 537                );
 538            }
 539        }
 540
 541        command_encoder.end_encoding();
 542
 543        instance_buffer.metal_buffer.did_modify_range(NSRange {
 544            location: 0,
 545            length: instance_offset as NSUInteger,
 546        });
 547        Ok(command_buffer.to_owned())
 548    }
 549
 550    fn draw_paths_to_intermediate(
 551        &self,
 552        paths: &[Path<ScaledPixels>],
 553        instance_buffer: &mut InstanceBuffer,
 554        instance_offset: &mut usize,
 555        viewport_size: Size<DevicePixels>,
 556        command_buffer: &metal::CommandBufferRef,
 557    ) -> bool {
 558        if paths.is_empty() {
 559            return true;
 560        }
 561        let Some(intermediate_texture) = &self.path_intermediate_texture else {
 562            return false;
 563        };
 564
 565        let render_pass_descriptor = metal::RenderPassDescriptor::new();
 566        let color_attachment = render_pass_descriptor
 567            .color_attachments()
 568            .object_at(0)
 569            .unwrap();
 570        color_attachment.set_load_action(metal::MTLLoadAction::Clear);
 571        color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 0.));
 572
 573        if let Some(msaa_texture) = &self.path_intermediate_msaa_texture {
 574            color_attachment.set_texture(Some(msaa_texture));
 575            color_attachment.set_resolve_texture(Some(intermediate_texture));
 576            color_attachment.set_store_action(metal::MTLStoreAction::MultisampleResolve);
 577        } else {
 578            color_attachment.set_texture(Some(intermediate_texture));
 579            color_attachment.set_store_action(metal::MTLStoreAction::Store);
 580        }
 581
 582        let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
 583        command_encoder.set_render_pipeline_state(&self.paths_rasterization_pipeline_state);
 584
 585        align_offset(instance_offset);
 586        let mut vertices = Vec::new();
 587        for path in paths {
 588            vertices.extend(path.vertices.iter().map(|v| PathRasterizationVertex {
 589                xy_position: v.xy_position,
 590                st_position: v.st_position,
 591                color: path.color,
 592                bounds: path.bounds.intersect(&path.content_mask.bounds),
 593            }));
 594        }
 595        let vertices_bytes_len = mem::size_of_val(vertices.as_slice());
 596        let next_offset = *instance_offset + vertices_bytes_len;
 597        if next_offset > instance_buffer.size {
 598            command_encoder.end_encoding();
 599            return false;
 600        }
 601        command_encoder.set_vertex_buffer(
 602            PathRasterizationInputIndex::Vertices as u64,
 603            Some(&instance_buffer.metal_buffer),
 604            *instance_offset as u64,
 605        );
 606        command_encoder.set_vertex_bytes(
 607            PathRasterizationInputIndex::ViewportSize as u64,
 608            mem::size_of_val(&viewport_size) as u64,
 609            &viewport_size as *const Size<DevicePixels> as *const _,
 610        );
 611        command_encoder.set_fragment_buffer(
 612            PathRasterizationInputIndex::Vertices as u64,
 613            Some(&instance_buffer.metal_buffer),
 614            *instance_offset as u64,
 615        );
 616        let buffer_contents =
 617            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 618        unsafe {
 619            ptr::copy_nonoverlapping(
 620                vertices.as_ptr() as *const u8,
 621                buffer_contents,
 622                vertices_bytes_len,
 623            );
 624        }
 625        command_encoder.draw_primitives(
 626            metal::MTLPrimitiveType::Triangle,
 627            0,
 628            vertices.len() as u64,
 629        );
 630        *instance_offset = next_offset;
 631
 632        command_encoder.end_encoding();
 633        true
 634    }
 635
 636    fn draw_shadows(
 637        &self,
 638        shadows: &[Shadow],
 639        instance_buffer: &mut InstanceBuffer,
 640        instance_offset: &mut usize,
 641        viewport_size: Size<DevicePixels>,
 642        command_encoder: &metal::RenderCommandEncoderRef,
 643    ) -> bool {
 644        if shadows.is_empty() {
 645            return true;
 646        }
 647        align_offset(instance_offset);
 648
 649        command_encoder.set_render_pipeline_state(&self.shadows_pipeline_state);
 650        command_encoder.set_vertex_buffer(
 651            ShadowInputIndex::Vertices as u64,
 652            Some(&self.unit_vertices),
 653            0,
 654        );
 655        command_encoder.set_vertex_buffer(
 656            ShadowInputIndex::Shadows as u64,
 657            Some(&instance_buffer.metal_buffer),
 658            *instance_offset as u64,
 659        );
 660        command_encoder.set_fragment_buffer(
 661            ShadowInputIndex::Shadows as u64,
 662            Some(&instance_buffer.metal_buffer),
 663            *instance_offset as u64,
 664        );
 665
 666        command_encoder.set_vertex_bytes(
 667            ShadowInputIndex::ViewportSize as u64,
 668            mem::size_of_val(&viewport_size) as u64,
 669            &viewport_size as *const Size<DevicePixels> as *const _,
 670        );
 671
 672        let shadow_bytes_len = mem::size_of_val(shadows);
 673        let buffer_contents =
 674            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 675
 676        let next_offset = *instance_offset + shadow_bytes_len;
 677        if next_offset > instance_buffer.size {
 678            return false;
 679        }
 680
 681        unsafe {
 682            ptr::copy_nonoverlapping(
 683                shadows.as_ptr() as *const u8,
 684                buffer_contents,
 685                shadow_bytes_len,
 686            );
 687        }
 688
 689        command_encoder.draw_primitives_instanced(
 690            metal::MTLPrimitiveType::Triangle,
 691            0,
 692            6,
 693            shadows.len() as u64,
 694        );
 695        *instance_offset = next_offset;
 696        true
 697    }
 698
 699    fn draw_quads(
 700        &self,
 701        quads: &[Quad],
 702        instance_buffer: &mut InstanceBuffer,
 703        instance_offset: &mut usize,
 704        viewport_size: Size<DevicePixels>,
 705        command_encoder: &metal::RenderCommandEncoderRef,
 706    ) -> bool {
 707        if quads.is_empty() {
 708            return true;
 709        }
 710        align_offset(instance_offset);
 711
 712        command_encoder.set_render_pipeline_state(&self.quads_pipeline_state);
 713        command_encoder.set_vertex_buffer(
 714            QuadInputIndex::Vertices as u64,
 715            Some(&self.unit_vertices),
 716            0,
 717        );
 718        command_encoder.set_vertex_buffer(
 719            QuadInputIndex::Quads as u64,
 720            Some(&instance_buffer.metal_buffer),
 721            *instance_offset as u64,
 722        );
 723        command_encoder.set_fragment_buffer(
 724            QuadInputIndex::Quads as u64,
 725            Some(&instance_buffer.metal_buffer),
 726            *instance_offset as u64,
 727        );
 728
 729        command_encoder.set_vertex_bytes(
 730            QuadInputIndex::ViewportSize as u64,
 731            mem::size_of_val(&viewport_size) as u64,
 732            &viewport_size as *const Size<DevicePixels> as *const _,
 733        );
 734
 735        let quad_bytes_len = mem::size_of_val(quads);
 736        let buffer_contents =
 737            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 738
 739        let next_offset = *instance_offset + quad_bytes_len;
 740        if next_offset > instance_buffer.size {
 741            return false;
 742        }
 743
 744        unsafe {
 745            ptr::copy_nonoverlapping(quads.as_ptr() as *const u8, buffer_contents, quad_bytes_len);
 746        }
 747
 748        command_encoder.draw_primitives_instanced(
 749            metal::MTLPrimitiveType::Triangle,
 750            0,
 751            6,
 752            quads.len() as u64,
 753        );
 754        *instance_offset = next_offset;
 755        true
 756    }
 757
 758    fn draw_paths_from_intermediate(
 759        &self,
 760        paths: &[Path<ScaledPixels>],
 761        instance_buffer: &mut InstanceBuffer,
 762        instance_offset: &mut usize,
 763        viewport_size: Size<DevicePixels>,
 764        command_encoder: &metal::RenderCommandEncoderRef,
 765    ) -> bool {
 766        let Some(first_path) = paths.first() else {
 767            return true;
 768        };
 769
 770        let Some(ref intermediate_texture) = self.path_intermediate_texture else {
 771            return false;
 772        };
 773
 774        command_encoder.set_render_pipeline_state(&self.path_sprites_pipeline_state);
 775        command_encoder.set_vertex_buffer(
 776            SpriteInputIndex::Vertices as u64,
 777            Some(&self.unit_vertices),
 778            0,
 779        );
 780        command_encoder.set_vertex_bytes(
 781            SpriteInputIndex::ViewportSize as u64,
 782            mem::size_of_val(&viewport_size) as u64,
 783            &viewport_size as *const Size<DevicePixels> as *const _,
 784        );
 785
 786        command_encoder.set_fragment_texture(
 787            SpriteInputIndex::AtlasTexture as u64,
 788            Some(intermediate_texture),
 789        );
 790
 791        // When copying paths from the intermediate texture to the drawable,
 792        // each pixel must only be copied once, in case of transparent paths.
 793        //
 794        // If all paths have the same draw order, then their bounds are all
 795        // disjoint, so we can copy each path's bounds individually. If this
 796        // batch combines different draw orders, we perform a single copy
 797        // for a minimal spanning rect.
 798        let sprites;
 799        if paths.last().unwrap().order == first_path.order {
 800            sprites = paths
 801                .iter()
 802                .map(|path| PathSprite {
 803                    bounds: path.clipped_bounds(),
 804                })
 805                .collect();
 806        } else {
 807            let mut bounds = first_path.clipped_bounds();
 808            for path in paths.iter().skip(1) {
 809                bounds = bounds.union(&path.clipped_bounds());
 810            }
 811            sprites = vec![PathSprite { bounds }];
 812        }
 813
 814        align_offset(instance_offset);
 815        let sprite_bytes_len = mem::size_of_val(sprites.as_slice());
 816        let next_offset = *instance_offset + sprite_bytes_len;
 817        if next_offset > instance_buffer.size {
 818            return false;
 819        }
 820
 821        command_encoder.set_vertex_buffer(
 822            SpriteInputIndex::Sprites as u64,
 823            Some(&instance_buffer.metal_buffer),
 824            *instance_offset as u64,
 825        );
 826
 827        let buffer_contents =
 828            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 829        unsafe {
 830            ptr::copy_nonoverlapping(
 831                sprites.as_ptr() as *const u8,
 832                buffer_contents,
 833                sprite_bytes_len,
 834            );
 835        }
 836
 837        command_encoder.draw_primitives_instanced(
 838            metal::MTLPrimitiveType::Triangle,
 839            0,
 840            6,
 841            sprites.len() as u64,
 842        );
 843        *instance_offset = next_offset;
 844
 845        true
 846    }
 847
 848    fn draw_underlines(
 849        &self,
 850        underlines: &[Underline],
 851        instance_buffer: &mut InstanceBuffer,
 852        instance_offset: &mut usize,
 853        viewport_size: Size<DevicePixels>,
 854        command_encoder: &metal::RenderCommandEncoderRef,
 855    ) -> bool {
 856        if underlines.is_empty() {
 857            return true;
 858        }
 859        align_offset(instance_offset);
 860
 861        command_encoder.set_render_pipeline_state(&self.underlines_pipeline_state);
 862        command_encoder.set_vertex_buffer(
 863            UnderlineInputIndex::Vertices as u64,
 864            Some(&self.unit_vertices),
 865            0,
 866        );
 867        command_encoder.set_vertex_buffer(
 868            UnderlineInputIndex::Underlines as u64,
 869            Some(&instance_buffer.metal_buffer),
 870            *instance_offset as u64,
 871        );
 872        command_encoder.set_fragment_buffer(
 873            UnderlineInputIndex::Underlines as u64,
 874            Some(&instance_buffer.metal_buffer),
 875            *instance_offset as u64,
 876        );
 877
 878        command_encoder.set_vertex_bytes(
 879            UnderlineInputIndex::ViewportSize as u64,
 880            mem::size_of_val(&viewport_size) as u64,
 881            &viewport_size as *const Size<DevicePixels> as *const _,
 882        );
 883
 884        let underline_bytes_len = mem::size_of_val(underlines);
 885        let buffer_contents =
 886            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 887
 888        let next_offset = *instance_offset + underline_bytes_len;
 889        if next_offset > instance_buffer.size {
 890            return false;
 891        }
 892
 893        unsafe {
 894            ptr::copy_nonoverlapping(
 895                underlines.as_ptr() as *const u8,
 896                buffer_contents,
 897                underline_bytes_len,
 898            );
 899        }
 900
 901        command_encoder.draw_primitives_instanced(
 902            metal::MTLPrimitiveType::Triangle,
 903            0,
 904            6,
 905            underlines.len() as u64,
 906        );
 907        *instance_offset = next_offset;
 908        true
 909    }
 910
 911    fn draw_monochrome_sprites(
 912        &self,
 913        texture_id: AtlasTextureId,
 914        sprites: &[MonochromeSprite],
 915        instance_buffer: &mut InstanceBuffer,
 916        instance_offset: &mut usize,
 917        viewport_size: Size<DevicePixels>,
 918        command_encoder: &metal::RenderCommandEncoderRef,
 919    ) -> bool {
 920        if sprites.is_empty() {
 921            return true;
 922        }
 923        align_offset(instance_offset);
 924
 925        let sprite_bytes_len = mem::size_of_val(sprites);
 926        let buffer_contents =
 927            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
 928
 929        let next_offset = *instance_offset + sprite_bytes_len;
 930        if next_offset > instance_buffer.size {
 931            return false;
 932        }
 933
 934        let texture = self.sprite_atlas.metal_texture(texture_id);
 935        let texture_size = size(
 936            DevicePixels(texture.width() as i32),
 937            DevicePixels(texture.height() as i32),
 938        );
 939        command_encoder.set_render_pipeline_state(&self.monochrome_sprites_pipeline_state);
 940        command_encoder.set_vertex_buffer(
 941            SpriteInputIndex::Vertices as u64,
 942            Some(&self.unit_vertices),
 943            0,
 944        );
 945        command_encoder.set_vertex_buffer(
 946            SpriteInputIndex::Sprites as u64,
 947            Some(&instance_buffer.metal_buffer),
 948            *instance_offset as u64,
 949        );
 950        command_encoder.set_vertex_bytes(
 951            SpriteInputIndex::ViewportSize as u64,
 952            mem::size_of_val(&viewport_size) as u64,
 953            &viewport_size as *const Size<DevicePixels> as *const _,
 954        );
 955        command_encoder.set_vertex_bytes(
 956            SpriteInputIndex::AtlasTextureSize as u64,
 957            mem::size_of_val(&texture_size) as u64,
 958            &texture_size as *const Size<DevicePixels> as *const _,
 959        );
 960        command_encoder.set_fragment_buffer(
 961            SpriteInputIndex::Sprites as u64,
 962            Some(&instance_buffer.metal_buffer),
 963            *instance_offset as u64,
 964        );
 965        command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
 966
 967        unsafe {
 968            ptr::copy_nonoverlapping(
 969                sprites.as_ptr() as *const u8,
 970                buffer_contents,
 971                sprite_bytes_len,
 972            );
 973        }
 974
 975        command_encoder.draw_primitives_instanced(
 976            metal::MTLPrimitiveType::Triangle,
 977            0,
 978            6,
 979            sprites.len() as u64,
 980        );
 981        *instance_offset = next_offset;
 982        true
 983    }
 984
 985    fn draw_polychrome_sprites(
 986        &self,
 987        texture_id: AtlasTextureId,
 988        sprites: &[PolychromeSprite],
 989        instance_buffer: &mut InstanceBuffer,
 990        instance_offset: &mut usize,
 991        viewport_size: Size<DevicePixels>,
 992        command_encoder: &metal::RenderCommandEncoderRef,
 993    ) -> bool {
 994        if sprites.is_empty() {
 995            return true;
 996        }
 997        align_offset(instance_offset);
 998
 999        let texture = self.sprite_atlas.metal_texture(texture_id);
1000        let texture_size = size(
1001            DevicePixels(texture.width() as i32),
1002            DevicePixels(texture.height() as i32),
1003        );
1004        command_encoder.set_render_pipeline_state(&self.polychrome_sprites_pipeline_state);
1005        command_encoder.set_vertex_buffer(
1006            SpriteInputIndex::Vertices as u64,
1007            Some(&self.unit_vertices),
1008            0,
1009        );
1010        command_encoder.set_vertex_buffer(
1011            SpriteInputIndex::Sprites as u64,
1012            Some(&instance_buffer.metal_buffer),
1013            *instance_offset as u64,
1014        );
1015        command_encoder.set_vertex_bytes(
1016            SpriteInputIndex::ViewportSize as u64,
1017            mem::size_of_val(&viewport_size) as u64,
1018            &viewport_size as *const Size<DevicePixels> as *const _,
1019        );
1020        command_encoder.set_vertex_bytes(
1021            SpriteInputIndex::AtlasTextureSize as u64,
1022            mem::size_of_val(&texture_size) as u64,
1023            &texture_size as *const Size<DevicePixels> as *const _,
1024        );
1025        command_encoder.set_fragment_buffer(
1026            SpriteInputIndex::Sprites as u64,
1027            Some(&instance_buffer.metal_buffer),
1028            *instance_offset as u64,
1029        );
1030        command_encoder.set_fragment_texture(SpriteInputIndex::AtlasTexture as u64, Some(&texture));
1031
1032        let sprite_bytes_len = mem::size_of_val(sprites);
1033        let buffer_contents =
1034            unsafe { (instance_buffer.metal_buffer.contents() as *mut u8).add(*instance_offset) };
1035
1036        let next_offset = *instance_offset + sprite_bytes_len;
1037        if next_offset > instance_buffer.size {
1038            return false;
1039        }
1040
1041        unsafe {
1042            ptr::copy_nonoverlapping(
1043                sprites.as_ptr() as *const u8,
1044                buffer_contents,
1045                sprite_bytes_len,
1046            );
1047        }
1048
1049        command_encoder.draw_primitives_instanced(
1050            metal::MTLPrimitiveType::Triangle,
1051            0,
1052            6,
1053            sprites.len() as u64,
1054        );
1055        *instance_offset = next_offset;
1056        true
1057    }
1058
1059    fn draw_surfaces(
1060        &mut self,
1061        surfaces: &[PaintSurface],
1062        instance_buffer: &mut InstanceBuffer,
1063        instance_offset: &mut usize,
1064        viewport_size: Size<DevicePixels>,
1065        command_encoder: &metal::RenderCommandEncoderRef,
1066    ) -> bool {
1067        command_encoder.set_render_pipeline_state(&self.surfaces_pipeline_state);
1068        command_encoder.set_vertex_buffer(
1069            SurfaceInputIndex::Vertices as u64,
1070            Some(&self.unit_vertices),
1071            0,
1072        );
1073        command_encoder.set_vertex_bytes(
1074            SurfaceInputIndex::ViewportSize as u64,
1075            mem::size_of_val(&viewport_size) as u64,
1076            &viewport_size as *const Size<DevicePixels> as *const _,
1077        );
1078
1079        for surface in surfaces {
1080            let texture_size = size(
1081                DevicePixels::from(surface.image_buffer.get_width() as i32),
1082                DevicePixels::from(surface.image_buffer.get_height() as i32),
1083            );
1084
1085            assert_eq!(
1086                surface.image_buffer.get_pixel_format(),
1087                kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
1088            );
1089
1090            let y_texture = self
1091                .core_video_texture_cache
1092                .create_texture_from_image(
1093                    surface.image_buffer.as_concrete_TypeRef(),
1094                    None,
1095                    MTLPixelFormat::R8Unorm,
1096                    surface.image_buffer.get_width_of_plane(0),
1097                    surface.image_buffer.get_height_of_plane(0),
1098                    0,
1099                )
1100                .unwrap();
1101            let cb_cr_texture = self
1102                .core_video_texture_cache
1103                .create_texture_from_image(
1104                    surface.image_buffer.as_concrete_TypeRef(),
1105                    None,
1106                    MTLPixelFormat::RG8Unorm,
1107                    surface.image_buffer.get_width_of_plane(1),
1108                    surface.image_buffer.get_height_of_plane(1),
1109                    1,
1110                )
1111                .unwrap();
1112
1113            align_offset(instance_offset);
1114            let next_offset = *instance_offset + mem::size_of::<Surface>();
1115            if next_offset > instance_buffer.size {
1116                return false;
1117            }
1118
1119            command_encoder.set_vertex_buffer(
1120                SurfaceInputIndex::Surfaces as u64,
1121                Some(&instance_buffer.metal_buffer),
1122                *instance_offset as u64,
1123            );
1124            command_encoder.set_vertex_bytes(
1125                SurfaceInputIndex::TextureSize as u64,
1126                mem::size_of_val(&texture_size) as u64,
1127                &texture_size as *const Size<DevicePixels> as *const _,
1128            );
1129            // let y_texture = y_texture.get_texture().unwrap().
1130            command_encoder.set_fragment_texture(SurfaceInputIndex::YTexture as u64, unsafe {
1131                let texture = CVMetalTextureGetTexture(y_texture.as_concrete_TypeRef());
1132                Some(metal::TextureRef::from_ptr(texture as *mut _))
1133            });
1134            command_encoder.set_fragment_texture(SurfaceInputIndex::CbCrTexture as u64, unsafe {
1135                let texture = CVMetalTextureGetTexture(cb_cr_texture.as_concrete_TypeRef());
1136                Some(metal::TextureRef::from_ptr(texture as *mut _))
1137            });
1138
1139            unsafe {
1140                let buffer_contents = (instance_buffer.metal_buffer.contents() as *mut u8)
1141                    .add(*instance_offset)
1142                    as *mut SurfaceBounds;
1143                ptr::write(
1144                    buffer_contents,
1145                    SurfaceBounds {
1146                        bounds: surface.bounds,
1147                        content_mask: surface.content_mask.clone(),
1148                    },
1149                );
1150            }
1151
1152            command_encoder.draw_primitives(metal::MTLPrimitiveType::Triangle, 0, 6);
1153            *instance_offset = next_offset;
1154        }
1155        true
1156    }
1157}
1158
1159fn new_command_encoder<'a>(
1160    command_buffer: &'a metal::CommandBufferRef,
1161    drawable: &'a metal::MetalDrawableRef,
1162    viewport_size: Size<DevicePixels>,
1163    configure_color_attachment: impl Fn(&RenderPassColorAttachmentDescriptorRef),
1164) -> &'a metal::RenderCommandEncoderRef {
1165    let render_pass_descriptor = metal::RenderPassDescriptor::new();
1166    let color_attachment = render_pass_descriptor
1167        .color_attachments()
1168        .object_at(0)
1169        .unwrap();
1170    color_attachment.set_texture(Some(drawable.texture()));
1171    color_attachment.set_store_action(metal::MTLStoreAction::Store);
1172    configure_color_attachment(color_attachment);
1173
1174    let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
1175    command_encoder.set_viewport(metal::MTLViewport {
1176        originX: 0.0,
1177        originY: 0.0,
1178        width: i32::from(viewport_size.width) as f64,
1179        height: i32::from(viewport_size.height) as f64,
1180        znear: 0.0,
1181        zfar: 1.0,
1182    });
1183    command_encoder
1184}
1185
1186fn build_pipeline_state(
1187    device: &metal::DeviceRef,
1188    library: &metal::LibraryRef,
1189    label: &str,
1190    vertex_fn_name: &str,
1191    fragment_fn_name: &str,
1192    pixel_format: metal::MTLPixelFormat,
1193) -> metal::RenderPipelineState {
1194    let vertex_fn = library
1195        .get_function(vertex_fn_name, None)
1196        .expect("error locating vertex function");
1197    let fragment_fn = library
1198        .get_function(fragment_fn_name, None)
1199        .expect("error locating fragment function");
1200
1201    let descriptor = metal::RenderPipelineDescriptor::new();
1202    descriptor.set_label(label);
1203    descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
1204    descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
1205    let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
1206    color_attachment.set_pixel_format(pixel_format);
1207    color_attachment.set_blending_enabled(true);
1208    color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
1209    color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
1210    color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::SourceAlpha);
1211    color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
1212    color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
1213    color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
1214
1215    device
1216        .new_render_pipeline_state(&descriptor)
1217        .expect("could not create render pipeline state")
1218}
1219
1220fn build_path_sprite_pipeline_state(
1221    device: &metal::DeviceRef,
1222    library: &metal::LibraryRef,
1223    label: &str,
1224    vertex_fn_name: &str,
1225    fragment_fn_name: &str,
1226    pixel_format: metal::MTLPixelFormat,
1227) -> metal::RenderPipelineState {
1228    let vertex_fn = library
1229        .get_function(vertex_fn_name, None)
1230        .expect("error locating vertex function");
1231    let fragment_fn = library
1232        .get_function(fragment_fn_name, None)
1233        .expect("error locating fragment function");
1234
1235    let descriptor = metal::RenderPipelineDescriptor::new();
1236    descriptor.set_label(label);
1237    descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
1238    descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
1239    let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
1240    color_attachment.set_pixel_format(pixel_format);
1241    color_attachment.set_blending_enabled(true);
1242    color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
1243    color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
1244    color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
1245    color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
1246    color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
1247    color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
1248
1249    device
1250        .new_render_pipeline_state(&descriptor)
1251        .expect("could not create render pipeline state")
1252}
1253
1254fn build_path_rasterization_pipeline_state(
1255    device: &metal::DeviceRef,
1256    library: &metal::LibraryRef,
1257    label: &str,
1258    vertex_fn_name: &str,
1259    fragment_fn_name: &str,
1260    pixel_format: metal::MTLPixelFormat,
1261    path_sample_count: u32,
1262) -> metal::RenderPipelineState {
1263    let vertex_fn = library
1264        .get_function(vertex_fn_name, None)
1265        .expect("error locating vertex function");
1266    let fragment_fn = library
1267        .get_function(fragment_fn_name, None)
1268        .expect("error locating fragment function");
1269
1270    let descriptor = metal::RenderPipelineDescriptor::new();
1271    descriptor.set_label(label);
1272    descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
1273    descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
1274    if path_sample_count > 1 {
1275        descriptor.set_raster_sample_count(path_sample_count as _);
1276        descriptor.set_alpha_to_coverage_enabled(false);
1277    }
1278    let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
1279    color_attachment.set_pixel_format(pixel_format);
1280    color_attachment.set_blending_enabled(true);
1281    color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
1282    color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
1283    color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::One);
1284    color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
1285    color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
1286    color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
1287
1288    device
1289        .new_render_pipeline_state(&descriptor)
1290        .expect("could not create render pipeline state")
1291}
1292
1293// Align to multiples of 256 make Metal happy.
1294const fn align_offset(offset: &mut usize) {
1295    *offset = (*offset).div_ceil(256) * 256;
1296}
1297
1298#[repr(C)]
1299enum ShadowInputIndex {
1300    Vertices = 0,
1301    Shadows = 1,
1302    ViewportSize = 2,
1303}
1304
1305#[repr(C)]
1306enum QuadInputIndex {
1307    Vertices = 0,
1308    Quads = 1,
1309    ViewportSize = 2,
1310}
1311
1312#[repr(C)]
1313enum UnderlineInputIndex {
1314    Vertices = 0,
1315    Underlines = 1,
1316    ViewportSize = 2,
1317}
1318
1319#[repr(C)]
1320enum SpriteInputIndex {
1321    Vertices = 0,
1322    Sprites = 1,
1323    ViewportSize = 2,
1324    AtlasTextureSize = 3,
1325    AtlasTexture = 4,
1326}
1327
1328#[repr(C)]
1329enum SurfaceInputIndex {
1330    Vertices = 0,
1331    Surfaces = 1,
1332    ViewportSize = 2,
1333    TextureSize = 3,
1334    YTexture = 4,
1335    CbCrTexture = 5,
1336}
1337
1338#[repr(C)]
1339enum PathRasterizationInputIndex {
1340    Vertices = 0,
1341    ViewportSize = 1,
1342}
1343
1344#[derive(Clone, Debug, Eq, PartialEq)]
1345#[repr(C)]
1346pub struct PathSprite {
1347    pub bounds: Bounds<ScaledPixels>,
1348}
1349
1350#[derive(Clone, Debug, Eq, PartialEq)]
1351#[repr(C)]
1352pub struct SurfaceBounds {
1353    pub bounds: Bounds<ScaledPixels>,
1354    pub content_mask: ContentMask<ScaledPixels>,
1355}