aboutsummaryrefslogtreecommitdiff
path: root/stockton-render
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:22 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:22 +0100
commitbf9573764c695e65b1504419fafb76ccabb0322b (patch)
tree1702411472e080b436d27f34655ae4ada68ecc7b /stockton-render
parentdfb74318dae09b3430acf533374dc7004df65c65 (diff)
feat(render): generic and empty texture stores
Diffstat (limited to 'stockton-render')
-rw-r--r--stockton-render/src/draw/texture/chunk.rs37
-rw-r--r--stockton-render/src/draw/texture/image.rs49
-rw-r--r--stockton-render/src/draw/texture/loader.rs133
-rw-r--r--stockton-render/src/draw/texture/mod.rs1
-rw-r--r--stockton-render/src/draw/texture/resolver.rs8
-rw-r--r--stockton-render/src/systems.rs20
6 files changed, 230 insertions, 18 deletions
diff --git a/stockton-render/src/draw/texture/chunk.rs b/stockton-render/src/draw/texture/chunk.rs
index 8abdb4a..f1b88e9 100644
--- a/stockton-render/src/draw/texture/chunk.rs
+++ b/stockton-render/src/draw/texture/chunk.rs
@@ -18,6 +18,7 @@
//! A chunk of textures is an array of textures, the size of which is known at compile time.
//! This reduces the number of times we need to re-bind our descriptor sets
+use crate::draw::texture::image::LoadableImage;
use hal::prelude::*;
use image::{Rgba, RgbaImage};
@@ -42,9 +43,39 @@ pub struct TextureChunk {
}
impl TextureChunk {
+ /// Create a new empty texture chunk
+ pub fn new_empty(
+ device: &mut Device,
+ adapter: &mut Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ descriptor_set: DescriptorSet,
+ ) -> Result<TextureChunk, error::CreationError> {
+ let mut store = TextureChunk {
+ descriptor_set,
+ sampled_images: Vec::with_capacity(CHUNK_SIZE),
+ };
+
+ for i in 0..CHUNK_SIZE {
+ debug!("Putting a placeholder in slot {}", i);
+ store
+ .put_texture(
+ RgbaImage::from_pixel(1, 1, Rgba([0, 0, 0, 1])),
+ i,
+ device,
+ adapter,
+ command_queue,
+ command_pool,
+ )
+ .unwrap();
+ }
+
+ Ok(store)
+ }
+
/// Create a new texture chunk and load in the textures specified by `range` from `file` using `resolver`
/// Can error if the descriptor pool is too small or if a texture isn't found
- pub fn new<'a, I, R: TextureResolver>(
+ pub fn new<'a, I, R: TextureResolver<T>, T: LoadableImage>(
device: &mut Device,
adapter: &mut Adapter,
command_queue: &mut CommandQueue,
@@ -99,9 +130,9 @@ impl TextureChunk {
Ok(store)
}
- pub fn put_texture(
+ pub fn put_texture<T: LoadableImage>(
&mut self,
- image: RgbaImage,
+ image: T,
idx: usize,
device: &mut Device,
adapter: &mut Adapter,
diff --git a/stockton-render/src/draw/texture/image.rs b/stockton-render/src/draw/texture/image.rs
index 4a0fa08..2e0b27b 100644
--- a/stockton-render/src/draw/texture/image.rs
+++ b/stockton-render/src/draw/texture/image.rs
@@ -35,7 +35,34 @@ use crate::draw::buffer::create_buffer;
use crate::types::*;
/// The size of each pixel in an image
-const PIXEL_SIZE: usize = size_of::<image::Rgba<u8>>();
+const PIXEL_SIZE: usize = size_of::<u8>() * 4;
+
+/// An object that can be loaded as an image into GPU memory
+pub trait LoadableImage {
+ fn width(&self) -> u32;
+ fn height(&self) -> u32;
+ fn copy_row(&self, y: u32, ptr: *mut u8) -> ();
+}
+
+impl LoadableImage for RgbaImage {
+ fn width(&self) -> u32 {
+ self.width()
+ }
+
+ fn height(&self) -> u32 {
+ self.height()
+ }
+
+ fn copy_row(&self, y: u32, ptr: *mut u8) -> () {
+ let row_size_bytes = self.width() as usize * PIXEL_SIZE;
+ let raw: &Vec<u8> = self.as_raw();
+ let row = &raw[y as usize * row_size_bytes..(y as usize + 1) * row_size_bytes];
+
+ unsafe {
+ copy_nonoverlapping(row.as_ptr(), ptr, row.len());
+ }
+ }
+}
/// Holds an image that's loaded into GPU memory and can be sampled from
pub struct LoadedImage {
@@ -137,9 +164,9 @@ impl LoadedImage {
}
/// Load the given image
- pub fn load(
+ pub fn load<T: LoadableImage>(
&mut self,
- img: RgbaImage,
+ img: T,
device: &mut Device,
adapter: &Adapter,
command_queue: &mut CommandQueue,
@@ -166,15 +193,13 @@ impl LoadedImage {
// Copy everything into it
unsafe {
- let mapped_memory: *mut u8 = device
+ let mapped_memory: *mut u8 = std::mem::transmute(device
.map_memory(&staging_memory, Segment::ALL)
- .map_err(|_| "Couldn't map buffer memory")?;
+ .map_err(|_| "Couldn't map buffer memory")?);
for y in 0..img.height() as usize {
- let row = &(*img)[y * initial_row_size..(y + 1) * initial_row_size];
let dest_base: isize = (y * row_size).try_into().unwrap();
-
- copy_nonoverlapping(row.as_ptr(), mapped_memory.offset(dest_base), row.len());
+ img.copy_row(y as u32, mapped_memory.offset(dest_base));
}
device
.flush_mapped_memory_ranges(once((&staging_memory, Segment::ALL)))
@@ -288,8 +313,8 @@ impl LoadedImage {
}
/// Load the given image into a new buffer
- pub fn load_into_new(
- img: RgbaImage,
+ pub fn load_into_new<T: LoadableImage>(
+ img: T,
device: &mut Device,
adapter: &Adapter,
command_queue: &mut CommandQueue,
@@ -369,8 +394,8 @@ impl SampledImage {
})
}
- pub fn load_into_new(
- img: RgbaImage,
+ pub fn load_into_new<T: LoadableImage>(
+ img: T,
device: &mut Device,
adapter: &Adapter,
command_queue: &mut CommandQueue,
diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs
index dbd9d70..30c89dc 100644
--- a/stockton-render/src/draw/texture/loader.rs
+++ b/stockton-render/src/draw/texture/loader.rs
@@ -19,6 +19,7 @@
use super::chunk::TextureChunk;
use crate::draw::texture::chunk::CHUNK_SIZE;
+use crate::draw::texture::image::LoadableImage;
use crate::draw::texture::resolver::BasicFSResolver;
use core::mem::ManuallyDrop;
use std::path::Path;
@@ -43,6 +44,117 @@ pub struct TextureStore {
}
impl TextureStore {
+ pub fn new_empty(
+ device: &mut Device,
+ adapter: &mut Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ size: usize,
+ ) -> Result<TextureStore, error::CreationError> {
+ // Figure out how many textures in this file / how many chunks needed
+ let num_chunks = {
+ let mut x = size / CHUNK_SIZE;
+ if size % CHUNK_SIZE != 0 {
+ x += 1;
+ }
+ x
+ };
+ let rounded_size = num_chunks * CHUNK_SIZE;
+
+ // Descriptor pool, where we get our sets from
+ let mut descriptor_pool = unsafe {
+ use hal::pso::{
+ DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType, ImageDescriptorType,
+ };
+
+ device
+ .create_descriptor_pool(
+ num_chunks,
+ &[
+ DescriptorRangeDesc {
+ ty: DescriptorType::Image {
+ ty: ImageDescriptorType::Sampled {
+ with_sampler: false,
+ },
+ },
+ count: rounded_size,
+ },
+ DescriptorRangeDesc {
+ ty: DescriptorType::Sampler,
+ count: rounded_size,
+ },
+ ],
+ DescriptorPoolCreateFlags::empty(),
+ )
+ .map_err(|e| {
+ println!("{:?}", e);
+ error::CreationError::OutOfMemoryError
+ })?
+ };
+
+ // Layout of our descriptor sets
+ let descriptor_set_layout = unsafe {
+ use hal::pso::{
+ DescriptorSetLayoutBinding, DescriptorType, ImageDescriptorType, ShaderStageFlags,
+ };
+
+ device.create_descriptor_set_layout(
+ &[
+ DescriptorSetLayoutBinding {
+ binding: 0,
+ ty: DescriptorType::Image {
+ ty: ImageDescriptorType::Sampled {
+ with_sampler: false,
+ },
+ },
+ count: CHUNK_SIZE,
+ stage_flags: ShaderStageFlags::FRAGMENT,
+ immutable_samplers: false,
+ },
+ DescriptorSetLayoutBinding {
+ binding: 1,
+ ty: DescriptorType::Sampler,
+ count: CHUNK_SIZE,
+ stage_flags: ShaderStageFlags::FRAGMENT,
+ immutable_samplers: false,
+ },
+ ],
+ &[],
+ )
+ }
+ .map_err(|_| error::CreationError::OutOfMemoryError)?;
+
+ log::debug!("texture ds layout: {:?}", descriptor_set_layout);
+
+ // Create texture chunks
+ debug!("Starting to load textures...");
+ let mut chunks = Vec::with_capacity(num_chunks);
+ for i in 0..num_chunks {
+ debug!("Chunk {} / {}", i + 1, num_chunks);
+
+ let descriptor_set = unsafe {
+ descriptor_pool
+ .allocate_set(&descriptor_set_layout)
+ .map_err(|_| error::CreationError::OutOfMemoryError)?
+ };
+ chunks.push(TextureChunk::new_empty(
+ device,
+ adapter,
+ command_queue,
+ command_pool,
+ descriptor_set,
+ )?);
+ }
+
+ debug!("All textures loaded.");
+
+ Ok(TextureStore {
+ descriptor_pool: ManuallyDrop::new(descriptor_pool),
+ descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout),
+ chunks: chunks.into_boxed_slice(),
+ })
+ }
+
/// Create a new texture store for the given file, loading all textures from it.
pub fn new<T: HasTextures>(
device: &mut Device,
@@ -180,4 +292,25 @@ impl TextureStore {
pub fn get_chunk_descriptor_set(&self, idx: usize) -> &DescriptorSet {
&self.chunks[idx].descriptor_set
}
+
+ pub fn put_texture<T: LoadableImage>(
+ &mut self,
+ idx: usize,
+ img: T,
+ device: &mut Device,
+ adapter: &mut Adapter,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool,
+ ) -> Result<(), &'static str> {
+ // TODO: Resizing, etc?
+ let chunk = &mut self.chunks[idx / CHUNK_SIZE];
+ chunk.put_texture(
+ img,
+ idx % CHUNK_SIZE,
+ device,
+ adapter,
+ command_queue,
+ command_pool,
+ )
+ }
}
diff --git a/stockton-render/src/draw/texture/mod.rs b/stockton-render/src/draw/texture/mod.rs
index 91b15dc..ec36502 100644
--- a/stockton-render/src/draw/texture/mod.rs
+++ b/stockton-render/src/draw/texture/mod.rs
@@ -24,3 +24,4 @@ mod resolver;
pub use self::image::{LoadedImage, SampledImage};
pub use self::loader::TextureStore;
+pub use self::image::LoadableImage; \ No newline at end of file
diff --git a/stockton-render/src/draw/texture/resolver.rs b/stockton-render/src/draw/texture/resolver.rs
index 21e7628..610d43a 100644
--- a/stockton-render/src/draw/texture/resolver.rs
+++ b/stockton-render/src/draw/texture/resolver.rs
@@ -17,6 +17,7 @@
//! Resolves a texture in a BSP File to an image
+use crate::draw::texture::image::LoadableImage;
use stockton_levels::traits::textures::Texture;
use image::{io::Reader, RgbaImage};
@@ -24,9 +25,10 @@ use image::{io::Reader, RgbaImage};
use std::path::Path;
/// An object that can be used to resolve a texture from a BSP File
-pub trait TextureResolver {
+pub trait TextureResolver<T: LoadableImage> {
+
/// Get the given texture, or None if it's corrupt/not there.
- fn resolve(&mut self, texture: &Texture) -> Option<RgbaImage>;
+ fn resolve(&mut self, texture: &Texture) -> Option<T>;
}
/// A basic filesystem resolver which expects no file extension and guesses the image format
@@ -40,7 +42,7 @@ impl<'a> BasicFSResolver<'a> {
}
}
-impl<'a> TextureResolver for BasicFSResolver<'a> {
+impl<'a> TextureResolver<RgbaImage> for BasicFSResolver<'a> {
fn resolve(&mut self, tex: &Texture) -> Option<RgbaImage> {
let path = self.path.join(&tex.name);
diff --git a/stockton-render/src/systems.rs b/stockton-render/src/systems.rs
new file mode 100644
index 0000000..8001e1b
--- /dev/null
+++ b/stockton-render/src/systems.rs
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) Oscar Shrimpton 2020
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+pub use crate::window::process_window_events_system;
+pub use crate::draw::calc_vp_matrix_system;
+pub use crate::do_render_system; \ No newline at end of file