diff options
| author | Micha White <botahamec@outlook.com> | 2022-10-11 21:42:10 -0400 |
|---|---|---|
| committer | Micha White <botahamec@outlook.com> | 2022-10-11 21:42:10 -0400 |
| commit | 76ed4fe2d3f96c1c25905875a0d5dacc5ff7ed8a (patch) | |
| tree | b327a5decfc7a7706c8911f050545556135f1aa5 /src/texture.rs | |
| parent | 19811a235d8b6791e4a70a89c0cd21a928de6e95 (diff) | |
Big performance improvement
Diffstat (limited to 'src/texture.rs')
| -rw-r--r-- | src/texture.rs | 98 |
1 files changed, 42 insertions, 56 deletions
diff --git a/src/texture.rs b/src/texture.rs index 457fea8..a3e2c10 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -3,10 +3,11 @@ use std::num::NonZeroU32; use std::sync::atomic::{AtomicUsize, Ordering}; use image::error::DecodingError; -use image::{DynamicImage, EncodableLayout, GenericImage, ImageError, RgbaImage}; +use image::{EncodableLayout, GenericImage, ImageError, RgbaImage}; +use texture_packer::TexturePacker; use texture_packer::{ exporter::{ExportResult, ImageExporter}, - MultiTexturePacker, TexturePackerConfig, + TexturePackerConfig, }; use thiserror::Error; @@ -64,8 +65,8 @@ impl From<ImageError> for TextureError { // TODO make these resizable // TODO this could probably be moved into WgpuTextures pub struct TextureAtlases<'a> { - packer: MultiTexturePacker<'a, image::RgbaImage, TextureId>, - images: Vec<RgbaImage>, + packer: TexturePacker<'a, image::RgbaImage, TextureId>, + image: RgbaImage, width: u32, height: u32, } @@ -90,14 +91,19 @@ impl<'a> TextureAtlases<'a> { // TODO why is this u32? pub fn new(width: u32, height: u32) -> Self { Self { - packer: MultiTexturePacker::new_skyline(TexturePackerConfig { + packer: TexturePacker::new_skyline(TexturePackerConfig { max_width: width, max_height: height, ..Default::default() }), width, height, - images: Vec::with_capacity(1), + image: RgbaImage::from_raw( + width, + height, + vec![0; 4 * width as usize * height as usize], + ) + .unwrap(), } } @@ -117,11 +123,7 @@ impl<'a> TextureAtlases<'a> { } fn texture_frame(&self, id: TextureId) -> Option<&texture_packer::Frame<TextureId>> { - self.packer - .get_pages() - .iter() - .map(|a| a.get_frame(&id)) - .next()? + self.packer.get_frame(&id) } texture_info!(texture_width, w); @@ -137,59 +139,29 @@ impl<'a> TextureAtlases<'a> { } } - fn atlases(&self) -> ExportResult<Box<[DynamicImage]>> { - self.packer - .get_pages() - .iter() - .map(ImageExporter::export) - .collect::<ExportResult<Vec<DynamicImage>>>() - .map(Vec::into_boxed_slice) - } - - fn push_image(&mut self) -> &mut RgbaImage { - self.images.push( - RgbaImage::from_raw( - self.width, - self.height, - vec![0; 4 * self.width as usize * self.height as usize], - ) - .expect("the image was the wrong size"), - ); - - self.images - .last_mut() - .expect("we just added an image to the list") - } - - fn fill_images(&mut self) -> ExportResult<()> { - for (i, atlas) in self.atlases()?.iter().enumerate() { - #[allow(clippy::option_if_let_else)] - if let Some(image) = self.images.get_mut(i) { - image - .copy_from(atlas, 0, 0) - .expect("atlases shouldn't be too large"); - } else { - let image = self.push_image(); - image - .copy_from(atlas, 0, 0) - .expect("atlases shouldn't be too large"); - } - } - + fn fill_image(&mut self) -> ExportResult<()> { + let atlas = { + profiling::scope!("export atlas"); + ImageExporter::export(&self.packer)? + }; + profiling::scope!("copy image"); + self.image + .copy_from(&atlas, 0, 0) + .expect("image cache is too small"); Ok(()) } fn clear(&mut self) { - self.packer = MultiTexturePacker::new_skyline(TexturePackerConfig { + self.packer = TexturePacker::new_skyline(TexturePackerConfig { max_width: self.width, max_height: self.height, ..Default::default() }); } - fn images(&mut self) -> ExportResult<&[RgbaImage]> { - self.fill_images()?; - Ok(&self.images) + fn image(&mut self) -> ExportResult<&RgbaImage> { + self.fill_image()?; + Ok(&self.image) } } @@ -197,6 +169,7 @@ pub struct WgpuTextures { atlases: TextureAtlases<'static>, diffuse_texture: wgpu::Texture, diffuse_bind_group: wgpu::BindGroup, + changed: bool, } macro_rules! get_info { @@ -276,6 +249,7 @@ impl WgpuTextures { atlases, diffuse_texture, diffuse_bind_group, + changed: true, }, texture_bind_group_layout, ) @@ -292,6 +266,7 @@ impl WgpuTextures { texture: &[u8], format: ImageFormat, ) -> Result<TextureId, TextureError> { + self.changed = true; self.atlases.load_from_memory(texture, format) } @@ -304,12 +279,20 @@ impl WgpuTextures { &self.diffuse_bind_group } + #[profiling::function] pub fn fill_textures(&mut self, queue: &wgpu::Queue) { + // saves time if nothing changed since the last time we did this + // FIXME This doesn't do much good once we get procedurally generated animation + // We'll have to create our own texture packer, with mutable subtextures, + // and more efficient algorithms. This'll also make frame times more consistent + if !self.changed { + return; + } + let atlas_size = self.atlases.extent_3d(); // put the packed texture into the base image - let Ok(atlases) = self.atlases.images() else { return }; - let Some(atlas) = atlases.first() else { return }; + let Ok(atlas) = self.atlases.image() else { return }; // copy that to the gpu queue.write_texture( @@ -327,9 +310,12 @@ impl WgpuTextures { }, atlas_size, ); + + self.changed = false; } pub fn clear_textures(&mut self) { + self.changed = true; self.atlases.clear(); } } |
