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>
53 lines
1.5 KiB
Rust
53 lines
1.5 KiB
Rust
use std::marker::PhantomData;
|
|
|
|
use bytemuck::Pod;
|
|
use wgpu::*;
|
|
|
|
pub struct ArrBuf<T: Pod> {
|
|
label: &'static str,
|
|
usage: BufferUsages,
|
|
pub buffer: Buffer,
|
|
len: usize,
|
|
_pd: PhantomData<T>,
|
|
}
|
|
|
|
impl<T: Pod> ArrBuf<T> {
|
|
pub fn new(device: &Device, usage: BufferUsages, label: &'static str) -> Self {
|
|
Self {
|
|
label,
|
|
usage,
|
|
buffer: Self::init_buf(device, 0, usage, label),
|
|
len: 0,
|
|
_pd: PhantomData,
|
|
}
|
|
}
|
|
/// Returns whether the `Buffer` was recreated, which stales any cached
|
|
/// `BindGroup` holding it.
|
|
pub fn update(&mut self, device: &Device, queue: &Queue, data: &[T]) -> bool {
|
|
let resized = self.len != data.len();
|
|
if resized {
|
|
self.len = data.len();
|
|
self.buffer =
|
|
Self::init_buf(device, std::mem::size_of_val(data), self.usage, self.label);
|
|
}
|
|
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(data));
|
|
resized
|
|
}
|
|
pub fn len(&self) -> usize {
|
|
self.len
|
|
}
|
|
fn init_buf(device: &Device, size: usize, usage: BufferUsages, label: &'static str) -> Buffer {
|
|
let mut size = size as u64;
|
|
if usage.contains(BufferUsages::STORAGE) {
|
|
// A binding cannot be empty or under the layout's minimum.
|
|
size = size.max(std::mem::size_of::<T>() as u64);
|
|
}
|
|
device.create_buffer(&BufferDescriptor {
|
|
label: Some(label),
|
|
size,
|
|
mapped_at_creation: false,
|
|
usage,
|
|
})
|
|
}
|
|
}
|