Replaces the bindless `binding_array<texture_2d<f32>>` the renderer bound every texture through. That array needs `VK_EXT_descriptor_indexing`, which a real share of Android GPUs lack, so the old shape did not run there at all. The two things being bound want opposite treatment, so they are now split: - **Glyph atlas pages become layers of one `texture_2d_array`.** A glyph primitive carries a `layer` instead of a view/sampler index pair. A layer index is an ordinary sampling operand, so this needs nothing beyond plain Vulkan 1.0 / GLES. Growing the atlas recreates the array with headroom and `copy_texture_to_texture`s the old layers across, no readback. - **A standalone image gets its own texture and its own bind group,** and draws in its own call. It no longer needs a per-instance entry in `PrimitiveData`: the bind group has already picked the texture. `Primitives` keeps images in a list of their own as a result, with `PrimitiveChange::is_image` naming which list a renumbering belongs to -- the two have independent index spaces, so `(layer, inst_idx)` alone would collide between them. Two notes on judgement calls, since this slice was rebuilt on top of `main` rather than transplanted: - The source version renamed `GlyphEntry::is_colored` to `is_color` and added a second `IS_COLOR` flag constant beside the existing `GlyphEntry::IS_COLORED`. Both dropped: #10's naming and its `flags()` are kept, and UVs stay `Vec2` rather than going back to `[f32; 2]`. - `ImageGpu` no longer holds the `Texture` behind its view, which removes an `#[allow(dead_code)]`. A `TextureView` keeps its own reference to the texture, checked by rendering rather than assumed -- see below. ### Verification ``` cargo fmt --all --check cargo clippy --workspace --all-targets --locked -- -D warnings cargo test --workspace --locked ``` All clean; the 4 text-edit tests pass. The only clippy output is the pre-existing future-incompatibility notice about `naga`/`wgpu`/`winit`. Because this is a rendering change, it was also run for real rather than only compiled. The `tabs` example was rendered on this machine's GPU -- Venus onto an RX 7900 XT, confirmed from the loaded ICD (`libvulkan_virtio.so` on `/dev/dri/renderD128`) rather than assumed, since a failed Vulkan init here silently falls back to llvmpipe and would make the screenshots meaningless. Screenshots before and after the change are **byte-identical** (same md5) in two scenes: the default tab, which exercises text (the atlas path) and rects, and the image tab with a standalone image pushed at startup, which exercises the per-image bind group. The image-tab scene needed a temporary local edit to the example to push the image without a click; that edit is not part of this branch. The same comparison, re-run after dropping the `Texture` field, is still byte-identical -- which is the check that the view alone keeps it alive. --------- Co-authored-by: iris <2+iris@noreply.localhost> Reviewed-on: iris/iris#11 Reviewed-by: iris <2+iris@noreply.localhost> Co-authored-by: AIris <4+iris-ai@noreply.localhost>
276 lines
6.7 KiB
Rust
276 lines
6.7 KiB
Rust
use std::ops::{Index, IndexMut};
|
|
|
|
use crate::{
|
|
render::{LayerDraws, MaskIdx, Primitive, PrimitiveHandle, PrimitiveInst},
|
|
util::to_mut,
|
|
};
|
|
|
|
pub type LayerId = usize;
|
|
|
|
struct LayerNode<T> {
|
|
next: Ptr,
|
|
prev: Ptr,
|
|
child: Option<Child>,
|
|
depth: usize,
|
|
data: T,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
enum Ptr {
|
|
/// continue on same level
|
|
Next(usize),
|
|
/// go back to parent
|
|
Parent(usize),
|
|
/// end
|
|
None,
|
|
}
|
|
|
|
/// TODO: currently this does not ever free layers
|
|
/// is that realistically desired?
|
|
pub struct Layers<T> {
|
|
vec: Vec<LayerNode<T>>,
|
|
/// index of last layer at top level (start at first = 0)
|
|
last: usize,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
struct Child {
|
|
head: usize,
|
|
tail: usize,
|
|
}
|
|
|
|
pub type DrawLayers = Layers<LayerDraws>;
|
|
|
|
impl<T: Default> Layers<T> {
|
|
pub fn new() -> Layers<T> {
|
|
Self {
|
|
vec: vec![LayerNode::head()],
|
|
last: 0,
|
|
}
|
|
}
|
|
|
|
pub fn clear(&mut self) {
|
|
self.vec.clear();
|
|
self.vec.push(LayerNode::head());
|
|
}
|
|
|
|
fn push(&mut self, node: LayerNode<T>) -> LayerId {
|
|
let i = self.vec.len();
|
|
self.vec.push(node);
|
|
i
|
|
}
|
|
|
|
pub fn next(&mut self, i: LayerId) -> LayerId {
|
|
if let Ptr::Next(i) = self.vec[i].next {
|
|
return i;
|
|
}
|
|
let i_new = self.push(LayerNode::new(
|
|
T::default(),
|
|
self.vec[i].next,
|
|
Ptr::Next(i),
|
|
self.vec[i].depth,
|
|
));
|
|
self.vec[i].next = Ptr::Next(i_new);
|
|
self.vec[i_new].prev = Ptr::Next(i);
|
|
match self.vec[i_new].next {
|
|
Ptr::Next(i) => self.vec[i].prev = Ptr::Next(i_new),
|
|
Ptr::Parent(i) => self.vec[i].child.as_mut().unwrap().tail = i_new,
|
|
Ptr::None => self.last = i_new,
|
|
}
|
|
i_new
|
|
}
|
|
|
|
pub fn child(&mut self, i: LayerId) -> LayerId {
|
|
if let Some(c) = self.vec[i].child {
|
|
return c.head;
|
|
}
|
|
let i_child = self.push(LayerNode::new(
|
|
T::default(),
|
|
Ptr::Parent(i),
|
|
Ptr::Parent(i),
|
|
self.vec[i].depth + 1,
|
|
));
|
|
self.vec[i].child = Some(Child {
|
|
head: i_child,
|
|
tail: i_child,
|
|
});
|
|
i_child
|
|
}
|
|
|
|
pub fn iter_mut(&mut self) -> LayerIteratorMut<'_, T> {
|
|
LayerIteratorMut::new(&mut self.vec, self.last)
|
|
}
|
|
|
|
pub fn iter_orderless_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
|
|
self.vec.iter_mut().map(|n| &mut n.data).enumerate()
|
|
}
|
|
|
|
pub fn iter(&self) -> impl Iterator<Item = (LayerId, &T)> {
|
|
self.indices().map(|i| (i, &self.vec[i].data))
|
|
}
|
|
|
|
pub fn iter_depth(&self) -> impl Iterator<Item = ((LayerId, usize), &T)> {
|
|
self.indices()
|
|
.map(|i| ((i, self.vec[i].depth), &self.vec[i].data))
|
|
}
|
|
|
|
pub fn indices(&self) -> LayerIndexIterator<'_, T> {
|
|
LayerIndexIterator::new(&self.vec, self.last)
|
|
}
|
|
}
|
|
|
|
impl DrawLayers {
|
|
pub fn write<P: Primitive>(
|
|
&mut self,
|
|
layer: LayerId,
|
|
info: PrimitiveInst<P>,
|
|
) -> PrimitiveHandle {
|
|
self[layer].write(layer, info)
|
|
}
|
|
|
|
pub fn free(&mut self, h: &PrimitiveHandle) -> MaskIdx {
|
|
self[h.layer].free(h)
|
|
}
|
|
}
|
|
|
|
impl<T: Default> Default for Layers<T> {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl<T> Index<LayerId> for Layers<T> {
|
|
type Output = T;
|
|
|
|
fn index(&self, index: LayerId) -> &Self::Output {
|
|
&self.vec[index].data
|
|
}
|
|
}
|
|
|
|
impl<T> IndexMut<LayerId> for Layers<T> {
|
|
fn index_mut(&mut self, index: LayerId) -> &mut Self::Output {
|
|
&mut self.vec[index].data
|
|
}
|
|
}
|
|
|
|
impl<T: Default> LayerNode<T> {
|
|
pub fn new(data: T, next: Ptr, prev: Ptr, depth: usize) -> Self {
|
|
Self {
|
|
next,
|
|
prev,
|
|
child: None,
|
|
data,
|
|
depth,
|
|
}
|
|
}
|
|
|
|
pub fn head() -> Self {
|
|
Self::new(T::default(), Ptr::None, Ptr::None, 0)
|
|
}
|
|
}
|
|
|
|
pub struct LayerIteratorMut<'a, T> {
|
|
inner: LayerIndexIterator<'a, T>,
|
|
}
|
|
|
|
impl<'a, T> Iterator for LayerIteratorMut<'a, T> {
|
|
type Item = (usize, &'a mut T);
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let i = self.inner.next()?;
|
|
// SAFETY: requires index iterator to work properly
|
|
let layer = unsafe { to_mut(&self.inner.vec[i].data) };
|
|
Some((i, layer))
|
|
}
|
|
}
|
|
|
|
impl<'a, T> DoubleEndedIterator for LayerIteratorMut<'a, T> {
|
|
fn next_back(&mut self) -> Option<Self::Item> {
|
|
let i = self.inner.next_back()?;
|
|
// SAFETY: requires index iterator to work properly
|
|
let layer = unsafe { to_mut(&self.inner.vec[i].data) };
|
|
Some((i, layer))
|
|
}
|
|
}
|
|
|
|
impl<'a, T> LayerIteratorMut<'a, T> {
|
|
fn new(vec: &'a mut Vec<LayerNode<T>>, last: usize) -> Self {
|
|
Self {
|
|
inner: LayerIndexIterator::new(vec, last),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct LayerIndexIterator<'a, T> {
|
|
next: Option<usize>,
|
|
next_back: Option<usize>,
|
|
vec: &'a Vec<LayerNode<T>>,
|
|
}
|
|
|
|
impl<'a, T> Iterator for LayerIndexIterator<'a, T> {
|
|
type Item = usize;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let ret_i = self.next?;
|
|
let node = &self.vec[ret_i];
|
|
self.next = if let Some(c) = node.child {
|
|
Some(c.head)
|
|
} else if let Ptr::Next(i) = node.next {
|
|
Some(i)
|
|
} else if let Ptr::Parent(i) = node.next {
|
|
let mut node = &self.vec[i];
|
|
while let Ptr::Parent(i) = node.next {
|
|
node = &self.vec[i];
|
|
}
|
|
if let Ptr::Next(i) = node.next {
|
|
Some(i)
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
None
|
|
};
|
|
if self.next_back.unwrap() == ret_i {
|
|
self.next = None;
|
|
self.next_back = None;
|
|
}
|
|
Some(ret_i)
|
|
}
|
|
}
|
|
|
|
impl<'a, T> DoubleEndedIterator for LayerIndexIterator<'a, T> {
|
|
fn next_back(&mut self) -> Option<Self::Item> {
|
|
let ret_i = self.next_back?;
|
|
let node = &self.vec[ret_i];
|
|
self.next_back = if let Ptr::Next(mut i) = node.prev {
|
|
while let Some(c) = self.vec[i].child {
|
|
i = c.tail
|
|
}
|
|
Some(i)
|
|
} else if let Ptr::Parent(i) = node.prev {
|
|
Some(i)
|
|
} else {
|
|
None
|
|
};
|
|
if self.next.unwrap() == ret_i {
|
|
self.next = None;
|
|
self.next_back = None;
|
|
}
|
|
Some(ret_i)
|
|
}
|
|
}
|
|
|
|
impl<'a, T> LayerIndexIterator<'a, T> {
|
|
fn new(vec: &'a Vec<LayerNode<T>>, last: usize) -> Self {
|
|
let mut last = last;
|
|
while let Some(c) = vec[last].child {
|
|
last = c.tail;
|
|
}
|
|
Self {
|
|
next: Some(0),
|
|
next_back: Some(last),
|
|
vec,
|
|
}
|
|
}
|
|
}
|