Give the atlas to the primitive that samples it, not to every shader

`Primitive::SAMPLES` says what a primitive samples and how often it has to be
bound: `Atlas` once for a list, `Image` for one instance alone. Group 2 is
that, whichever it is, so a rect's pipeline has no texture and no sampler in
its layout and the prelude hands out neither.

The two differ only in the view dimension and in what binds them, so they
share `sampled_layout` and `sampled_group`; `GpuPages` owns its bind group
again and rebuilds it when the array grows.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
irisandClaude Opus 5 committed 2026-09-13 17:43:55 -04:00
1 parent c0fcc0345c
commit 79dcc156c9
7 files changed
+174 -124

No files matched your search

+29 -17
View File
@@ -2,7 +2,10 @@ use wgpu::*;
use crate::GlyphAtlas;
use super::{atlas::PAGE, texture::write_region};
use super::{
atlas::PAGE,
texture::{sampled_group, write_region},
};
/// The glyph atlas on the GPU: one array texture whose layers are the pages
/// `GlyphAtlas` packs.
@@ -14,25 +17,28 @@ pub struct GpuPages {
device: Device,
queue: Queue,
texture: Texture,
view: TextureView,
group: BindGroup,
}
impl GpuPages {
pub fn new(device: &Device, queue: &Queue) -> Self {
pub fn new(
device: &Device,
queue: &Queue,
layout: &BindGroupLayout,
sampler: &Sampler,
) -> Self {
let texture = create_array(device, 1);
Self {
device: device.clone(),
queue: queue.clone(),
view: array_view(&texture),
group: atlas_group(device, layout, &texture, sampler),
texture,
}
}
/// Returns whether the array was replaced, which stales the view.
pub fn update(&mut self, atlas: &mut GlyphAtlas) -> bool {
let grew = atlas.page_count() > self.texture.depth_or_array_layers();
if grew {
self.grow(atlas.page_count());
pub fn update(&mut self, atlas: &mut GlyphAtlas, layout: &BindGroupLayout, sampler: &Sampler) {
if atlas.page_count() > self.texture.depth_or_array_layers() {
self.grow(atlas.page_count(), layout, sampler);
}
for (upload, page) in atlas.uploads() {
let dst = TexelCopyTextureInfo {
@@ -47,15 +53,15 @@ impl GpuPages {
};
write_region(&self.queue, dst, page, upload.rect);
}
grew
}
pub fn view(&self) -> &TextureView {
&self.view
pub fn group(&self) -> &BindGroup {
&self.group
}
/// Doubles until `needed` fits and copies the old layers across GPU side.
fn grow(&mut self, needed: u32) {
/// The new texture stales the group, so that is rebuilt here.
fn grow(&mut self, needed: u32, layout: &BindGroupLayout, sampler: &Sampler) {
let old = self.texture.depth_or_array_layers();
let mut layers = old;
while layers < needed {
@@ -77,16 +83,22 @@ impl GpuPages {
},
);
self.queue.submit(std::iter::once(encoder.finish()));
self.view = array_view(&texture);
self.group = atlas_group(&self.device, layout, &texture, sampler);
self.texture = texture;
}
}
fn array_view(texture: &Texture) -> TextureView {
texture.create_view(&TextureViewDescriptor {
fn atlas_group(
device: &Device,
layout: &BindGroupLayout,
texture: &Texture,
sampler: &Sampler,
) -> BindGroup {
let view = texture.create_view(&TextureViewDescriptor {
dimension: Some(TextureViewDimension::D2Array),
..Default::default()
})
});
sampled_group(device, layout, &view, sampler, "ui atlas")
}
fn create_array(device: &Device, layers: u32) -> Texture {