vello_shaders 0.8.0

Vello infrastructure to preprocess and cross-compile shaders at compile time.
Documentation
// Copyright 2023 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Support for CPU implementations of compute shaders.
//!
//! Note that while this CPU implementation is useful for testing and debugging,
//! a full CPU fallback as an alternative to GPU shaders is not provided.

// Allow un-idiomatic Rust to more closely match shaders
#![expect(
    clippy::needless_range_loop,
    reason = "Keeps code easily comparable to GPU shaders"
)]

mod backdrop;
mod bbox_clear;
mod binning;
mod clip_leaf;
mod clip_reduce;
mod coarse;
mod draw_leaf;
mod draw_reduce;
mod euler;
mod fine;
mod flatten;
mod path_count;
mod path_count_setup;
mod path_tiling;
mod path_tiling_setup;
mod pathtag_reduce;
mod pathtag_scan;
mod tile_alloc;
mod util;

pub use backdrop::backdrop;
pub use bbox_clear::bbox_clear;
pub use binning::binning;
pub use clip_leaf::clip_leaf;
pub use clip_reduce::clip_reduce;
pub use coarse::coarse;
pub use draw_leaf::draw_leaf;
pub use draw_reduce::draw_reduce;
pub use flatten::flatten;
pub use path_count::path_count;
pub use path_count_setup::path_count_setup;
pub use path_tiling::path_tiling;
pub use path_tiling_setup::path_tiling_setup;
pub use pathtag_reduce::pathtag_reduce;
pub use pathtag_scan::pathtag_scan;
pub use tile_alloc::tile_alloc;

use std::cell::{Ref, RefCell, RefMut};
use std::ops::{Deref, DerefMut};

use bytemuck::Pod;

#[derive(Clone, Copy)]
pub enum CpuBinding<'a> {
    Buffer(&'a [u8]),
    BufferRW(&'a RefCell<Vec<u8>>),
    Texture(&'a CpuTexture),
}

pub enum TypedBufGuard<'a, T: ?Sized> {
    Slice(&'a T),
    Interior(Ref<'a, T>),
}

pub enum TypedBufGuardMut<'a, T: ?Sized> {
    Slice(&'a mut T),
    Interior(RefMut<'a, T>),
}

impl<T: ?Sized> Deref for TypedBufGuard<'_, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        match self {
            TypedBufGuard::Slice(s) => s,
            TypedBufGuard::Interior(r) => r,
        }
    }
}

impl<T: ?Sized> Deref for TypedBufGuardMut<'_, T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        match self {
            TypedBufGuardMut::Slice(s) => s,
            TypedBufGuardMut::Interior(r) => r,
        }
    }
}

impl<T: ?Sized> DerefMut for TypedBufGuardMut<'_, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            TypedBufGuardMut::Slice(s) => s,
            TypedBufGuardMut::Interior(r) => r,
        }
    }
}

impl CpuBinding<'_> {
    pub fn as_typed<T: Pod>(&self) -> TypedBufGuard<'_, T> {
        match self {
            CpuBinding::Buffer(b) => TypedBufGuard::Slice(bytemuck::from_bytes(b)),
            CpuBinding::BufferRW(b) => {
                TypedBufGuard::Interior(Ref::map(b.borrow(), |buf| bytemuck::from_bytes(buf)))
            }
            _ => panic!("resource type mismatch"),
        }
    }

    pub fn as_typed_mut<T: Pod>(&self) -> TypedBufGuardMut<'_, T> {
        match self {
            CpuBinding::Buffer(_) => panic!("can't borrow external buffer mutably"),
            CpuBinding::BufferRW(b) => {
                TypedBufGuardMut::Interior(RefMut::map(b.borrow_mut(), |buf| {
                    bytemuck::from_bytes_mut(buf)
                }))
            }
            _ => panic!("resource type mismatch"),
        }
    }

    pub fn as_slice<T: Pod>(&self) -> TypedBufGuard<'_, [T]> {
        match self {
            CpuBinding::Buffer(b) => TypedBufGuard::Slice(bytemuck::cast_slice(b)),
            CpuBinding::BufferRW(b) => {
                TypedBufGuard::Interior(Ref::map(b.borrow(), |buf| bytemuck::cast_slice(buf)))
            }
            _ => panic!("resource type mismatch"),
        }
    }

    pub fn as_slice_mut<T: Pod>(&self) -> TypedBufGuardMut<'_, [T]> {
        match self {
            CpuBinding::Buffer(_) => panic!("can't borrow external buffer mutably"),
            CpuBinding::BufferRW(b) => {
                TypedBufGuardMut::Interior(RefMut::map(b.borrow_mut(), |buf| {
                    bytemuck::cast_slice_mut(buf)
                }))
            }
            _ => panic!("resource type mismatch"),
        }
    }

    // TODO: same guard as buf to make mutable
    pub fn as_tex(&self) -> &CpuTexture {
        match self {
            CpuBinding::Texture(t) => t,
            _ => panic!("resource type mismatch"),
        }
    }
}

/// Structure used for binding textures to CPU shaders.
pub struct CpuTexture {
    pub width: usize,
    pub height: usize,
    // In RGBA format. May expand in the future.
    pub pixels: Vec<u32>,
}

// Common internal definitions

const PTCL_INITIAL_ALLOC: u32 = 64;

// Tags for PTCL commands
const CMD_END: u32 = 0;
const CMD_FILL: u32 = 1;
//const CMD_STROKE: u32 = 2;
const CMD_SOLID: u32 = 3;
const CMD_COLOR: u32 = 5;
const CMD_LIN_GRAD: u32 = 6;
const CMD_RAD_GRAD: u32 = 7;
const CMD_SWEEP_GRAD: u32 = 8;
const CMD_IMAGE: u32 = 9;
const CMD_BEGIN_CLIP: u32 = 10;
const CMD_END_CLIP: u32 = 11;
const CMD_JUMP: u32 = 12;
const CMD_BLUR_RECT: u32 = 13;

// The following are computed in draw_leaf from the generic gradient parameters
// encoded in the scene, and stored in the gradient's info struct, for
// consumption during fine rasterization.

// Radial gradient kinds
const RAD_GRAD_KIND_CIRCULAR: u32 = 1;
const RAD_GRAD_KIND_STRIP: u32 = 2;
const RAD_GRAD_KIND_FOCAL_ON_CIRCLE: u32 = 3;
const RAD_GRAD_KIND_CONE: u32 = 4;

// Radial gradient flags
const RAD_GRAD_SWAPPED: u32 = 1;