Files
iris/core/src/render/data.rs
T
iris-aiandClaude Opus 5 1f9dc48b80 Carry a box in a move slot, not a translation
A slot now holds the box its contents are placed within, in the coordinates
of the slot it names, and `prelude.wgsl` composes the chain with `within`
instead of adding a delta. A translation is the special case where the box
has its parent's relative extent, so every caller passes
`UiRegion::FULL.offset(delta)` and nothing changes on screen yet: 42 tests
pass and `tabs` at 1920x1200 is byte-identical.

`Moves::resolve` takes the region to compose rather than returning a sum, so
the CPU walk is the same operation the shader performs.

Measured against the translate slot on the same binary with
`tests/chain_cost.rs`, 200k instances: +0.6% at depth 1, +0.5% at 2, +0.8% at
4, then +9.6% at 8 and +32.2% at 64. Free at the depth opt-in slots produce,
which is the next commit; the per-level cost was always the dependent load
rather than the arithmetic.

The identity is `UiRegion::FULL` rather than zero, which `MoveOffset`'s
comment says beside the `Zeroable` that `Pod` requires: a zeroed entry is a
box of no extent and collapses its subtree to a point.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-14 12:14:24 -04:00

92 lines
2.3 KiB
Rust

use crate::{UiRegion, util::Id, util::Vec2};
use wgpu::*;
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, Default)]
pub struct WindowUniform {
pub dim: Vec2,
}
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct PrimitiveInstance {
pub region: UiRegion,
pub mask_idx: MaskIdx,
pub move_idx: MoveIdx,
}
impl PrimitiveInstance {
const ATTRIBS: [VertexAttribute; 6] = vertex_attr_array![
0 => Float32x2,
1 => Float32x2,
2 => Float32x2,
3 => Float32x2,
4 => Uint32,
5 => Uint32,
];
pub fn desc() -> VertexBufferLayout<'static> {
VertexBufferLayout {
array_stride: std::mem::size_of::<Self>() as BufferAddress,
step_mode: VertexStepMode::Instance,
attributes: &Self::ATTRIBS,
}
}
}
pub type MaskIdx = Id<u32>;
impl MaskIdx {
pub const NONE: Self = Self::preset(u32::MAX);
}
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Mask {
pub region: UiRegion,
pub move_idx: MoveIdx,
}
/// Its own type rather than another `Id<u32>`, because it sits beside
/// `MaskIdx` in an instance and the two must not be swappable.
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, bytemuck::Pod, bytemuck::Zeroable)]
pub struct MoveIdx(u32);
impl MoveIdx {
pub const NONE: Self = Self(u32::MAX);
pub(crate) fn slot(idx: usize) -> Self {
Self(idx as u32)
}
pub(crate) fn idx(self) -> usize {
self.0 as usize
}
}
/// One link of the chain a primitive's position is resolved through: the box
/// its contents are placed within, given in the coordinates of the slot it
/// names. Moving or resizing a subtree writes its own slot and nothing else.
///
/// The identity is `UiRegion::FULL`, not zero: a zeroed entry is a box of no
/// extent, which collapses everything under it to a point.
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct MoveOffset {
pub region: UiRegion,
pub parent: MoveIdx,
}
unsafe impl bytemuck::Pod for MoveOffset {}
unsafe impl bytemuck::Zeroable for MoveOffset {}
impl MoveOffset {
pub fn root(parent: MoveIdx) -> Self {
Self {
region: UiRegion::FULL,
parent,
}
}
}