summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--alligator_render/Cargo.toml2
-rw-r--r--alligator_render/examples/black.rs6
-rw-r--r--alligator_render/examples/bmp.rs47
-rw-r--r--alligator_render/examples/bunnymark.rs31
-rw-r--r--alligator_render/src/lib.rs2
-rw-r--r--alligator_render/src/renderer.rs4
-rw-r--r--alligator_render/src/texture.rs85
-rw-r--r--alligator_resources/src/texture.rs136
8 files changed, 82 insertions, 231 deletions
diff --git a/alligator_render/Cargo.toml b/alligator_render/Cargo.toml
index a613dcd..8917a2f 100644
--- a/alligator_render/Cargo.toml
+++ b/alligator_render/Cargo.toml
@@ -19,7 +19,7 @@ cgmath = "0.18"
pollster = "0.2"
log = "0.4"
parking_lot = "0.12"
-texture_packer = "0.25"
+texture_packer = { git="https://github.com/botahamec/piston_texture_packer", branch="u16" }
tracy-client = { version = "0.15", optional = true }
dhat = { version = "0.3", optional = true }
diff --git a/alligator_render/examples/black.rs b/alligator_render/examples/black.rs
index 198eef2..655cbde 100644
--- a/alligator_render/examples/black.rs
+++ b/alligator_render/examples/black.rs
@@ -1,5 +1,7 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+use std::sync::Arc;
+
use alligator_render::{RenderWindowConfig, Renderer};
use alligator_resources::texture::{TextureManager, TextureManagerConfig};
@@ -19,11 +21,9 @@ fn main() {
let texture_config = TextureManagerConfig {
initial_capacity: 0,
max_size: 0,
- atlas_width: 1,
- atlas_height: 1,
};
- let texture_manager = TextureManager::new(&texture_config);
+ let texture_manager = Arc::new(TextureManager::new(&texture_config));
let renderer = Renderer::new(&render_config, texture_manager).unwrap();
println!("Startup time: {:?}", start.elapsed());
diff --git a/alligator_render/examples/bmp.rs b/alligator_render/examples/bmp.rs
index 0bad037..9d864d0 100644
--- a/alligator_render/examples/bmp.rs
+++ b/alligator_render/examples/bmp.rs
@@ -1,9 +1,9 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
-use std::num::NonZeroU32;
+use std::{num::NonZeroU32, sync::Arc};
-use alligator_render::{ImageFormat, Instance, RenderWindowConfig, Renderer};
-use alligator_resources::texture::{TextureManager, TextureManagerConfig};
+use alligator_render::{Instance, RenderWindowConfig, Renderer};
+use alligator_resources::texture::{ImageFormat, TextureManager, TextureManagerConfig};
#[profiling::function]
fn update(renderer: &mut Renderer) {
@@ -26,23 +26,21 @@ fn main() {
let texture_config = TextureManagerConfig {
initial_capacity: 3,
max_size: 3_000_000,
- atlas_height: 150,
- atlas_width: 150,
};
- let texture_manager = TextureManager::new(&texture_config);
- let mut renderer = Renderer::new(&render_config, texture_manager).unwrap();
+ let texture_manager = Arc::new(TextureManager::new(&texture_config));
+ let mut renderer = Renderer::new(&render_config, texture_manager.clone()).unwrap();
// render the alligator
let gator = include_bytes!("res/gator.ff");
- let gator_id = renderer
- .textures_mut()
+ let gator_id = texture_manager
.load_from_memory(gator, ImageFormat::Farbfeld)
.unwrap();
- let gator_width = renderer.textures().texture_width(gator_id).unwrap();
- let gator_height = renderer.textures().texture_height(gator_id).unwrap();
- let gator_x = renderer.textures().texture_x(gator_id).unwrap();
- let gator_y = renderer.textures().texture_y(gator_id).unwrap();
+ renderer.textures_mut().load_texture(gator_id).unwrap();
+ let gator_width = renderer.textures_mut().texture_width(gator_id).unwrap();
+ let gator_height = renderer.textures_mut().texture_height(gator_id).unwrap();
+ let gator_x = renderer.textures_mut().texture_x(gator_id).unwrap();
+ let gator_y = renderer.textures_mut().texture_y(gator_id).unwrap();
renderer.instances_mut().push_instance(Instance {
position: [-0.5, 0.5],
@@ -55,14 +53,14 @@ fn main() {
// render the ghost
let icon = include_bytes!("res/ghost.ico");
- let icon_id = renderer
- .textures_mut()
+ let icon_id = texture_manager
.load_from_memory(icon, ImageFormat::Ico)
.unwrap();
- let icon_width = renderer.textures().texture_width(icon_id).unwrap();
- let icon_height = renderer.textures().texture_height(icon_id).unwrap();
- let icon_x = renderer.textures().texture_x(icon_id).unwrap();
- let icon_y = renderer.textures().texture_y(icon_id).unwrap();
+ renderer.textures_mut().load_texture(icon_id).unwrap();
+ let icon_width = renderer.textures_mut().texture_width(icon_id).unwrap();
+ let icon_height = renderer.textures_mut().texture_height(icon_id).unwrap();
+ let icon_x = renderer.textures_mut().texture_x(icon_id).unwrap();
+ let icon_y = renderer.textures_mut().texture_y(icon_id).unwrap();
renderer.instances_mut().push_instance(Instance {
position: [0.5, 0.5],
@@ -76,14 +74,13 @@ fn main() {
// render the bitmap alligator
let gator = include_bytes!("res/gator.bmp");
- let gator_id = renderer
- .textures_mut()
+ let gator_id = texture_manager
.load_from_memory(gator, ImageFormat::Bmp)
.unwrap();
- let gator_width = renderer.textures().texture_width(gator_id).unwrap();
- let gator_height = renderer.textures().texture_height(gator_id).unwrap();
- let gator_x = renderer.textures().texture_x(gator_id).unwrap();
- let gator_y = renderer.textures().texture_y(gator_id).unwrap();
+ let gator_width = renderer.textures_mut().texture_width(gator_id).unwrap();
+ let gator_height = renderer.textures_mut().texture_height(gator_id).unwrap();
+ let gator_x = renderer.textures_mut().texture_x(gator_id).unwrap();
+ let gator_y = renderer.textures_mut().texture_y(gator_id).unwrap();
renderer.instances_mut().push_instance(Instance {
position: [0.0, -0.5],
diff --git a/alligator_render/examples/bunnymark.rs b/alligator_render/examples/bunnymark.rs
index ab38b5e..f9aed1a 100644
--- a/alligator_render/examples/bunnymark.rs
+++ b/alligator_render/examples/bunnymark.rs
@@ -1,9 +1,7 @@
-use std::{num::NonZeroU32, time::Instant};
+use std::{num::NonZeroU32, sync::Arc, time::Instant};
-use alligator_render::{
- ImageFormat, Instance, InstanceId, RenderWindowConfig, Renderer, TextureId,
-};
-use alligator_resources::texture::{TextureManager, TextureManagerConfig};
+use alligator_render::{Instance, InstanceId, RenderWindowConfig, Renderer};
+use alligator_resources::texture::{ImageFormat, TextureId, TextureManager, TextureManagerConfig};
fn xorshift_plus(seed: &mut [u64; 2]) -> u64 {
let mut t = seed[0];
@@ -88,10 +86,16 @@ impl State {
}
for _ in 0..=(fps as u64 * 50) {
- let texture_x = renderer.textures().texture_x(self.texture_id).unwrap();
- let texture_y = renderer.textures().texture_x(self.texture_id).unwrap();
- let texture_height = renderer.textures().texture_height(self.texture_id).unwrap();
- let texture_width = renderer.textures().texture_width(self.texture_id).unwrap();
+ let texture_x = renderer.textures_mut().texture_x(self.texture_id).unwrap();
+ let texture_y = renderer.textures_mut().texture_x(self.texture_id).unwrap();
+ let texture_height = renderer
+ .textures_mut()
+ .texture_height(self.texture_id)
+ .unwrap();
+ let texture_width = renderer
+ .textures_mut()
+ .texture_width(self.texture_id)
+ .unwrap();
let instance_id = renderer.instances_mut().push_instance(Instance {
texture_coordinates: [texture_x, texture_y],
texture_size: [texture_width, texture_height],
@@ -137,15 +141,12 @@ fn main() {
let texture_config = TextureManagerConfig {
initial_capacity: 1,
max_size: 10_000,
- atlas_width: 100,
- atlas_height: 100,
};
let bunny = include_bytes!("res/bunny.ff");
- let texture_manager = TextureManager::new(&texture_config);
- let mut renderer = Renderer::new(&render_config, texture_manager).unwrap();
- let texture_id = renderer
- .textures_mut()
+ let texture_manager = Arc::new(TextureManager::new(&texture_config));
+ let renderer = Renderer::new(&render_config, texture_manager.clone()).unwrap();
+ let texture_id = texture_manager
.load_from_memory(bunny, ImageFormat::Farbfeld)
.unwrap();
diff --git a/alligator_render/src/lib.rs b/alligator_render/src/lib.rs
index 5e0aab6..0d76cc8 100644
--- a/alligator_render/src/lib.rs
+++ b/alligator_render/src/lib.rs
@@ -17,7 +17,5 @@ pub use instance::Instance;
pub(crate) use instance::InstanceBuffer;
pub use instance::InstanceId;
pub use renderer::Renderer;
-pub use texture::ImageFormat;
pub(crate) use texture::TextureAtlas;
-pub use texture::TextureId;
pub(crate) use vertex::Vertex;
diff --git a/alligator_render/src/renderer.rs b/alligator_render/src/renderer.rs
index f448119..fdd8c34 100644
--- a/alligator_render/src/renderer.rs
+++ b/alligator_render/src/renderer.rs
@@ -1,5 +1,5 @@
-use std::convert::TryInto;
use std::num::NonZeroU32;
+use std::{convert::TryInto, sync::Arc};
use crate::{
vertex::SQUARE, Camera, Instance, InstanceBuffer, RenderWindowConfig, TextureAtlas, Vertex,
@@ -150,7 +150,7 @@ impl Renderer {
// TODO this function needs to be smaller
pub fn new(
config: &RenderWindowConfig,
- textures: TextureManager,
+ textures: Arc<TextureManager>,
) -> Result<Self, NewRendererError> {
// build the window
let event_loop = EventLoop::new();
diff --git a/alligator_render/src/texture.rs b/alligator_render/src/texture.rs
index 2a86501..0afa0af 100644
--- a/alligator_render/src/texture.rs
+++ b/alligator_render/src/texture.rs
@@ -1,10 +1,9 @@
use std::error::Error;
use std::num::NonZeroU32;
-use std::sync::atomic::{AtomicUsize, Ordering};
+use std::sync::Arc;
-use alligator_resources::texture::TextureManager;
-use image::error::DecodingError;
-use image::{EncodableLayout, GenericImage, ImageError, RgbaImage};
+use alligator_resources::texture::{LoadError, Rgba16Texture, TextureId, TextureManager};
+use image::{EncodableLayout, GenericImage, RgbaImage};
use texture_packer::TexturePacker;
use texture_packer::{
exporter::{ExportResult, ImageExporter},
@@ -12,39 +11,6 @@ use texture_packer::{
};
use thiserror::Error;
-static NEXT_TEXTURE_ID: AtomicUsize = AtomicUsize::new(0);
-
-/// The unique ID for a subtexture
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct TextureId(usize);
-
-impl TextureId {
- #[allow(clippy::new_without_default)]
- #[must_use]
- pub fn new() -> Self {
- Self(NEXT_TEXTURE_ID.fetch_add(1, Ordering::Relaxed))
- }
-}
-
-/// These are the formats supported by the renderer.
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-#[non_exhaustive]
-pub enum ImageFormat {
- Bmp,
- Ico,
- Farbfeld,
-}
-
-impl ImageFormat {
- const fn format(self) -> image::ImageFormat {
- match self {
- Self::Bmp => image::ImageFormat::Bmp,
- Self::Ico => image::ImageFormat::Ico,
- Self::Farbfeld => image::ImageFormat::Farbfeld,
- }
- }
-}
-
/// The texture did not fit in the texture atlas
#[derive(Debug, Error)]
#[error("{:?}", .0)]
@@ -58,20 +24,11 @@ pub enum TextureError {
#[error("{:?}", .0)]
TextureTooLarge(#[from] PackError),
#[error("{}", .0)]
- BadImage(#[from] DecodingError), // TODO don't export this
+ BadImage(#[from] LoadError),
#[error("Unexpected Error (this is a bug in alligator_render): {}", .0)]
Unexpected(#[source] Box<dyn Error>),
}
-impl From<ImageError> for TextureError {
- fn from(ie: ImageError) -> Self {
- match ie {
- ImageError::Decoding(de) => de.into(),
- _ => Self::Unexpected(Box::new(ie)),
- }
- }
-}
-
/// Simpler constructor for a wgpu extent3d
const fn extent_3d(width: u32, height: u32) -> wgpu::Extent3d {
wgpu::Extent3d {
@@ -85,8 +42,8 @@ const fn extent_3d(width: u32, height: u32) -> wgpu::Extent3d {
// TODO make this Debug
// TODO make these resizable
pub struct TextureAtlas {
- textures: TextureManager,
- packer: TexturePacker<'static, image::RgbaImage, TextureId>,
+ textures: Arc<TextureManager>,
+ packer: TexturePacker<'static, Rgba16Texture, TextureId>,
diffuse_texture: wgpu::Texture,
diffuse_bind_group: wgpu::BindGroup,
image: RgbaImage,
@@ -97,11 +54,17 @@ pub struct TextureAtlas {
macro_rules! texture_info {
($name: ident, $prop: ident, $divisor: ident) => {
- pub fn $name(&self, id: TextureId) -> Option<f32> {
- let frame = self.texture_frame(id)?;
+ pub fn $name(&mut self, id: TextureId) -> Result<f32, TextureError> {
+ let frame = match self.texture_frame(id) {
+ Some(frame) => frame,
+ None => {
+ self.load_texture(id)?;
+ self.texture_frame(id).unwrap()
+ }
+ };
let property = frame.frame.$prop;
let value = property as f32 / self.$divisor as f32;
- Some(value)
+ Ok(value)
}
};
}
@@ -112,7 +75,7 @@ impl TextureAtlas {
// TODO this is still too large
pub fn new(
device: &wgpu::Device,
- textures: TextureManager,
+ textures: Arc<TextureManager>,
width: u32,
height: u32,
) -> (Self, wgpu::BindGroupLayout) {
@@ -206,13 +169,8 @@ impl TextureAtlas {
/// Load a new subtexture from memory
// TODO support RGBA16
- pub fn load_from_memory(
- &mut self,
- buf: &[u8],
- format: ImageFormat,
- ) -> Result<TextureId, TextureError> {
- let img = image::load_from_memory_with_format(buf, format.format())?.into_rgba8();
- let id = TextureId::new();
+ pub fn load_texture(&mut self, id: TextureId) -> Result<TextureId, TextureError> {
+ let img = self.textures.load_texture(id)?;
self.packer.pack_own(id, img).map_err(PackError)?;
Ok(id)
}
@@ -240,8 +198,11 @@ impl TextureAtlas {
Ok(())
}
- /// Clear the texture atlas
- pub fn clear(&mut self) {
+ /// Clear the texture atlas, and give it a new size
+ pub fn clear(&mut self, width: u32, height: u32) {
+ self.changed = true;
+ self.width = width;
+ self.height = height;
self.packer = TexturePacker::new_skyline(TexturePackerConfig {
max_width: self.width,
max_height: self.height,
diff --git a/alligator_resources/src/texture.rs b/alligator_resources/src/texture.rs
index e5b4147..3a5bf3e 100644
--- a/alligator_resources/src/texture.rs
+++ b/alligator_resources/src/texture.rs
@@ -1,6 +1,5 @@
use std::cmp::Reverse;
-use std::collections::HashMap;
-use std::mem::{self};
+use std::mem;
use std::path::Path;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
@@ -8,8 +7,6 @@ use std::sync::Arc;
use dashmap::DashMap;
use image::ImageBuffer;
use parking_lot::Mutex;
-use texture_packer::exporter::ImageExporter;
-use texture_packer::{Frame, TexturePacker, TexturePackerConfig};
use thiserror::Error;
use crate::Priority;
@@ -76,7 +73,7 @@ fn convert_image_load_error(e: image::ImageError) -> LoadError {
}
}
-type Rgba16Texture = image::ImageBuffer<image::Rgba<u16>, Box<[u16]>>;
+pub type Rgba16Texture = image::ImageBuffer<image::Rgba<u16>, Box<[u16]>>;
fn vec_image_to_box(vec_image: image::ImageBuffer<image::Rgba<u16>, Vec<u16>>) -> Rgba16Texture {
let width = vec_image.width();
@@ -238,116 +235,18 @@ impl Texture {
}
}
-pub struct TextureAtlas<'a> {
- width: u32,
- height: u32,
- packer: TexturePacker<'a, Rgba16Texture, TextureId>,
-}
-
-impl<'a> TextureAtlas<'a> {
- fn new(width: u32, height: u32, textures: &HashMap<TextureId, Texture>) -> Self {
- profiling::scope!("new atlas");
-
- let mut packer = TexturePacker::new_skyline(TexturePackerConfig {
- max_width: width,
- max_height: height,
- allow_rotation: false,
- trim: false,
- texture_padding: 0,
- ..Default::default()
- });
-
- for (id, texture) in textures {
- if texture.is_loaded() {
- let texture = texture
- .loaded_texture()
- .expect("texture couldn't be loaded");
-
- // if the textures don't fit, make a bigger packer
- if packer.pack_own(*id, texture.clone()).is_err() {
- return Self::new(width * 2, height * 2, textures);
- }
- }
- }
-
- Self {
- width,
- height,
- packer,
- }
- }
-
- fn subtexture(&self, id: TextureId) -> Option<&Frame<TextureId>> {
- self.packer.get_frame(&id)
- }
-
- #[must_use]
- pub const fn atlas_width(&self) -> u32 {
- self.width
- }
-
- #[must_use]
- pub const fn atlas_height(&self) -> u32 {
- self.height
- }
-
- /// Get the x-position of a texture, if it is in the texture atlas
- #[must_use]
- #[allow(clippy::cast_precision_loss)] // TODO remove this
- pub fn subtexture_x(&self, id: TextureId) -> Option<f32> {
- let x = self.subtexture(id)?.frame.x;
- Some(x as f32 / self.width as f32)
- }
-
- /// Get the y-position of a texture, if it is in the texture atlas
- #[must_use]
- #[allow(clippy::cast_precision_loss)] // TODO remove this
- pub fn subtexture_y(&self, id: TextureId) -> Option<f32> {
- let y = self.subtexture(id)?.frame.y;
- Some(y as f32 / self.height as f32)
- }
-
- /// Get the width of a texture, if it is in the texture atlas
- #[must_use]
- #[allow(clippy::cast_precision_loss)] // TODO remove this
- pub fn subtexture_width(&self, id: TextureId) -> Option<f32> {
- let width = self.subtexture(id)?.frame.w;
- Some(width as f32 / self.width as f32)
- }
-
- /// Get the height of a texture, if it is in the texture atlas
- #[must_use]
- #[allow(clippy::cast_precision_loss)] // TODO remove this
- pub fn subtexture_height(&self, id: TextureId) -> Option<f32> {
- let height = self.subtexture(id)?.frame.h;
- Some(height as f32 / self.height as f32)
- }
-
- #[must_use]
- pub fn to_texture(&self) -> Rgba16Texture {
- profiling::scope!("export atlas");
- vec_image_to_box(
- ImageExporter::export(&self.packer)
- .expect("ImageExporter error?")
- .into_rgba16(),
- )
- }
-}
-
pub struct TextureManager {
textures: DashMap<TextureId, Texture>,
max_size: usize,
- atlas_width: u32,
- atlas_height: u32,
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,
- pub atlas_width: u32,
- pub atlas_height: u32,
}
impl Default for TextureManagerConfig {
@@ -355,8 +254,6 @@ impl Default for TextureManagerConfig {
Self {
initial_capacity: 500,
max_size: 10 * 1024 * 1024, // 10 MiB
- atlas_width: 3980,
- atlas_height: 2160, // 4K resolution
}
}
}
@@ -370,8 +267,6 @@ impl TextureManager {
Self {
textures,
max_size: config.max_size,
- atlas_width: config.atlas_width,
- atlas_height: config.atlas_height,
needs_atlas_update: AtomicBool::new(false),
}
}
@@ -408,7 +303,7 @@ impl TextureManager {
/// This returns `Expected(DecodingError)` if the given buffer was invalid
/// for the given format.
pub fn load_from_memory(
- &mut self,
+ &self,
buf: &[u8],
format: ImageFormat,
) -> Result<TextureId, DecodingError> {
@@ -432,7 +327,7 @@ impl TextureManager {
/// 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(
- &mut self,
+ &self,
path: impl AsRef<Path>,
priority: Priority,
) -> Result<TextureId, LoadError> {
@@ -463,7 +358,7 @@ impl TextureManager {
///
/// 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(&mut self, id: TextureId, priority: Priority) -> Result<(), LoadError> {
+ 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);
@@ -478,10 +373,17 @@ impl TextureManager {
/// 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(&mut self) -> bool {
+ 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<Rgba16Texture, LoadError> {
self.textures
.get_mut(&id)
@@ -489,12 +391,4 @@ impl TextureManager {
.load_texture()
.cloned()
}
-
- // Create a texture atlas
- /*pub fn atlas(&mut self) -> TextureAtlas<'_> {
- let atlas = TextureAtlas::new(self.atlas_width, self.atlas_height, &self.textures);
- self.atlas_width = atlas.atlas_width();
- self.atlas_height = atlas.atlas_height();
- atlas
- }*/
}