Register a primitive only when it is drawn, and keep the prelude shared
Nothing seeds the registry any more, so a kind's id is decided by the first draw and no order within a layer can be relied on even by accident. A ui that draws no images now pays for no image pipeline, and a layer's list vector only reaches the highest kind that layer draws. The atlas and the sampler are still bound for every draw, but are declared by the two shaders that read them rather than by the prelude, which is now only what every primitive uses. `TexturePrimitive` gets a `From<&TextureHandle>`; `Painter::texture_at` stays because the share of the handle is what keeps the slot from being freed while it is drawn, which a `Pod` primitive cannot hold. Checked on the headless rig that the layers carry the ordering rather than the ids: with a bare text drawn first, so glyph registers before rect, a stacked label still draws over its background. Also re-ran an image alone in a layer, now the only primitive a ui registers, and a four-layer atlas. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
29d390da52
commit
01a9b8633d
6 files changed
+35
-40
No files matched your search
@@ -364,9 +364,9 @@ impl UiRenderNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One list's per-instance data. Per primitive with `stride` stated: a
|
/// One list's per-instance data. Each primitive needs its own, stating its
|
||||||
/// `None` minimum is filled in from the first pipeline built against the
|
/// own `stride`: a `None` minimum takes its value from the first pipeline
|
||||||
/// layout, so a shared one would hold every primitive to the largest.
|
/// built against the layout, and then holds every later one to that.
|
||||||
fn data_layout(device: &Device, stride: u64) -> BindGroupLayout {
|
fn data_layout(device: &Device, stride: u64) -> BindGroupLayout {
|
||||||
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
entries: &[BindGroupLayoutEntry {
|
entries: &[BindGroupLayoutEntry {
|
||||||
@@ -383,8 +383,7 @@ impl UiRenderNode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The one image an instance samples. The sampler is shared, so there is
|
/// The image one instance samples.
|
||||||
/// nothing else in here.
|
|
||||||
fn image_layout(device: &Device) -> BindGroupLayout {
|
fn image_layout(device: &Device) -> BindGroupLayout {
|
||||||
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
||||||
entries: &[BindGroupLayoutEntry {
|
entries: &[BindGroupLayoutEntry {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{any::TypeId, marker::PhantomData};
|
use std::{any::TypeId, marker::PhantomData};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Color, UiRegion, WidgetId,
|
Color, TextureHandle, UiRegion, WidgetId,
|
||||||
render::data::{MaskIdx, PrimitiveInstance},
|
render::data::{MaskIdx, PrimitiveInstance},
|
||||||
util::{HashMap, Vec2},
|
util::{HashMap, Vec2},
|
||||||
};
|
};
|
||||||
@@ -15,13 +15,12 @@ pub trait Primitive: Pod + 'static {
|
|||||||
/// Compiled after `prelude.wgsl`, which states what it declares and what
|
/// Compiled after `prelude.wgsl`, which states what it declares and what
|
||||||
/// it is given.
|
/// it is given.
|
||||||
const WGSL: &'static str;
|
const WGSL: &'static str;
|
||||||
/// Reads the image an instance samples, for a primitive that draws one.
|
/// Reads the image an instance uses. Each instance is then a draw of its
|
||||||
/// Each instance is then a draw of its own, bound for it alone.
|
/// own, with that image bound for it alone.
|
||||||
const TEXTURE: Option<fn(&Self) -> u32> = None;
|
const TEXTURE: Option<fn(&Self) -> u32> = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Which registered primitive an instance is. Only `PrimitiveRegistry::kind`
|
/// Which registered primitive an instance is.
|
||||||
/// mints one, so holding it is the proof that `P` has a list to go in.
|
|
||||||
pub struct PrimitiveKind<P> {
|
pub struct PrimitiveKind<P> {
|
||||||
id: u32,
|
id: u32,
|
||||||
_p: PhantomData<fn(P)>,
|
_p: PhantomData<fn(P)>,
|
||||||
@@ -45,6 +44,7 @@ impl<P> Clone for PrimitiveKind<P> {
|
|||||||
impl<P> Copy for PrimitiveKind<P> {}
|
impl<P> Copy for PrimitiveKind<P> {}
|
||||||
|
|
||||||
/// Every primitive a ui can draw, in the order they were first drawn.
|
/// Every primitive a ui can draw, in the order they were first drawn.
|
||||||
|
#[derive(Default)]
|
||||||
pub struct PrimitiveRegistry {
|
pub struct PrimitiveRegistry {
|
||||||
kinds: Vec<PrimitiveSource>,
|
kinds: Vec<PrimitiveSource>,
|
||||||
ids: HashMap<TypeId, u32>,
|
ids: HashMap<TypeId, u32>,
|
||||||
@@ -59,21 +59,6 @@ pub struct PrimitiveSource {
|
|||||||
pub textured: bool,
|
pub textured: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for PrimitiveRegistry {
|
|
||||||
/// The built-ins are registered up front rather than on first use, so that
|
|
||||||
/// what a ui happens to draw first cannot decide anything about them.
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut registry = Self {
|
|
||||||
kinds: Vec::new(),
|
|
||||||
ids: HashMap::default(),
|
|
||||||
};
|
|
||||||
registry.kind::<RectPrimitive>();
|
|
||||||
registry.kind::<GlyphPrimitive>();
|
|
||||||
registry.kind::<TexturePrimitive>();
|
|
||||||
registry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PrimitiveRegistry {
|
impl PrimitiveRegistry {
|
||||||
/// Registers `P` if this is the first time it has been drawn.
|
/// Registers `P` if this is the first time it has been drawn.
|
||||||
pub fn kind<P: Primitive>(&mut self) -> PrimitiveKind<P> {
|
pub fn kind<P: Primitive>(&mut self) -> PrimitiveKind<P> {
|
||||||
@@ -354,3 +339,11 @@ impl Primitive for TexturePrimitive {
|
|||||||
const WGSL: &'static str = include_str!("shader/texture.wgsl");
|
const WGSL: &'static str = include_str!("shader/texture.wgsl");
|
||||||
const TEXTURE: Option<fn(&Self) -> u32> = Some(|texture| texture.slot);
|
const TEXTURE: Option<fn(&Self) -> u32> = Some(|texture| texture.slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&TextureHandle> for TexturePrimitive {
|
||||||
|
fn from(handle: &TextureHandle) -> Self {
|
||||||
|
Self {
|
||||||
|
slot: handle.slot(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,12 @@
|
|||||||
// Matches `GlyphEntry::IS_COLORED`.
|
// Matches `GlyphEntry::IS_COLORED`.
|
||||||
const COLORED: u32 = 1u;
|
const COLORED: u32 = 1u;
|
||||||
|
|
||||||
|
// The glyph atlas, whose array layers are its pages.
|
||||||
|
@group(0) @binding(2)
|
||||||
|
var atlas: texture_2d_array<f32>;
|
||||||
|
@group(0) @binding(3)
|
||||||
|
var samp: sampler;
|
||||||
|
|
||||||
struct GlyphInfo {
|
struct GlyphInfo {
|
||||||
uv_min: vec2<f32>,
|
uv_min: vec2<f32>,
|
||||||
uv_max: vec2<f32>,
|
uv_max: vec2<f32>,
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
// Prepended to every primitive's shader, which declares its own instance data
|
// Prepended to every primitive's shader, which declares its own instance data
|
||||||
// as `var<storage> <name>: array<T>` at group 1 binding 0, and an `fs_main`
|
// as `var<storage> <name>: array<T>` at group 1 binding 0, and an `fs_main`
|
||||||
// shading one instance of it. A primitive that samples an image of its own
|
// shading one instance of it.
|
||||||
// takes it at group 2 binding 0; see texture.wgsl.
|
//
|
||||||
|
// The rest of group 0 is bound for every draw but declared by the shaders that
|
||||||
|
// read it: the glyph atlas at binding 2 and the ui's sampler at binding 3. A
|
||||||
|
// primitive that samples an image of its own takes it at group 2 binding 0.
|
||||||
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var<uniform> window: WindowUniform;
|
var<uniform> window: WindowUniform;
|
||||||
@group(0) @binding(1)
|
@group(0) @binding(1)
|
||||||
var<storage> masks: array<Mask>;
|
var<storage> masks: array<Mask>;
|
||||||
// The glyph atlas, whose array layers are its pages.
|
|
||||||
@group(0) @binding(2)
|
|
||||||
var atlas: texture_2d_array<f32>;
|
|
||||||
@group(0) @binding(3)
|
|
||||||
var samp: sampler;
|
|
||||||
|
|
||||||
struct WindowUniform {
|
struct WindowUniform {
|
||||||
dim: vec2<f32>,
|
dim: vec2<f32>,
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// The image this instance draws, bound for it alone.
|
// The image this instance draws, bound for it alone.
|
||||||
@group(2) @binding(0)
|
@group(2) @binding(0)
|
||||||
var image: texture_2d<f32>;
|
var image: texture_2d<f32>;
|
||||||
|
@group(0) @binding(3)
|
||||||
|
var samp: sampler;
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ impl<'a> Painter<'a> {
|
|||||||
self.write(kind, primitive, region);
|
self.write(kind, primitive, region);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// For a caller with many of one primitive to write, since looking the kind
|
/// Takes the kind, for a caller writing many of one primitive.
|
||||||
/// up is per type rather than per instance.
|
|
||||||
fn write<P: Primitive>(&mut self, kind: PrimitiveKind<P>, primitive: P, region: UiRegion) {
|
fn write<P: Primitive>(&mut self, kind: PrimitiveKind<P>, primitive: P, region: UiRegion) {
|
||||||
let h = self.state.layers.write(
|
let h = self.state.layers.write(
|
||||||
self.layer,
|
self.layer,
|
||||||
@@ -98,14 +97,12 @@ impl<'a> Painter<'a> {
|
|||||||
self.texture_at(handle, self.region);
|
self.texture_at(handle, self.region);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A texture primitive, plus a share of the handle it names -- which a
|
||||||
|
/// `Pod` primitive cannot carry, and without which the slot could be
|
||||||
|
/// freed and reused while still drawn.
|
||||||
pub fn texture_at(&mut self, handle: &TextureHandle, region: UiRegion) {
|
pub fn texture_at(&mut self, handle: &TextureHandle, region: UiRegion) {
|
||||||
self.textures.push(handle.clone());
|
self.textures.push(handle.clone());
|
||||||
self.primitive_at(
|
self.primitive_at(TexturePrimitive::from(handle), region);
|
||||||
TexturePrimitive {
|
|
||||||
slot: handle.slot(),
|
|
||||||
},
|
|
||||||
region,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_text(
|
pub fn render_text(
|
||||||
|
|||||||
Reference in new issue
Block a user