use cranpose_core::NodeId;
use cranpose_ui_graphics::{Point, Rect};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct RasterScale(u32);
impl RasterScale {
pub fn from_scale(scale: f32) -> Self {
let normalized = if scale.is_finite() && scale > 0.0 {
scale
} else {
1.0
};
Self(normalized.to_bits())
}
pub fn raw(self) -> u32 {
self.0
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct LayerRasterCacheHashes {
pub target_content: u64,
pub effect: u64,
}
pub const LAYER_RASTER_CACHE_KIND_COUNT: usize = 5;
pub const LAYER_RASTER_CACHE_KIND_LABELS: [&str; LAYER_RASTER_CACHE_KIND_COUNT] =
["src", "backdrop", "range", "prefix", "effect"];
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum LayerRasterCacheKind {
SourceContent,
BackdropEffect,
SceneRange,
PrefixSnapshot,
LayerEffect,
}
impl LayerRasterCacheKind {
fn identity_kind(self) -> u8 {
match self {
Self::SourceContent => 0,
Self::BackdropEffect => 1,
Self::SceneRange => 2,
Self::PrefixSnapshot => 3,
Self::LayerEffect => 4,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LayerRasterCacheIdentity {
stable_id: NodeId,
kind: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct LayerRasterCacheKey {
kind: LayerRasterCacheKind,
stable_id: Option<NodeId>,
content_hash: u64,
effect_hash: u64,
local_bounds_bits: [u32; 4],
pixel_size: [u32; 2],
raster_scale: RasterScale,
device_phase_steps: [u32; 2],
}
const DEVICE_PHASE_STEPS: f32 = 16.0;
fn device_phase_steps(phase: Point) -> [u32; 2] {
let steps = |value: f32| {
((value.rem_euclid(1.0) * DEVICE_PHASE_STEPS).round() as u32) % DEVICE_PHASE_STEPS as u32
};
[steps(phase.x), steps(phase.y)]
}
fn local_bounds_bits(local_bounds: Rect) -> [u32; 4] {
[
local_bounds.x.to_bits(),
local_bounds.y.to_bits(),
local_bounds.width.to_bits(),
local_bounds.height.to_bits(),
]
}
impl LayerRasterCacheKey {
pub fn source_content(
stable_id: Option<NodeId>,
content_hash: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
device_phase: Point,
) -> Self {
Self {
kind: LayerRasterCacheKind::SourceContent,
stable_id,
content_hash,
effect_hash: 0,
local_bounds_bits: local_bounds_bits(local_bounds),
pixel_size: [pixel_size.0, pixel_size.1],
raster_scale,
device_phase_steps: device_phase_steps(device_phase),
}
}
pub fn backdrop_effect(
stable_id: Option<NodeId>,
input_hash: u64,
effect_hash: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
) -> Self {
Self::effect(
LayerRasterCacheKind::BackdropEffect,
stable_id,
input_hash,
effect_hash,
local_bounds,
pixel_size,
raster_scale,
)
}
pub fn layer_effect(
stable_id: Option<NodeId>,
input_hash: u64,
effect_hash: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
) -> Self {
Self::effect(
LayerRasterCacheKind::LayerEffect,
stable_id,
input_hash,
effect_hash,
local_bounds,
pixel_size,
raster_scale,
)
}
fn effect(
kind: LayerRasterCacheKind,
stable_id: Option<NodeId>,
input_hash: u64,
effect_hash: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
) -> Self {
Self {
kind,
stable_id,
content_hash: input_hash,
effect_hash,
local_bounds_bits: local_bounds_bits(local_bounds),
pixel_size: [pixel_size.0, pixel_size.1],
raster_scale,
device_phase_steps: [0; 2],
}
}
pub fn scene_range(
content_hash: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
) -> Self {
Self {
kind: LayerRasterCacheKind::SceneRange,
stable_id: None,
content_hash,
effect_hash: 0,
local_bounds_bits: local_bounds_bits(local_bounds),
pixel_size: [pixel_size.0, pixel_size.1],
raster_scale,
device_phase_steps: [0; 2],
}
}
pub fn prefix_snapshot(
content_hash: u64,
prefix_len: u64,
local_bounds: Rect,
pixel_size: (u32, u32),
raster_scale: RasterScale,
) -> Self {
Self {
kind: LayerRasterCacheKind::PrefixSnapshot,
stable_id: None,
content_hash,
effect_hash: prefix_len,
local_bounds_bits: local_bounds_bits(local_bounds),
pixel_size: [pixel_size.0, pixel_size.1],
raster_scale,
device_phase_steps: [0; 2],
}
}
pub fn kind_slot(self) -> usize {
self.kind.identity_kind() as usize
}
pub fn stable_id(self) -> Option<NodeId> {
self.stable_id
}
pub fn is_scene_range(self) -> bool {
matches!(
self.kind,
LayerRasterCacheKind::SceneRange | LayerRasterCacheKind::PrefixSnapshot
)
}
pub fn is_source_content(self) -> bool {
self.kind == LayerRasterCacheKind::SourceContent
}
pub fn identity(self) -> Option<LayerRasterCacheIdentity> {
Some(LayerRasterCacheIdentity {
stable_id: self.stable_id?,
kind: self.kind.identity_kind(),
})
}
pub fn pixel_size(self) -> (u32, u32) {
(self.pixel_size[0], self.pixel_size[1])
}
pub fn local_bounds_bits(self) -> [u32; 4] {
self.local_bounds_bits
}
pub fn raster_scale(self) -> RasterScale {
self.raster_scale
}
}
#[cfg(test)]
#[path = "tests/raster_cache_tests.rs"]
mod tests;