1use super::{BladeBelt, BladeBeltDescriptor};
2use crate::{
3 AtlasKey, AtlasTextureId, AtlasTextureKind, AtlasTile, Bounds, DevicePixels, PlatformAtlas,
4 Point, Size,
5};
6use anyhow::Result;
7use blade_graphics as gpu;
8use collections::FxHashMap;
9use etagere::BucketedAtlasAllocator;
10use parking_lot::Mutex;
11use std::{borrow::Cow, ops, sync::Arc};
12
13pub(crate) const PATH_TEXTURE_FORMAT: gpu::TextureFormat = gpu::TextureFormat::R16Float;
14
15pub(crate) struct BladeAtlas(Mutex<BladeAtlasState>);
16
17struct PendingUpload {
18 id: AtlasTextureId,
19 bounds: Bounds<DevicePixels>,
20 data: gpu::BufferPiece,
21}
22
23struct BladeAtlasState {
24 gpu: Arc<gpu::Context>,
25 upload_belt: BladeBelt,
26 storage: BladeAtlasStorage,
27 tiles_by_key: FxHashMap<AtlasKey, AtlasTile>,
28 initializations: Vec<AtlasTextureId>,
29 uploads: Vec<PendingUpload>,
30}
31
32impl BladeAtlasState {
33 fn destroy(&mut self) {
34 self.storage.destroy(&self.gpu);
35 self.upload_belt.destroy(&self.gpu);
36 }
37}
38
39pub struct BladeTextureInfo {
40 pub size: gpu::Extent,
41 pub raw_view: gpu::TextureView,
42}
43
44impl BladeAtlas {
45 pub(crate) fn new(gpu: &Arc<gpu::Context>) -> Self {
46 BladeAtlas(Mutex::new(BladeAtlasState {
47 gpu: Arc::clone(gpu),
48 upload_belt: BladeBelt::new(BladeBeltDescriptor {
49 memory: gpu::Memory::Upload,
50 min_chunk_size: 0x10000,
51 alignment: 64, // Vulkan `optimalBufferCopyOffsetAlignment` on Intel XE
52 }),
53 storage: BladeAtlasStorage::default(),
54 tiles_by_key: Default::default(),
55 initializations: Vec::new(),
56 uploads: Vec::new(),
57 }))
58 }
59
60 pub(crate) fn destroy(&self) {
61 self.0.lock().destroy();
62 }
63
64 pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) {
65 let mut lock = self.0.lock();
66 let textures = &mut lock.storage[texture_kind];
67 for texture in textures {
68 texture.clear();
69 }
70 }
71
72 pub fn allocate(&self, size: Size<DevicePixels>, texture_kind: AtlasTextureKind) -> AtlasTile {
73 let mut lock = self.0.lock();
74 lock.allocate(size, texture_kind)
75 }
76
77 pub fn before_frame(&self, gpu_encoder: &mut gpu::CommandEncoder) {
78 let mut lock = self.0.lock();
79 lock.flush(gpu_encoder);
80 }
81
82 pub fn after_frame(&self, sync_point: &gpu::SyncPoint) {
83 let mut lock = self.0.lock();
84 lock.upload_belt.flush(sync_point);
85 }
86
87 pub fn get_texture_info(&self, id: AtlasTextureId) -> BladeTextureInfo {
88 let lock = self.0.lock();
89 let texture = &lock.storage[id];
90 let size = texture.allocator.size();
91 BladeTextureInfo {
92 size: gpu::Extent {
93 width: size.width as u32,
94 height: size.height as u32,
95 depth: 1,
96 },
97 raw_view: texture.raw_view,
98 }
99 }
100}
101
102impl PlatformAtlas for BladeAtlas {
103 fn get_or_insert_with<'a>(
104 &self,
105 key: &AtlasKey,
106 build: &mut dyn FnMut() -> Result<(Size<DevicePixels>, Cow<'a, [u8]>)>,
107 ) -> Result<AtlasTile> {
108 let mut lock = self.0.lock();
109 if let Some(tile) = lock.tiles_by_key.get(key) {
110 Ok(tile.clone())
111 } else {
112 let (size, bytes) = build()?;
113 let tile = lock.allocate(size, key.texture_kind());
114 lock.upload_texture(tile.texture_id, tile.bounds, &bytes);
115 lock.tiles_by_key.insert(key.clone(), tile.clone());
116 Ok(tile)
117 }
118 }
119}
120
121impl BladeAtlasState {
122 fn allocate(&mut self, size: Size<DevicePixels>, texture_kind: AtlasTextureKind) -> AtlasTile {
123 let textures = &mut self.storage[texture_kind];
124 textures
125 .iter_mut()
126 .rev()
127 .find_map(|texture| texture.allocate(size))
128 .unwrap_or_else(|| {
129 let texture = self.push_texture(size, texture_kind);
130 texture.allocate(size).unwrap()
131 })
132 }
133
134 fn push_texture(
135 &mut self,
136 min_size: Size<DevicePixels>,
137 kind: AtlasTextureKind,
138 ) -> &mut BladeAtlasTexture {
139 const DEFAULT_ATLAS_SIZE: Size<DevicePixels> = Size {
140 width: DevicePixels(1024),
141 height: DevicePixels(1024),
142 };
143
144 let size = min_size.max(&DEFAULT_ATLAS_SIZE);
145 let format;
146 let usage;
147 match kind {
148 AtlasTextureKind::Monochrome => {
149 format = gpu::TextureFormat::R8Unorm;
150 usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE;
151 }
152 AtlasTextureKind::Polychrome => {
153 format = gpu::TextureFormat::Bgra8Unorm;
154 usage = gpu::TextureUsage::COPY | gpu::TextureUsage::RESOURCE;
155 }
156 AtlasTextureKind::Path => {
157 format = PATH_TEXTURE_FORMAT;
158 usage = gpu::TextureUsage::COPY
159 | gpu::TextureUsage::RESOURCE
160 | gpu::TextureUsage::TARGET;
161 }
162 }
163
164 let raw = self.gpu.create_texture(gpu::TextureDesc {
165 name: "atlas",
166 format,
167 size: gpu::Extent {
168 width: size.width.into(),
169 height: size.height.into(),
170 depth: 1,
171 },
172 array_layer_count: 1,
173 mip_level_count: 1,
174 dimension: gpu::TextureDimension::D2,
175 usage,
176 });
177 let raw_view = self.gpu.create_texture_view(gpu::TextureViewDesc {
178 name: "",
179 texture: raw,
180 format,
181 dimension: gpu::ViewDimension::D2,
182 subresources: &Default::default(),
183 });
184
185 let textures = &mut self.storage[kind];
186 let atlas_texture = BladeAtlasTexture {
187 id: AtlasTextureId {
188 index: textures.len() as u32,
189 kind,
190 },
191 allocator: etagere::BucketedAtlasAllocator::new(size.into()),
192 format,
193 raw,
194 raw_view,
195 };
196
197 self.initializations.push(atlas_texture.id);
198 textures.push(atlas_texture);
199 textures.last_mut().unwrap()
200 }
201
202 fn upload_texture(&mut self, id: AtlasTextureId, bounds: Bounds<DevicePixels>, bytes: &[u8]) {
203 let data = self.upload_belt.alloc_data(bytes, &self.gpu);
204 self.uploads.push(PendingUpload { id, bounds, data });
205 }
206
207 fn flush(&mut self, encoder: &mut gpu::CommandEncoder) {
208 for id in self.initializations.drain(..) {
209 let texture = &self.storage[id];
210 encoder.init_texture(texture.raw);
211 }
212
213 let mut transfers = encoder.transfer();
214 for upload in self.uploads.drain(..) {
215 let texture = &self.storage[upload.id];
216 transfers.copy_buffer_to_texture(
217 upload.data,
218 upload.bounds.size.width.to_bytes(texture.bytes_per_pixel()),
219 gpu::TexturePiece {
220 texture: texture.raw,
221 mip_level: 0,
222 array_layer: 0,
223 origin: [
224 upload.bounds.origin.x.into(),
225 upload.bounds.origin.y.into(),
226 0,
227 ],
228 },
229 gpu::Extent {
230 width: upload.bounds.size.width.into(),
231 height: upload.bounds.size.height.into(),
232 depth: 1,
233 },
234 );
235 }
236 }
237}
238
239#[derive(Default)]
240struct BladeAtlasStorage {
241 monochrome_textures: Vec<BladeAtlasTexture>,
242 polychrome_textures: Vec<BladeAtlasTexture>,
243 path_textures: Vec<BladeAtlasTexture>,
244}
245
246impl ops::Index<AtlasTextureKind> for BladeAtlasStorage {
247 type Output = Vec<BladeAtlasTexture>;
248 fn index(&self, kind: AtlasTextureKind) -> &Self::Output {
249 match kind {
250 crate::AtlasTextureKind::Monochrome => &self.monochrome_textures,
251 crate::AtlasTextureKind::Polychrome => &self.polychrome_textures,
252 crate::AtlasTextureKind::Path => &self.path_textures,
253 }
254 }
255}
256
257impl ops::IndexMut<AtlasTextureKind> for BladeAtlasStorage {
258 fn index_mut(&mut self, kind: AtlasTextureKind) -> &mut Self::Output {
259 match kind {
260 crate::AtlasTextureKind::Monochrome => &mut self.monochrome_textures,
261 crate::AtlasTextureKind::Polychrome => &mut self.polychrome_textures,
262 crate::AtlasTextureKind::Path => &mut self.path_textures,
263 }
264 }
265}
266
267impl ops::Index<AtlasTextureId> for BladeAtlasStorage {
268 type Output = BladeAtlasTexture;
269 fn index(&self, id: AtlasTextureId) -> &Self::Output {
270 let textures = match id.kind {
271 crate::AtlasTextureKind::Monochrome => &self.monochrome_textures,
272 crate::AtlasTextureKind::Polychrome => &self.polychrome_textures,
273 crate::AtlasTextureKind::Path => &self.path_textures,
274 };
275 &textures[id.index as usize]
276 }
277}
278
279impl BladeAtlasStorage {
280 fn destroy(&mut self, gpu: &gpu::Context) {
281 for mut texture in self.monochrome_textures.drain(..) {
282 texture.destroy(gpu);
283 }
284 for mut texture in self.polychrome_textures.drain(..) {
285 texture.destroy(gpu);
286 }
287 for mut texture in self.path_textures.drain(..) {
288 texture.destroy(gpu);
289 }
290 }
291}
292
293struct BladeAtlasTexture {
294 id: AtlasTextureId,
295 allocator: BucketedAtlasAllocator,
296 raw: gpu::Texture,
297 raw_view: gpu::TextureView,
298 format: gpu::TextureFormat,
299}
300
301impl BladeAtlasTexture {
302 fn clear(&mut self) {
303 self.allocator.clear();
304 }
305
306 fn allocate(&mut self, size: Size<DevicePixels>) -> Option<AtlasTile> {
307 let allocation = self.allocator.allocate(size.into())?;
308 let tile = AtlasTile {
309 texture_id: self.id,
310 tile_id: allocation.id.into(),
311 padding: 0,
312 bounds: Bounds {
313 origin: allocation.rectangle.min.into(),
314 size,
315 },
316 };
317 Some(tile)
318 }
319
320 fn destroy(&mut self, gpu: &gpu::Context) {
321 gpu.destroy_texture(self.raw);
322 gpu.destroy_texture_view(self.raw_view);
323 }
324
325 fn bytes_per_pixel(&self) -> u8 {
326 self.format.block_info().size
327 }
328}
329
330impl From<Size<DevicePixels>> for etagere::Size {
331 fn from(size: Size<DevicePixels>) -> Self {
332 etagere::Size::new(size.width.into(), size.height.into())
333 }
334}
335
336impl From<etagere::Point> for Point<DevicePixels> {
337 fn from(value: etagere::Point) -> Self {
338 Point {
339 x: DevicePixels::from(value.x),
340 y: DevicePixels::from(value.y),
341 }
342 }
343}
344
345impl From<etagere::Size> for Size<DevicePixels> {
346 fn from(size: etagere::Size) -> Self {
347 Size {
348 width: DevicePixels::from(size.width),
349 height: DevicePixels::from(size.height),
350 }
351 }
352}
353
354impl From<etagere::Rectangle> for Bounds<DevicePixels> {
355 fn from(rectangle: etagere::Rectangle) -> Self {
356 Bounds {
357 origin: rectangle.min.into(),
358 size: rectangle.size().into(),
359 }
360 }
361}