use wgpu::*; use crate::GlyphAtlas; 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. /// /// One array rather than a texture per page because a layer index is ordinary /// Vulkan 1.0 / GLES sampling, where a `binding_array` would need /// `VK_EXT_descriptor_indexing`, which a real share of Android GPUs lack. pub struct GpuPages { device: Device, queue: Queue, texture: Texture, group: BindGroup, } impl GpuPages { 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(), group: atlas_group(device, layout, &texture, sampler), texture, } } 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 { texture: &self.texture, mip_level: 0, origin: Origin3d { x: upload.rect.x, y: upload.rect.y, z: upload.layer, }, aspect: TextureAspect::All, }; write_region(&self.queue, dst, page, upload.rect); } } pub fn group(&self) -> &BindGroup { &self.group } /// Doubles until `needed` fits and copies the old layers across GPU side. /// 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 { layers *= 2; } let texture = create_array(&self.device, layers); let mut encoder = self .device .create_command_encoder(&CommandEncoderDescriptor { label: Some("atlas grow"), }); encoder.copy_texture_to_texture( self.texture.as_image_copy(), texture.as_image_copy(), Extent3d { width: PAGE, height: PAGE, depth_or_array_layers: old, }, ); self.queue.submit(std::iter::once(encoder.finish())); self.group = atlas_group(&self.device, layout, &texture, sampler); self.texture = texture; } } 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 { device.create_texture(&TextureDescriptor { label: Some("glyph atlas"), size: Extent3d { width: PAGE, height: PAGE, depth_or_array_layers: layers, }, mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, format: TextureFormat::Rgba8Unorm, usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST | TextureUsages::COPY_SRC, view_formats: &[], }) }