blob: bb8982257d853d836d71498607bc7a60189271e6 [file]
// Copyright 2025 the Vello Authors
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! Backend agnostic renderer module.
use core::fmt;
use bytemuck::{Pod, Zeroable};
/// Dimensions of the rendering target
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RenderSize {
/// Width of the rendering target
pub width: u32,
/// Height of the rendering target
pub height: u32,
}
/// Configuration for the GPU renderer
#[repr(C)]
#[derive(Debug, Copy, Clone, Pod, Zeroable)]
pub struct Config {
/// Width of the rendering target
pub width: u32,
/// Height of the rendering target
pub height: u32,
/// Height of a strip in the rendering
pub strip_height: u32,
/// Number of trailing zeros in `alphas_tex_width` (log2 of width).
/// Pre-calculated on CPU since downlevel targets do not support `firstTrailingBit`.
pub alphas_tex_width_bits: u32,
}
/// Represents a GPU strip for rendering.
///
/// This struct corresponds to the `StripInstance` struct in the shader.
/// See the `StripInstance` documentation in `render_strips.wgsl` for detailed field descriptions.
#[repr(C)]
#[derive(Clone, Copy, Zeroable, Pod)]
pub struct GpuStrip {
/// See `StripInstance::xy` documentation in `render_strips.wgsl`.
pub x: u16,
/// See `StripInstance::xy` documentation in `render_strips.wgsl`.
pub y: u16,
/// See `StripInstance::widths` documentation in `render_strips.wgsl`.
pub width: u16,
/// See `StripInstance::widths` documentation in `render_strips.wgsl`.
pub dense_width: u16,
/// See `StripInstance::col_idx` documentation in `render_strips.wgsl`.
pub col_idx: u32,
/// See `StripInstance::payload` documentation in `render_strips.wgsl`.
pub payload: u32,
/// See `StripInstance::paint` documentation in `render_strips.wgsl`.
pub paint: u32,
}
// Constants must stay in sync with `render_strips.wgsl`.
const COLOR_SOURCE_PAYLOAD: u32 = 0;
const COLOR_SOURCE_SLOT: u32 = 1;
const COLOR_SOURCE_BLEND: u32 = 2;
const PAINT_TYPE_SOLID: u32 = 0;
const PAINT_TYPE_IMAGE: u32 = 1;
impl fmt::Debug for GpuStrip {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let color_source = (self.paint >> 30) & 0x3; // Changed to 2 bits for 3 source types
let mut debug_struct = f.debug_struct("GpuStrip");
debug_struct
.field("x", &self.x)
.field("y", &self.y)
.field("width", &self.width)
.field("dense_width", &self.dense_width)
.field("col_idx", &self.col_idx);
let paint_info = match color_source {
COLOR_SOURCE_PAYLOAD => {
let paint_type = (self.paint >> 28) & 0x3; // Adjusted bit position
if paint_type == PAINT_TYPE_SOLID {
format!("Solid(color_source=payload)")
} else if paint_type == PAINT_TYPE_IMAGE {
let paint_tex_id = self.paint & 0x0FFFFFFF; // Adjusted mask
format!("Image(color_source=payload, texture_id={})", paint_tex_id)
} else {
format!("Unknown(color_source=payload, type={})", paint_type)
}
}
COLOR_SOURCE_SLOT => {
let opacity = self.paint & 0xFF;
format!("Slot(color_source=slot, opacity={})", opacity)
}
COLOR_SOURCE_BLEND => {
let dest_slot = (self.paint >> 16) & 0x3FFF;
let mix = (self.paint >> 8) & 0xFF;
let compose = self.paint & 0xFF;
format!("Blend(dest_slot={}, mix={}, compose={})", dest_slot, mix, compose)
}
_ => format!("Unknown(color_source={})", color_source)
};
debug_struct.field("paint", &paint_info);
// Decode payload based on paint configuration
let payload_info = match color_source {
COLOR_SOURCE_PAYLOAD => {
let paint_type = (self.paint >> 28) & 0x3;
if paint_type == PAINT_TYPE_SOLID {
let r = (self.payload >> 0) & 0xFF;
let g = (self.payload >> 8) & 0xFF;
let b = (self.payload >> 16) & 0xFF;
let a = (self.payload >> 24) & 0xFF;
format!("Color(r={}, g={}, b={}, a={})", r, g, b, a)
} else if paint_type == PAINT_TYPE_IMAGE {
let x = self.payload & 0xFFFF;
let y = self.payload >> 16;
format!("ImageCoords(x={}, y={})", x, y)
} else {
format!("Unknown(raw=0x{:08x})", self.payload)
}
}
COLOR_SOURCE_SLOT => {
format!("SlotIndex({})", self.payload)
}
COLOR_SOURCE_BLEND => {
format!("SourceSlot({})", self.payload)
}
_ => format!("Unknown(raw=0x{:08x})", self.payload)
};
debug_struct.field("payload", &payload_info);
debug_struct.finish()
}
}
/// Represents a GPU encoded image data for rendering
// Align to 16 bytes for RGBA32Uint alignment
#[repr(C, align(16))]
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[allow(dead_code, reason = "Clippy fails when --no-default-features")]
pub(crate) struct GpuEncodedImage {
// 1st 16 bytes
/// The rendering quality of the image.
pub quality_and_extend_modes: u32,
/// The extends in the horizontal and vertical direction.
/// The size of the image in pixels.
pub image_size: u32,
/// The offset of the image in the atlas texture in pixels.
pub image_offset: u32,
pub _padding1: u32,
// 2nd & 3rd 16 bytes
/// A transform to apply to the image.
pub transform: [f32; 6],
/// Padding to align to 64 bytes (16-byte aligned)
pub _padding2: [u32; 2],
}
#[cfg(all(target_arch = "wasm32", feature = "webgl", feature = "wgpu"))]
pub(crate) fn maybe_warn_about_webgl_feature_conflict() {
use core::sync::atomic::{AtomicBool, Ordering};
static HAS_WARNED: AtomicBool = AtomicBool::new(false);
if !HAS_WARNED.swap(true, Ordering::Release)
&& wgpu::Backends::all().contains(wgpu::Backends::GL)
{
log::warn!(
r#"Both WebGL and wgpu with the \"webgl\" feature are enabled.
For optimal performance and binary size on web targets, use only the dedicated WebGL renderer."#
);
}
}
#[cfg(all(
any(all(target_arch = "wasm32", feature = "webgl"), feature = "wgpu"),
not(all(target_arch = "wasm32", feature = "webgl", feature = "wgpu"))
))]
pub(crate) fn maybe_warn_about_webgl_feature_conflict() {}