From 861b467b95be55db3a42182b77dba944869bf49f Mon Sep 17 00:00:00 2001 From: Micha White Date: Mon, 13 Feb 2023 00:24:07 -0500 Subject: Rename the subdirectories --- alligator_resources/src/lib.rs | 13 -- alligator_resources/src/texture.rs | 394 ------------------------------------- 2 files changed, 407 deletions(-) delete mode 100644 alligator_resources/src/lib.rs delete mode 100644 alligator_resources/src/texture.rs (limited to 'alligator_resources/src') diff --git a/alligator_resources/src/lib.rs b/alligator_resources/src/lib.rs deleted file mode 100644 index 9cbbba0..0000000 --- a/alligator_resources/src/lib.rs +++ /dev/null @@ -1,13 +0,0 @@ -#![feature(new_uninit, let_chains)] -#![warn(clippy::nursery, clippy::pedantic)] -#![allow(clippy::module_name_repetitions)] - -pub mod texture; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub enum Priority { - Unnecessary, - Possible(u8), - Eventual(u8), - Urgent, -} diff --git a/alligator_resources/src/texture.rs b/alligator_resources/src/texture.rs deleted file mode 100644 index 3a5bf3e..0000000 --- a/alligator_resources/src/texture.rs +++ /dev/null @@ -1,394 +0,0 @@ -use std::cmp::Reverse; -use std::mem; -use std::path::Path; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::Arc; - -use dashmap::DashMap; -use image::ImageBuffer; -use parking_lot::Mutex; -use thiserror::Error; - -use crate::Priority; - -/// The next texture ID -static NEXT_TEXTURE_ID: AtomicUsize = AtomicUsize::new(0); - -/// A unique identifier for a texture -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct TextureId(usize); - -impl TextureId { - fn new() -> Self { - Self(NEXT_TEXTURE_ID.fetch_add(1, Ordering::Relaxed)) - } -} - -/// These are the formats supported by the renderer. -// TODO make these feature-enabled -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -#[non_exhaustive] -pub enum ImageFormat { - Bmp, - Ico, - Farbfeld, -} - -impl From for image::ImageFormat { - fn from(format: ImageFormat) -> Self { - match format { - ImageFormat::Bmp => Self::Bmp, - ImageFormat::Ico => Self::Ico, - ImageFormat::Farbfeld => Self::Farbfeld, - } - } -} - -#[derive(Debug, Error)] -#[error("{}", .0)] -pub struct DecodingError(#[from] image::error::DecodingError); - -#[allow(clippy::missing_const_for_fn)] -fn convert_image_decoding(e: image::ImageError) -> DecodingError { - if let image::ImageError::Decoding(de) = e { - de.into() - } else { - unreachable!("No other error should be possible") - } -} - -#[derive(Debug, Error)] -pub enum LoadError { - #[error("{}", .0)] - Decoding(#[from] DecodingError), - #[error("{}", .0)] - Io(#[from] std::io::Error), -} - -fn convert_image_load_error(e: image::ImageError) -> LoadError { - match e { - image::ImageError::Decoding(de) => LoadError::Decoding(de.into()), - image::ImageError::IoError(ioe) => ioe.into(), - _ => unreachable!("No other error should be possible"), - } -} - -pub type Rgba16Texture = image::ImageBuffer, Box<[u16]>>; - -fn vec_image_to_box(vec_image: image::ImageBuffer, Vec>) -> Rgba16Texture { - let width = vec_image.width(); - let height = vec_image.height(); - let buf = vec_image.into_raw().into_boxed_slice(); - ImageBuffer::from_raw(width, height, buf).expect("image buffer is too small") -} - -/// Get the size, in bytes, of the texture -#[allow(clippy::missing_const_for_fn)] -fn texture_size(image: &Rgba16Texture) -> usize { - image.len() * mem::size_of::>() -} - -/// A texture from disk -struct TextureFile { - path: Box, - texture: Option>, -} - -impl TextureFile { - /// This doesn't load the texture - #[allow(clippy::missing_const_for_fn)] - fn new(path: impl AsRef) -> Self { - Self { - path: path.as_ref().into(), - texture: None, - } - } - - const fn is_loaded(&self) -> bool { - self.texture.is_some() - } - - fn load(&mut self) -> Result<&Rgba16Texture, LoadError> { - if self.texture.is_none() { - log::warn!("{} was not pre-loaded", self.path.to_string_lossy()); - let texture = image::open(&self.path).map_err(convert_image_load_error)?; - let texture = texture.to_rgba16(); - let texture = Arc::new(vec_image_to_box(texture)); - self.texture = Some(texture); - } - - Ok(self.texture.as_ref().expect("the texture wasn't loaded")) - } - - fn loaded_texture(&self) -> Option<&Rgba16Texture> { - self.texture.as_deref() - } - - fn is_used(&self) -> bool { - let Some(arc) = &self.texture else { return false }; - Arc::strong_count(arc) > 1 - } - - /// Unloads the texture from memory if it isn't being used - fn unload(&mut self) { - if !self.is_used() { - self.texture = None; - } - } - - /// The amount of heap memory used, in bytes. This returns 0 if the texture - /// hasn't been loaded yet. - fn allocated_size(&self) -> usize { - self.texture.as_ref().map_or(0, |t| texture_size(t)) - } -} - -enum TextureBuffer { - Memory(Arc), - Disk(TextureFile), -} - -struct Texture { - priority: Priority, - queued_priority: Arc>>, - buffer: TextureBuffer, -} - -impl Texture { - fn from_buffer(texture: Rgba16Texture) -> Self { - Self { - priority: Priority::Urgent, // indicates that it can't be unloaded - queued_priority: Arc::new(Mutex::new(None)), - buffer: TextureBuffer::Memory(Arc::new(texture)), - } - } - - fn from_path(path: impl AsRef, priority: Priority) -> Self { - Self { - priority, - queued_priority: Arc::new(Mutex::new(None)), - buffer: TextureBuffer::Disk(TextureFile::new(path)), - } - } - - const fn priority(&self) -> Priority { - self.priority - } - - fn _set_priority(buffer: &TextureBuffer, src: &mut Priority, priority: Priority) -> bool { - // memory textures and textures in use should always be urgent - if let TextureBuffer::Disk(disk) = buffer && !disk.is_used() { - *src = priority; - true - } else { - false - } - } - - fn unqueue_priority(&mut self) { - let mut queued_priority = self.queued_priority.lock(); - let unqueued_priority = queued_priority.unwrap_or(Priority::Unnecessary); - - if Self::_set_priority(&self.buffer, &mut self.priority, unqueued_priority) { - *queued_priority = None; - } - } - - fn set_priority(&mut self, priority: Priority) { - Self::_set_priority(&self.buffer, &mut self.priority, priority); - } - - fn load_texture(&mut self) -> Result<&Rgba16Texture, LoadError> { - match &mut self.buffer { - TextureBuffer::Memory(ref texture) => Ok(texture), - TextureBuffer::Disk(file) => file.load(), - } - } - - /// If the texture is loaded, return it. - fn loaded_texture(&self) -> Option<&Rgba16Texture> { - match &self.buffer { - TextureBuffer::Memory(ref texture) => Some(texture), - TextureBuffer::Disk(file) => file.loaded_texture(), - } - } - - fn unload(&mut self) { - if let TextureBuffer::Disk(file) = &mut self.buffer { - file.unload(); - } - } - - /// The amount of heap memory used for the texture, if any - fn allocated_size(&self) -> usize { - match &self.buffer { - TextureBuffer::Memory(texture) => texture_size(texture), - TextureBuffer::Disk(file) => file.allocated_size(), - } - } - - const fn is_loaded(&self) -> bool { - match &self.buffer { - TextureBuffer::Memory(_) => true, - TextureBuffer::Disk(file) => file.is_loaded(), - } - } -} - -pub struct TextureManager { - textures: DashMap, - max_size: usize, - needs_atlas_update: AtomicBool, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct TextureManagerConfig { - /// The initial capacity of the texture manager. This defaults to 500 textures. - pub initial_capacity: usize, - /// The maximum amount of heap usage acceptable. Defaults to 10 MiB. - pub max_size: usize, -} - -impl Default for TextureManagerConfig { - fn default() -> Self { - Self { - initial_capacity: 500, - max_size: 10 * 1024 * 1024, // 10 MiB - } - } -} - -impl TextureManager { - /// Create a new `TextureManager` with the given config options. - #[must_use] - pub fn new(config: &TextureManagerConfig) -> Self { - let textures = DashMap::with_capacity(config.initial_capacity); - - Self { - textures, - max_size: config.max_size, - needs_atlas_update: AtomicBool::new(false), - } - } - - /// Load textures into memory that will be needed soon. Unload unnecessary textures - pub fn cache_files(&self) { - let mut textures: Vec<_> = self - .textures - .iter_mut() - .map(|mut t| { - t.value_mut().unqueue_priority(); - t - }) - .collect(); - textures.sort_by_key(|t2| Reverse(t2.priority())); - - let max_size = self.max_size; - let mut total_size = 0; - - for texture in &mut textures { - drop(texture.load_texture()); - total_size += texture.allocated_size(); - if total_size > max_size && texture.priority() != Priority::Urgent { - texture.unload(); - return; - } - } - } - - /// Loads a texture from memory in the given format. - /// - /// # Errors - /// - /// This returns `Expected(DecodingError)` if the given buffer was invalid - /// for the given format. - pub fn load_from_memory( - &self, - buf: &[u8], - format: ImageFormat, - ) -> Result { - let id = TextureId::new(); - let texture = image::load_from_memory_with_format(buf, format.into()); - let texture = texture.map_err(convert_image_decoding)?; - let texture = texture.into_rgba16(); - let texture = vec_image_to_box(texture); - let texture = Texture::from_buffer(texture); - - self.textures.insert(id, texture); - self.needs_atlas_update.store(true, Ordering::Release); - - Ok(id) - } - - /// Loads a texture from disk. - /// - /// # Errors - /// - /// This returns an error if `priority` is set to [`Priority::Urgent`] but - /// there was an error in loading the file to a texture. - pub fn load_from_file( - &self, - path: impl AsRef, - priority: Priority, - ) -> Result { - let id = TextureId::new(); - let mut texture = Texture::from_path(path, priority); - - if priority == Priority::Urgent { - match texture.load_texture() { - Ok(_) => { - self.textures.insert(id, texture); - self.needs_atlas_update.store(true, Ordering::Release); - } - Err(e) => { - self.textures.insert(id, texture); - return Err(e); - } - } - } else { - self.textures.insert(id, texture); - } - - Ok(id) - } - - /// Loads a texture from disk. - /// - /// # Errors - /// - /// This returns an error if `priority` is set to [`Priority::Urgent`] but - /// there was an error in loading the file to a texture. - pub fn set_priority(&self, id: TextureId, priority: Priority) -> Result<(), LoadError> { - let mut texture = self.textures.get_mut(&id).expect("invalid texture id"); - texture.set_priority(priority); - - if !texture.is_loaded() && priority == Priority::Urgent { - let mut texture = self.textures.get_mut(&id).expect("invalid texture id"); - texture.load_texture()?; - self.needs_atlas_update.store(true, Ordering::Release); - } - - Ok(()) - } - - /// This returns `true` if a texture has been set to have an urgent - /// priority since the last time this function was called. - pub fn needs_atlas_update(&self) -> bool { - self.needs_atlas_update.fetch_and(false, Ordering::AcqRel) - } - - /// Load a texture into memory, if it hasn't been already. Then return a - /// copy of the texture. - /// - /// # Errors - /// - /// This returns an error if an error occurs in loading the texture from - /// disk, such as the file not existing, or not being a valid texture. - pub fn load_texture(&self, id: TextureId) -> Result { - self.textures - .get_mut(&id) - .expect("the TextureId was invalid") - .load_texture() - .cloned() - } -} -- cgit v1.2.3