use bytemuck::{Pod, Zeroable};
use core::ops::Range;
use frust_gpu::VertexLayout;
use vello_common::strip::Strip;
use vello_common::tile::Tile;
const _: () = assert!(
Tile::HEIGHT == 4,
"the strip shaders require `Tile::HEIGHT` to be 4",
);
pub const RECT_STRIP_FLAG: u32 = 1 << 31;
const COLOR_SOURCE_SHIFT: u32 = 29;
const PAINT_TYPE_SHIFT: u32 = 26;
pub const PAINT_TEXTURE_INDEX_MASK: u32 = (1 << PAINT_TYPE_SHIFT) - 1;
const COLOR_SOURCE_PAYLOAD: u32 = 0;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(u32)]
pub enum PaintType {
Solid = 0,
Image = 1,
LinearGradient = 2,
RadialGradient = 3,
SweepGradient = 4,
BlurredRoundedRect = 5,
}
#[must_use]
pub const fn pack_paint_descriptor(paint_type: PaintType, texel_index: u32) -> u32 {
(COLOR_SOURCE_PAYLOAD << COLOR_SOURCE_SHIFT)
| ((paint_type as u32) << PAINT_TYPE_SHIFT)
| (texel_index & PAINT_TEXTURE_INDEX_MASK)
}
#[must_use]
pub const fn alpha_column(alpha_idx: u32) -> u32 {
alpha_idx / Tile::HEIGHT as u32
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Pod, Zeroable)]
pub struct GpuStrip {
pub x: u16,
pub y: u16,
pub width: u16,
pub dense_width_or_rect_height: u16,
pub col_idx_or_rect_frac: u32,
pub payload: u32,
pub paint_and_rect_flag: u32,
pub depth_index: u32,
}
const _: () = assert!(
size_of::<GpuStrip>() == 24,
"`GpuStrip` must stay 24 bytes — the shaders' `StripInstance` layout",
);
const _: () = assert!(
size_of::<GpuStrip>() == size_of::<u16>() * 4 + size_of::<u32>() * 4,
"`GpuStrip` must stay padding-free — six `Uint32` vertex attributes read it",
);
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StripDraw {
pub payload: u32,
pub paint: u32,
pub depth_index: u32,
}
impl GpuStrip {
pub const QUAD_VERTICES: u32 = 4;
#[must_use]
pub fn vertex_attributes() -> [wgpu::VertexAttribute; 6] {
wgpu::vertex_attr_array![
0 => Uint32,
1 => Uint32,
2 => Uint32,
3 => Uint32,
4 => Uint32,
5 => Uint32,
]
}
#[must_use]
pub fn vertex_layout() -> VertexLayout {
VertexLayout {
array_stride: size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Instance,
attributes: Self::vertex_attributes().to_vec(),
}
}
#[must_use]
pub fn vertex_range() -> Range<u32> {
0..Self::QUAD_VERTICES
}
#[must_use]
pub fn instance_range(first_instance: u32, count: u32) -> Range<u32> {
first_instance..first_instance.saturating_add(count)
}
#[must_use]
pub fn from_strip(strip: &Strip, width: u16, draw: StripDraw) -> Self {
Self {
x: strip.x,
y: strip.y,
width,
dense_width_or_rect_height: width,
col_idx_or_rect_frac: alpha_column(strip.alpha_idx()),
payload: draw.payload,
paint_and_rect_flag: draw.paint,
depth_index: draw.depth_index,
}
}
#[must_use]
pub fn from_strip_pair(strip: &Strip, next: &Strip, draw: StripDraw) -> Self {
Self::from_strip(strip, strip.width_to(next), draw)
}
#[must_use]
pub fn gap_fill(strip: &Strip, next: &Strip, draw: StripDraw) -> Option<Self> {
if !next.fill_gap() || next.y != strip.y {
return None;
}
let gap_x = strip.x.saturating_add(strip.width_to(next));
let gap_width = next.x.saturating_sub(gap_x);
if gap_width == 0 {
return None;
}
Some(Self::solid_fill(gap_x, strip.y, gap_width, draw))
}
#[must_use]
pub fn solid_fill(x: u16, y: u16, width: u16, draw: StripDraw) -> Self {
Self {
x,
y,
width,
dense_width_or_rect_height: 0,
col_idx_or_rect_frac: 0,
payload: draw.payload,
paint_and_rect_flag: draw.paint,
depth_index: draw.depth_index,
}
}
#[must_use]
pub fn from_rect(x: u16, y: u16, width: u16, height: u16, frac: u32, draw: StripDraw) -> Self {
Self {
x,
y,
width,
dense_width_or_rect_height: height,
col_idx_or_rect_frac: frac,
payload: draw.payload,
paint_and_rect_flag: draw.paint | RECT_STRIP_FLAG,
depth_index: draw.depth_index,
}
}
#[must_use]
pub const fn is_rect(&self) -> bool {
self.paint_and_rect_flag & RECT_STRIP_FLAG != 0
}
#[must_use]
pub const fn paint(&self) -> u32 {
self.paint_and_rect_flag & !RECT_STRIP_FLAG
}
}