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