diff options
Diffstat (limited to 'stockton-render/src/texture')
-rw-r--r-- | stockton-render/src/texture/block.rs | 62 | ||||
-rw-r--r-- | stockton-render/src/texture/image.rs | 43 | ||||
-rw-r--r-- | stockton-render/src/texture/load.rs | 191 | ||||
-rw-r--r-- | stockton-render/src/texture/loader.rs | 711 | ||||
-rw-r--r-- | stockton-render/src/texture/mod.rs | 18 | ||||
-rw-r--r-- | stockton-render/src/texture/repo.rs | 199 | ||||
-rw-r--r-- | stockton-render/src/texture/resolver.rs | 55 | ||||
-rw-r--r-- | stockton-render/src/texture/staging_buffer.rs | 59 |
8 files changed, 0 insertions, 1338 deletions
diff --git a/stockton-render/src/texture/block.rs b/stockton-render/src/texture/block.rs deleted file mode 100644 index 5ac3a94..0000000 --- a/stockton-render/src/texture/block.rs +++ /dev/null @@ -1,62 +0,0 @@ -use super::{loader::BlockRef, repo::BLOCK_SIZE}; -use crate::types::*; - -use arrayvec::ArrayVec; -use rendy_memory::{Allocator, Block}; -use std::{iter::once, mem::ManuallyDrop}; - -pub struct TexturesBlock<B: Block<back::Backend>> { - pub id: BlockRef, - pub descriptor_set: ManuallyDrop<RDescriptorSet>, - pub imgs: ArrayVec<[LoadedImage<B>; BLOCK_SIZE]>, -} - -impl<B: Block<back::Backend>> TexturesBlock<B> { - pub fn deactivate<T: Allocator<back::Backend, Block = B>>( - mut self, - device: &mut DeviceT, - tex_alloc: &mut T, - desc_alloc: &mut DescriptorAllocator, - ) { - unsafe { - use std::ptr::read; - - // Descriptor set - desc_alloc.free(once(read(&*self.descriptor_set))); - - // Images - self.imgs - .drain(..) - .map(|x| x.deactivate(device, tex_alloc)) - .for_each(|_| {}); - } - } -} - -pub struct LoadedImage<B: Block<back::Backend>> { - pub mem: ManuallyDrop<B>, - pub img: ManuallyDrop<ImageT>, - pub img_view: ManuallyDrop<ImageViewT>, - pub sampler: ManuallyDrop<SamplerT>, - pub row_size: usize, - pub height: u32, - pub width: u32, -} - -impl<B: Block<back::Backend>> LoadedImage<B> { - pub fn deactivate<T: Allocator<back::Backend, Block = B>>( - self, - device: &mut DeviceT, - alloc: &mut T, - ) { - unsafe { - use std::ptr::read; - - device.destroy_image_view(read(&*self.img_view)); - device.destroy_image(read(&*self.img)); - device.destroy_sampler(read(&*self.sampler)); - - alloc.free(device, read(&*self.mem)); - } - } -} diff --git a/stockton-render/src/texture/image.rs b/stockton-render/src/texture/image.rs deleted file mode 100644 index f984b72..0000000 --- a/stockton-render/src/texture/image.rs +++ /dev/null @@ -1,43 +0,0 @@ -use super::PIXEL_SIZE; - -use core::ptr::copy_nonoverlapping; -use std::convert::TryInto; - -use image::RgbaImage; - -/// An object that can be loaded as an image into GPU memory -pub trait LoadableImage { - fn width(&self) -> u32; - fn height(&self) -> u32; - - /// # Safety - /// Ensure the ptr is at least width() * PIXEL_SIZE bytes. - unsafe fn copy_row(&self, y: u32, ptr: *mut u8); - - /// # Safety - /// Ensure the ptr is at least row_size * height() * PIXEL_SIZE bytes. - unsafe fn copy_into(&self, ptr: *mut u8, row_size: usize) { - for y in 0..self.height() as usize { - let dest_base: isize = (y * row_size).try_into().unwrap(); - self.copy_row(y as u32, ptr.offset(dest_base)); - } - } -} - -impl LoadableImage for RgbaImage { - fn width(&self) -> u32 { - self.width() - } - - fn height(&self) -> u32 { - self.height() - } - - unsafe 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]; - - copy_nonoverlapping(row.as_ptr(), ptr, row.len()); - } -} diff --git a/stockton-render/src/texture/load.rs b/stockton-render/src/texture/load.rs deleted file mode 100644 index 1f33ad5..0000000 --- a/stockton-render/src/texture/load.rs +++ /dev/null @@ -1,191 +0,0 @@ -use super::{ - block::LoadedImage, block::TexturesBlock, repo::BLOCK_SIZE, resolver::TextureResolver, - staging_buffer::StagingBuffer, LoadableImage, PIXEL_SIZE, -}; -use crate::types::*; - -use anyhow::{Context, Result}; -use arrayvec::ArrayVec; -use hal::{ - format::{Aspects, Format, Swizzle}, - image::{ - Filter, SamplerDesc, SubresourceLayers, SubresourceRange, Usage as ImgUsage, ViewKind, - WrapMode, - }, - memory::SparseFlags, - MemoryTypeId, -}; -use rendy_memory::{Allocator, Block}; -use std::mem::ManuallyDrop; -use thiserror::Error; - -#[derive(Error, Debug)] -pub enum TextureLoadError { - #[error("No available resources")] - NoResources, -} - -pub const FORMAT: Format = Format::Rgba8Srgb; -pub const RESOURCES: SubresourceRange = SubresourceRange { - aspects: Aspects::COLOR, - level_start: 0, - level_count: Some(1), - layer_start: 0, - layer_count: Some(1), -}; -pub const LAYERS: SubresourceLayers = SubresourceLayers { - aspects: Aspects::COLOR, - level: 0, - layers: 0..1, -}; - -pub struct TextureLoadConfig<R: TextureResolver> { - pub resolver: R, - pub filter: Filter, - pub wrap_mode: WrapMode, -} - -pub struct QueuedLoad<B: Block<back::Backend>> { - pub fence: FenceT, - pub buf: CommandBufferT, - pub block: TexturesBlock<B>, - pub staging_bufs: ArrayVec<[StagingBuffer; BLOCK_SIZE]>, -} - -impl<B: Block<back::Backend>> QueuedLoad<B> { - pub fn dissolve( - self, - ) -> ( - (FenceT, CommandBufferT), - ArrayVec<[StagingBuffer; BLOCK_SIZE]>, - TexturesBlock<B>, - ) { - ((self.fence, self.buf), self.staging_bufs, self.block) - } -} - -pub fn tex_size_info<T: LoadableImage>(img: &T, obcpa: hal::buffer::Offset) -> (usize, usize) { - let initial_row_size = PIXEL_SIZE * img.width() as usize; - let row_alignment_mask = obcpa as u32 - 1; - - let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize; - let total_size = (row_size * (img.height() as usize)) as u64; - debug_assert!(row_size as usize >= initial_row_size); - - (row_size, total_size as usize) -} - -pub fn create_image_view<T, I>( - device: &mut DeviceT, - allocator: &mut T, - format: Format, - usage: ImgUsage, - img: &I, -) -> Result<(T::Block, ImageT)> -where - T: Allocator<back::Backend>, - I: LoadableImage, -{ - // Make the image - let mut image_ref = unsafe { - use hal::image::{Kind, Tiling, ViewCapabilities}; - - device.create_image( - Kind::D2(img.width(), img.height(), 1, 1), - 1, - format, - Tiling::Optimal, - usage, - SparseFlags::empty(), - ViewCapabilities::empty(), - ) - } - .context("Error creating image")?; - - // Allocate memory - let (block, _) = unsafe { - let requirements = device.get_image_requirements(&image_ref); - - allocator.alloc(device, requirements.size, requirements.alignment) - } - .context("Error allocating memory")?; - - unsafe { - device - .bind_image_memory(block.memory(), block.range().start, &mut image_ref) - .context("Error binding memory to image")?; - } - - Ok((block, image_ref)) -} - -pub unsafe fn load_image<I: LoadableImage, R: TextureResolver>( - device: &mut DeviceT, - staging_allocator: &mut DynamicAllocator, - tex_allocator: &mut DynamicAllocator, - staging_memory_type: MemoryTypeId, - obcpa: u64, - img_data: I, - config: &TextureLoadConfig<R>, -) -> Result<(StagingBuffer, LoadedImage<DynamicBlock>)> { - // Calculate buffer size - let (row_size, total_size) = tex_size_info(&img_data, obcpa); - - // Create staging buffer - let mut staging_buffer = StagingBuffer::new( - device, - staging_allocator, - total_size as u64, - staging_memory_type, - ) - .context("Error creating staging buffer")?; - - // Write to staging buffer - let mapped_memory = staging_buffer - .map_memory(device) - .context("Error mapping staged memory")?; - - img_data.copy_into(mapped_memory, row_size); - - staging_buffer.unmap_memory(device); - - // Create image - let (img_mem, img) = create_image_view( - device, - tex_allocator, - FORMAT, - ImgUsage::SAMPLED | ImgUsage::TRANSFER_DST, - &img_data, - ) - .context("Error creating image")?; - - // Create image view - let img_view = device - .create_image_view( - &img, - ViewKind::D2, - FORMAT, - Swizzle::NO, - ImgUsage::SAMPLED | ImgUsage::TRANSFER_DST, - RESOURCES, - ) - .context("Error creating image view")?; - - // Create sampler - let sampler = device - .create_sampler(&SamplerDesc::new(config.filter, config.wrap_mode)) - .context("Error creating sampler")?; - - Ok(( - staging_buffer, - LoadedImage { - mem: ManuallyDrop::new(img_mem), - img: ManuallyDrop::new(img), - img_view: ManuallyDrop::new(img_view), - sampler: ManuallyDrop::new(sampler), - row_size, - height: img_data.height(), - width: img_data.width(), - }, - )) -} diff --git a/stockton-render/src/texture/loader.rs b/stockton-render/src/texture/loader.rs deleted file mode 100644 index 5c85fd3..0000000 --- a/stockton-render/src/texture/loader.rs +++ /dev/null @@ -1,711 +0,0 @@ -//! Manages the loading/unloading of textures - -use super::{ - block::{LoadedImage, TexturesBlock}, - load::{load_image, QueuedLoad, TextureLoadConfig, TextureLoadError, LAYERS, RESOURCES}, - repo::BLOCK_SIZE, - resolver::TextureResolver, - PIXEL_SIZE, -}; -use crate::{error::LockPoisoned, types::*, utils::find_memory_type_id}; - -use std::{ - array::IntoIter, - collections::VecDeque, - iter::{empty, once}, - mem::{drop, ManuallyDrop}, - sync::{ - mpsc::{Receiver, Sender}, - Arc, RwLock, - }, - thread::sleep, - time::Duration, -}; - -use anyhow::{Context, Result}; -use arrayvec::ArrayVec; -use hal::{ - command::{BufferImageCopy, CommandBufferFlags}, - format::{Aspects, Format}, - image::{Access, Extent, Layout, Offset, SubresourceLayers, SubresourceRange}, - memory::{Barrier, Dependencies, Properties as MemProps, SparseFlags}, - pso::{Descriptor, DescriptorSetWrite, ImageDescriptorType, PipelineStage, ShaderStageFlags}, - queue::family::QueueFamilyId, - MemoryTypeId, -}; -use image::{Rgba, RgbaImage}; -use log::*; -use rendy_descriptor::{DescriptorRanges, DescriptorSetLayoutBinding, DescriptorType}; -use rendy_memory::DynamicConfig; -use thiserror::Error; - -/// The number of command buffers to have in flight simultaneously. -pub const NUM_SIMULTANEOUS_CMDS: usize = 2; - -/// A reference to a texture of the current map -pub type BlockRef = usize; - -/// Manages the loading/unloading of textures -/// This is expected to load the textures, then send the loaded blocks back -pub struct TextureLoader<R: TextureResolver> { - /// Blocks for which commands have been queued and are done loading once the fence is triggered. - commands_queued: ArrayVec<[QueuedLoad<DynamicBlock>; NUM_SIMULTANEOUS_CMDS]>, - - /// The command buffers used and a fence to go with them - buffers: VecDeque<(FenceT, CommandBufferT)>, - - /// The command pool buffers were allocated from - pool: ManuallyDrop<CommandPoolT>, - - /// The GPU we're submitting to - device: Arc<RwLock<DeviceT>>, - - /// The command queue being used - queue: Arc<RwLock<QueueT>>, - - /// The memory allocator being used for textures - tex_allocator: ManuallyDrop<DynamicAllocator>, - - /// The memory allocator for staging memory - staging_allocator: ManuallyDrop<DynamicAllocator>, - - /// Allocator for descriptor sets - descriptor_allocator: ManuallyDrop<DescriptorAllocator>, - - ds_layout: Arc<RwLock<DescriptorSetLayoutT>>, - - /// Type ID for staging memory - staging_memory_type: MemoryTypeId, - - /// From adapter, used for determining alignment - optimal_buffer_copy_pitch_alignment: hal::buffer::Offset, - - /// Configuration for how to find and load textures - config: TextureLoadConfig<R>, - - /// The channel requests come in. - /// Requests should reference a texture **block**, for example textures 8..16 is block 1. - request_channel: Receiver<LoaderRequest>, - - /// The channel blocks are returned to. - return_channel: Sender<TexturesBlock<DynamicBlock>>, - - /// A filler image for descriptors that aren't needed but still need to be written to - blank_image: ManuallyDrop<LoadedImage<DynamicBlock>>, -} - -#[derive(Error, Debug)] -pub enum TextureLoaderError { - #[error("Couldn't find a suitable memory type")] - NoMemoryTypes, -} - -impl<R: TextureResolver> TextureLoader<R> { - pub fn loop_until_exit(mut self) -> Result<TextureLoaderRemains> { - debug!("TextureLoader starting main loop"); - let mut res = Ok(false); - while res.is_ok() { - res = self.main(); - if let Ok(true) = res { - break; - } - - sleep(Duration::from_secs(0)); - } - - match res { - Ok(true) => { - debug!("Starting to deactivate TextureLoader"); - - Ok(self.deactivate()) - } - Err(r) => Err(r.context("Error in TextureLoader loop")), - _ => unreachable!(), - } - } - fn main(&mut self) -> Result<bool> { - let mut device = self - .device - .write() - .map_err(|_| LockPoisoned::Device) - .context("Error getting device lock")?; - // Check for blocks that are finished, then send them back - let mut i = 0; - while i < self.commands_queued.len() { - let signalled = unsafe { device.get_fence_status(&self.commands_queued[i].fence) } - .context("Error checking fence status")?; - - if signalled { - let (assets, mut staging_bufs, block) = self.commands_queued.remove(i).dissolve(); - debug!("Load finished for texture block {:?}", block.id); - - // Destroy staging buffers - for buf in staging_bufs.drain(..) { - buf.deactivate(&mut device, &mut self.staging_allocator); - } - - self.buffers.push_back(assets); - self.return_channel - .send(block) - .context("Error returning texture block")?; - } else { - i += 1; - } - } - - drop(device); - - // Check for messages to start loading blocks - let req_iter: Vec<_> = self.request_channel.try_iter().collect(); - for to_load in req_iter { - match to_load { - LoaderRequest::Load(to_load) => { - // Attempt to load given block - debug!("Attempting to queue load for texture block {:?}", to_load); - - let result = unsafe { self.attempt_queue_load(to_load) }; - match result { - Ok(queued_load) => self.commands_queued.push(queued_load), - Err(x) => match x.downcast_ref::<TextureLoadError>() { - Some(TextureLoadError::NoResources) => { - debug!("No resources, trying again later"); - } - _ => return Err(x).context("Error queuing texture load"), - }, - } - } - LoaderRequest::End => return Ok(true), - } - } - - Ok(false) - } - - pub fn new( - adapter: &Adapter, - device_lock: Arc<RwLock<DeviceT>>, - (family, queue_lock): (QueueFamilyId, Arc<RwLock<QueueT>>), - ds_layout: Arc<RwLock<DescriptorSetLayoutT>>, - (request_channel, return_channel): ( - Receiver<LoaderRequest>, - Sender<TexturesBlock<DynamicBlock>>, - ), - config: TextureLoadConfig<R>, - ) -> Result<Self> { - let mut device = device_lock - .write() - .map_err(|_| LockPoisoned::Device) - .context("Error getting device lock")?; - let device_props = adapter.physical_device.properties(); - - let type_mask = unsafe { - use hal::image::{Kind, Tiling, Usage, ViewCapabilities}; - - // We create an empty image with the same format as used for textures - // this is to get the type_mask required, which will stay the same for - // all colour images of the same tiling. (certain memory flags excluded). - - // Size and alignment don't necessarily stay the same, so we're forced to - // guess at the alignment for our allocator. - - // TODO: Way to tune these options - let img = device - .create_image( - Kind::D2(16, 16, 1, 1), - 1, - Format::Rgba8Srgb, - Tiling::Optimal, - Usage::SAMPLED, - SparseFlags::empty(), - ViewCapabilities::empty(), - ) - .context("Error creating test image to get buffer settings")?; - - let type_mask = device.get_image_requirements(&img).type_mask; - - device.destroy_image(img); - - type_mask - }; - - debug!("Using type mask {:?}", type_mask); - - // Tex Allocator - let mut tex_allocator = { - let props = MemProps::DEVICE_LOCAL; - - DynamicAllocator::new( - find_memory_type_id(adapter, type_mask, props) - .ok_or(TextureLoaderError::NoMemoryTypes) - .context("Couldn't create tex memory allocator")?, - props, - DynamicConfig { - block_size_granularity: 4 * 32 * 32, // 32x32 image - max_chunk_size: u64::pow(2, 63), - min_device_allocation: 4 * 32 * 32, - }, - device_props.limits.non_coherent_atom_size as u64, - ) - }; - - let (staging_memory_type, mut staging_allocator) = { - let props = MemProps::CPU_VISIBLE | MemProps::COHERENT; - let t = find_memory_type_id(adapter, u32::MAX, props) - .ok_or(TextureLoaderError::NoMemoryTypes) - .context("Couldn't create staging memory allocator")?; - ( - t, - DynamicAllocator::new( - t, - props, - DynamicConfig { - block_size_granularity: 4 * 32 * 32, // 32x32 image - max_chunk_size: u64::pow(2, 63), - min_device_allocation: 4 * 32 * 32, - }, - device_props.limits.non_coherent_atom_size as u64, - ), - ) - }; - - // Pool - let mut pool = unsafe { - use hal::pool::CommandPoolCreateFlags; - - device.create_command_pool(family, CommandPoolCreateFlags::RESET_INDIVIDUAL) - } - .context("Error creating command pool")?; - - // Command buffers and fences - debug!("Creating resources..."); - let mut buffers = { - let mut data = VecDeque::with_capacity(NUM_SIMULTANEOUS_CMDS); - - for _ in 0..NUM_SIMULTANEOUS_CMDS { - unsafe { - data.push_back(( - device.create_fence(false).context("Error creating fence")?, - pool.allocate_one(hal::command::Level::Primary), - )); - }; - } - - data - }; - - let optimal_buffer_copy_pitch_alignment = - device_props.limits.optimal_buffer_copy_pitch_alignment; - - let blank_image = unsafe { - Self::get_blank_image( - &mut device, - &mut buffers[0].1, - &queue_lock, - (&mut staging_allocator, &mut tex_allocator), - staging_memory_type, - optimal_buffer_copy_pitch_alignment, - &config, - ) - } - .context("Error creating blank image")?; - - drop(device); - - Ok(TextureLoader { - commands_queued: ArrayVec::new(), - buffers, - pool: ManuallyDrop::new(pool), - device: device_lock, - queue: queue_lock, - ds_layout, - - tex_allocator: ManuallyDrop::new(tex_allocator), - staging_allocator: ManuallyDrop::new(staging_allocator), - descriptor_allocator: ManuallyDrop::new(DescriptorAllocator::new()), - - staging_memory_type, - optimal_buffer_copy_pitch_alignment, - - request_channel, - return_channel, - config, - blank_image: ManuallyDrop::new(blank_image), - }) - } - - unsafe fn attempt_queue_load(&mut self, block_ref: usize) -> Result<QueuedLoad<DynamicBlock>> { - let mut device = self - .device - .write() - .map_err(|_| LockPoisoned::Device) - .context("Error getting device lock")?; - - // Get assets to use - let (mut fence, mut buf) = self - .buffers - .pop_front() - .ok_or(TextureLoadError::NoResources) - .context("Error getting resources to use")?; - - // Create descriptor set - let mut descriptor_set = { - let mut v: ArrayVec<[RDescriptorSet; 1]> = ArrayVec::new(); - self.descriptor_allocator - .allocate( - &device, - &*self - .ds_layout - .read() - .map_err(|_| LockPoisoned::Other) - .context("Error reading descriptor set layout")?, - DescriptorRanges::from_bindings(&[ - DescriptorSetLayoutBinding { - binding: 0, - ty: DescriptorType::Image { - ty: ImageDescriptorType::Sampled { - with_sampler: false, - }, - }, - count: BLOCK_SIZE, - stage_flags: ShaderStageFlags::FRAGMENT, - immutable_samplers: false, - }, - DescriptorSetLayoutBinding { - binding: 1, - ty: DescriptorType::Sampler, - count: BLOCK_SIZE, - stage_flags: ShaderStageFlags::FRAGMENT, - immutable_samplers: false, - }, - ]), - 1, - &mut v, - ) - .context("Error creating descriptor set")?; - - v.pop().unwrap() - }; - - // Get a command buffer - buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); - - let mut imgs: ArrayVec<[_; BLOCK_SIZE]> = ArrayVec::new(); - let mut staging_bufs: ArrayVec<[_; BLOCK_SIZE]> = ArrayVec::new(); - - // For each texture in block - for tex_idx in (block_ref * BLOCK_SIZE)..(block_ref + 1) * BLOCK_SIZE { - // Resolve texture - let img_data = self.config.resolver.resolve(tex_idx as u32); - if img_data.is_none() { - // Write a blank descriptor - device.write_descriptor_set(DescriptorSetWrite { - set: descriptor_set.raw_mut(), - binding: 0, - array_offset: tex_idx % BLOCK_SIZE, - descriptors: once(Descriptor::Image( - &*self.blank_image.img_view, - Layout::ShaderReadOnlyOptimal, - )), - }); - device.write_descriptor_set(DescriptorSetWrite { - set: descriptor_set.raw_mut(), - binding: 1, - array_offset: tex_idx % BLOCK_SIZE, - descriptors: once(Descriptor::Sampler(&*self.blank_image.sampler)), - }); - - continue; - } - - let img_data = img_data.unwrap(); - - let array_offset = tex_idx % BLOCK_SIZE; - - let (staging_buffer, img) = load_image( - &mut device, - &mut self.staging_allocator, - &mut self.tex_allocator, - self.staging_memory_type, - self.optimal_buffer_copy_pitch_alignment, - img_data, - &self.config, - )?; - - // Write to descriptor set - { - device.write_descriptor_set(DescriptorSetWrite { - set: descriptor_set.raw_mut(), - binding: 0, - array_offset, - descriptors: once(Descriptor::Image( - &*img.img_view, - Layout::ShaderReadOnlyOptimal, - )), - }); - device.write_descriptor_set(DescriptorSetWrite { - set: descriptor_set.raw_mut(), - binding: 1, - array_offset, - descriptors: once(Descriptor::Sampler(&*img.sampler)), - }); - } - - imgs.push(img); - - staging_bufs.push(staging_buffer); - } - - // Add start pipeline barrier - buf.pipeline_barrier( - PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER, - Dependencies::empty(), - imgs.iter().map(|li| Barrier::Image { - states: (Access::empty(), Layout::Undefined) - ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal), - target: &*li.img, - families: None, - range: SubresourceRange { - aspects: Aspects::COLOR, - level_start: 0, - level_count: None, - layer_start: 0, - layer_count: None, - }, - }), - ); - - // Record copy commands - for (li, sb) in imgs.iter().zip(staging_bufs.iter()) { - buf.copy_buffer_to_image( - &*sb.buf, - &*li.img, - Layout::TransferDstOptimal, - once(BufferImageCopy { - buffer_offset: 0, - buffer_width: (li.row_size / super::PIXEL_SIZE) as u32, - buffer_height: li.height, - image_layers: SubresourceLayers { - aspects: Aspects::COLOR, - level: 0, - layers: 0..1, - }, - image_offset: Offset { x: 0, y: 0, z: 0 }, - image_extent: gfx_hal::image::Extent { - width: li.width, - height: li.height, - depth: 1, - }, - }), - ); - } - buf.pipeline_barrier( - PipelineStage::TRANSFER..PipelineStage::BOTTOM_OF_PIPE, - Dependencies::empty(), - imgs.iter().map(|li| Barrier::Image { - states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal) - ..(Access::empty(), Layout::ShaderReadOnlyOptimal), - target: &*li.img, - families: None, - range: RESOURCES, - }), - ); - - buf.finish(); - - // Submit command buffer - { - let mut queue = self.queue.write().map_err(|_| LockPoisoned::Queue)?; - - queue.submit(IntoIter::new([&buf]), empty(), empty(), Some(&mut fence)); - } - - Ok(QueuedLoad { - staging_bufs, - fence, - buf, - block: TexturesBlock { - id: block_ref, - imgs, - descriptor_set: ManuallyDrop::new(descriptor_set), - }, - }) - } - - unsafe fn get_blank_image( - device: &mut DeviceT, - buf: &mut CommandBufferT, - queue_lock: &Arc<RwLock<QueueT>>, - (staging_allocator, tex_allocator): (&mut DynamicAllocator, &mut DynamicAllocator), - staging_memory_type: MemoryTypeId, - obcpa: u64, - config: &TextureLoadConfig<R>, - ) -> Result<LoadedImage<DynamicBlock>> { - let img_data = RgbaImage::from_pixel(1, 1, Rgba([255, 0, 255, 255])); - - let height = img_data.height(); - let width = img_data.width(); - let row_alignment_mask = obcpa as u32 - 1; - let initial_row_size = PIXEL_SIZE * img_data.width() as usize; - let row_size = - ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize; - - let (staging_buffer, img) = load_image( - device, - staging_allocator, - tex_allocator, - staging_memory_type, - obcpa, - img_data, - config, - )?; - - buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); - - buf.pipeline_barrier( - PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER, - Dependencies::empty(), - once(Barrier::Image { - states: (Access::empty(), Layout::Undefined) - ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal), - target: &*img.img, - families: None, - range: SubresourceRange { - aspects: Aspects::COLOR, - level_start: 0, - level_count: None, - layer_start: 0, - layer_count: None, - }, - }), - ); - buf.copy_buffer_to_image( - &*staging_buffer.buf, - &*img.img, - Layout::TransferDstOptimal, - once(BufferImageCopy { - buffer_offset: 0, - buffer_width: (row_size / super::PIXEL_SIZE) as u32, - buffer_height: height, - image_layers: LAYERS, - image_offset: Offset { x: 0, y: 0, z: 0 }, - image_extent: Extent { - width, - height, - depth: 1, - }, - }), - ); - - buf.pipeline_barrier( - PipelineStage::TRANSFER..PipelineStage::BOTTOM_OF_PIPE, - Dependencies::empty(), - once(Barrier::Image { - states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal) - ..(Access::empty(), Layout::ShaderReadOnlyOptimal), - target: &*img.img, - families: None, - range: RESOURCES, - }), - ); - buf.finish(); - - let mut fence = device.create_fence(false).context("Error creating fence")?; - - { - let mut queue = queue_lock.write().map_err(|_| LockPoisoned::Queue)?; - - queue.submit( - IntoIter::new([buf as &CommandBufferT]), - empty(), - empty(), - Some(&mut fence), - ); - } - - device - .wait_for_fence(&fence, std::u64::MAX) - .context("Error waiting for copy")?; - - device.destroy_fence(fence); - - staging_buffer.deactivate(device, staging_allocator); - - Ok(img) - } - - /// Safely destroy all the vulkan stuff in this instance - /// Note that this returns the memory allocators, from which should be freed any TextureBlocks - /// All in-progress things are sent to return_channel. - fn deactivate(mut self) -> TextureLoaderRemains { - use std::ptr::read; - - let mut device = self.device.write().unwrap(); - - unsafe { - // Wait for any currently queued loads to be done - while self.commands_queued.len() > 0 { - let mut i = 0; - while i < self.commands_queued.len() { - let signalled = device - .get_fence_status(&self.commands_queued[i].fence) - .expect("Device lost by TextureManager"); - - if signalled { - // Destroy finished ones - let (assets, mut staging_bufs, block) = - self.commands_queued.remove(i).dissolve(); - - device.destroy_fence(assets.0); - // Command buffer will be freed when we reset the command pool - // Destroy staging buffers - for buf in staging_bufs.drain(..) { - buf.deactivate(&mut device, &mut self.staging_allocator); - } - - self.return_channel - .send(block) - .expect("Sending through return channel failed"); - } else { - i += 1; - } - } - - sleep(Duration::from_secs(0)); - } - - // Destroy blank image - read(&*self.blank_image).deactivate(&mut device, &mut *self.tex_allocator); - - // Destroy fences - - self.buffers - .drain(..) - .map(|(f, _)| device.destroy_fence(f)) - .for_each(|_| {}); - - // Free command pool - self.pool.reset(true); - device.destroy_command_pool(read(&*self.pool)); - - debug!("Done deactivating TextureLoader"); - - TextureLoaderRemains { - tex_allocator: ManuallyDrop::new(read(&*self.tex_allocator)), - descriptor_allocator: ManuallyDrop::new(read(&*self.descriptor_allocator)), - } - } - } -} - -pub struct TextureLoaderRemains { - pub tex_allocator: ManuallyDrop<DynamicAllocator>, - pub descriptor_allocator: ManuallyDrop<DescriptorAllocator>, -} - -pub enum LoaderRequest { - /// Load the given block - Load(BlockRef), - - /// Stop looping and deactivate - End, -} diff --git a/stockton-render/src/texture/mod.rs b/stockton-render/src/texture/mod.rs deleted file mode 100644 index aef1b03..0000000 --- a/stockton-render/src/texture/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -//! Everything related to loading textures into GPU memory - -mod block; -mod image; -mod load; -mod loader; -mod repo; -pub mod resolver; -mod staging_buffer; - -pub use self::block::TexturesBlock; -pub use self::image::LoadableImage; -pub use self::load::TextureLoadConfig; -pub use self::loader::BlockRef; -pub use self::repo::{TexLoadQueue, TextureRepo}; - -/// The size of each pixel in an image -pub const PIXEL_SIZE: usize = std::mem::size_of::<u8>() * 4; diff --git a/stockton-render/src/texture/repo.rs b/stockton-render/src/texture/repo.rs deleted file mode 100644 index 341d355..0000000 --- a/stockton-render/src/texture/repo.rs +++ /dev/null @@ -1,199 +0,0 @@ -use super::{ - block::TexturesBlock, - load::TextureLoadConfig, - loader::{BlockRef, LoaderRequest, TextureLoader, TextureLoaderRemains, NUM_SIMULTANEOUS_CMDS}, - resolver::TextureResolver, -}; -use crate::error::LockPoisoned; -use crate::queue_negotiator::QueueFamilySelector; -use crate::types::*; - -use std::{ - array::IntoIter, - collections::HashMap, - iter::empty, - mem::ManuallyDrop, - sync::{ - mpsc::{channel, Receiver, Sender}, - Arc, RwLock, RwLockReadGuard, - }, - thread::JoinHandle, -}; - -use anyhow::{Context, Result}; -use hal::{ - pso::{DescriptorSetLayoutBinding, DescriptorType, ImageDescriptorType, ShaderStageFlags}, - queue::family::QueueFamilyId, -}; -use log::debug; - -/// The number of textures in one 'block' -/// The textures of the loaded file are divided into blocks of this size. -/// Whenever a texture is needed, the whole block its in is loaded. -pub const BLOCK_SIZE: usize = 8; - -pub struct TextureRepo { - joiner: ManuallyDrop<JoinHandle<Result<TextureLoaderRemains>>>, - ds_layout: Arc<RwLock<DescriptorSetLayoutT>>, - req_send: Sender<LoaderRequest>, - resp_recv: Receiver<TexturesBlock<DynamicBlock>>, - blocks: HashMap<BlockRef, Option<TexturesBlock<DynamicBlock>>>, -} - -impl TextureRepo { - pub fn new<R: 'static + TextureResolver + Send + Sync>( - device_lock: Arc<RwLock<DeviceT>>, - family: QueueFamilyId, - queue: Arc<RwLock<QueueT>>, - adapter: &Adapter, - config: TextureLoadConfig<R>, - ) -> Result<Self> { - // Create Channels - let (req_send, req_recv) = channel(); - let (resp_send, resp_recv) = channel(); - let device = device_lock - .write() - .map_err(|_| LockPoisoned::Device) - .context("Error getting device lock")?; - - // Create descriptor set layout - let ds_lock = Arc::new(RwLock::new( - unsafe { - device.create_descriptor_set_layout( - IntoIter::new([ - DescriptorSetLayoutBinding { - binding: 0, - ty: DescriptorType::Image { - ty: ImageDescriptorType::Sampled { - with_sampler: false, - }, - }, - count: BLOCK_SIZE, - stage_flags: ShaderStageFlags::FRAGMENT, - immutable_samplers: false, - }, - DescriptorSetLayoutBinding { - binding: 1, - ty: DescriptorType::Sampler, - count: BLOCK_SIZE, - stage_flags: ShaderStageFlags::FRAGMENT, - immutable_samplers: false, - }, - ]), - empty(), - ) - } - .context("Error creating descriptor set layout")?, - )); - - debug!("Created descriptor set layout {:?}", ds_lock); - - drop(device); - - let joiner = { - let loader = TextureLoader::new( - adapter, - device_lock.clone(), - (family, queue), - ds_lock.clone(), - (req_recv, resp_send), - config, - )?; - - std::thread::spawn(move || loader.loop_until_exit()) - }; - - Ok(TextureRepo { - joiner: ManuallyDrop::new(joiner), - ds_layout: ds_lock, - blocks: HashMap::new(), - req_send, - resp_recv, - }) - } - - pub fn get_ds_layout(&self) -> Result<RwLockReadGuard<DescriptorSetLayoutT>> { - self.ds_layout - .read() - .map_err(|_| LockPoisoned::Other) - .context("Error locking descriptor set layout") - } - - pub fn queue_load(&mut self, block_id: BlockRef) -> Result<()> { - if self.blocks.contains_key(&block_id) { - return Ok(()); - } - - self.force_queue_load(block_id) - } - - pub fn force_queue_load(&mut self, block_id: BlockRef) -> Result<()> { - self.req_send - .send(LoaderRequest::Load(block_id)) - .context("Error queuing texture block load")?; - - self.blocks.insert(block_id, None); - - Ok(()) - } - - pub fn attempt_get_descriptor_set(&mut self, block_id: BlockRef) -> Option<&DescriptorSetT> { - self.blocks - .get(&block_id) - .and_then(|opt| opt.as_ref().map(|z| z.descriptor_set.raw())) - } - - pub fn process_responses(&mut self) { - let resp_iter: Vec<_> = self.resp_recv.try_iter().collect(); - for resp in resp_iter { - debug!("Got block {:?} back from loader", resp.id); - self.blocks.insert(resp.id, Some(resp)); - } - } - - pub fn deactivate(mut self, device_lock: &Arc<RwLock<DeviceT>>) { - unsafe { - use std::ptr::read; - - // Join the loader thread - self.req_send.send(LoaderRequest::End).unwrap(); - let mut remains = read(&*self.joiner).join().unwrap().unwrap(); - - // Process any ones that just got done loading - self.process_responses(); - - // Only now can we lock device without deadlocking - let mut device = device_lock.write().unwrap(); - - // Return all the texture memory and descriptors. - for (_, v) in self.blocks.drain() { - if let Some(block) = v { - block.deactivate( - &mut device, - &mut *remains.tex_allocator, - &mut remains.descriptor_allocator, - ); - } - } - - // Dispose of both allocators - read(&*remains.tex_allocator).dispose(); - read(&*remains.descriptor_allocator).dispose(&device); - - // Deactivate DS Layout - let ds_layout = Arc::try_unwrap(self.ds_layout) - .unwrap() - .into_inner() - .unwrap(); - device.destroy_descriptor_set_layout(ds_layout); - } - } -} - -pub struct TexLoadQueue; - -impl QueueFamilySelector for TexLoadQueue { - fn is_suitable(&self, family: &QueueFamilyT) -> bool { - family.queue_type().supports_transfer() && family.max_queues() >= NUM_SIMULTANEOUS_CMDS - } -} diff --git a/stockton-render/src/texture/resolver.rs b/stockton-render/src/texture/resolver.rs deleted file mode 100644 index f66b724..0000000 --- a/stockton-render/src/texture/resolver.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! Resolves a texture in a BSP File to an image - -use crate::texture::image::LoadableImage; -use stockton_levels::{parts::IsTexture, prelude::HasTextures}; - -use std::{ - path::Path, - sync::{Arc, RwLock}, -}; - -use image::{io::Reader, RgbaImage}; - -/// An object that can be used to resolve a texture from a BSP File -pub trait TextureResolver { - type Image: LoadableImage; - - /// Get the given texture, or None if it's corrupt/not there. - fn resolve(&mut self, texture_id: u32) -> Option<Self::Image>; -} - -/// A basic filesystem resolver which gets the texture name from any HasTextures Object. -pub struct FsResolver<'a, T: HasTextures> { - path: &'a Path, - map_lock: Arc<RwLock<T>>, -} - -impl<'a, T: HasTextures> FsResolver<'a, T> { - pub fn new(path: &'a Path, map_lock: Arc<RwLock<T>>) -> Self { - FsResolver { path, map_lock } - } -} - -impl<'a, T: HasTextures> TextureResolver for FsResolver<'a, T> { - type Image = RgbaImage; - - fn resolve(&mut self, tex: u32) -> Option<Self::Image> { - let map = self.map_lock.read().unwrap(); - let tex = map.get_texture(tex)?; - let path = self.path.join(&tex.name()); - - // drop(tex); - // drop(map); - - if let Ok(file) = Reader::open(path) { - if let Ok(guessed) = file.with_guessed_format() { - if let Ok(decoded) = guessed.decode() { - return Some(decoded.into_rgba8()); - } - } - } - - log::warn!("Couldn't resolve texture {:?}", tex.name()); - None - } -} diff --git a/stockton-render/src/texture/staging_buffer.rs b/stockton-render/src/texture/staging_buffer.rs deleted file mode 100644 index 8d2ae17..0000000 --- a/stockton-render/src/texture/staging_buffer.rs +++ /dev/null @@ -1,59 +0,0 @@ -#![allow(mutable_transmutes)] -use crate::types::*; - -use std::mem::ManuallyDrop; - -use anyhow::{Context, Result}; -use hal::{device::MapError, memory::SparseFlags, MemoryTypeId}; -use rendy_memory::{Allocator, Block}; - -pub struct StagingBuffer { - pub buf: ManuallyDrop<BufferT>, - pub mem: ManuallyDrop<DynamicBlock>, -} - -impl StagingBuffer { - const USAGE: hal::buffer::Usage = hal::buffer::Usage::TRANSFER_SRC; - - pub fn new( - device: &mut DeviceT, - alloc: &mut DynamicAllocator, - size: u64, - _memory_type_id: MemoryTypeId, - ) -> Result<StagingBuffer> { - let mut buffer = unsafe { device.create_buffer(size, Self::USAGE, SparseFlags::empty()) } - .context("Error creating buffer")?; - - let requirements = unsafe { device.get_buffer_requirements(&buffer) }; - - let (memory, _) = alloc - .alloc(device, requirements.size, requirements.alignment) - .context("Error allocating staging memory")?; - - unsafe { device.bind_buffer_memory(memory.memory(), 0, &mut buffer) } - .context("Error binding staging memory to buffer")?; - - Ok(StagingBuffer { - buf: ManuallyDrop::new(buffer), - mem: ManuallyDrop::new(memory), - }) - } - - pub unsafe fn map_memory(&mut self, device: &mut DeviceT) -> Result<*mut u8, MapError> { - let range = 0..(self.mem.range().end - self.mem.range().start); - Ok(self.mem.map(device, range)?.ptr().as_mut()) - } - pub unsafe fn unmap_memory(&mut self, device: &mut DeviceT) { - self.mem.unmap(device); - } - - pub fn deactivate(self, device: &mut DeviceT, alloc: &mut DynamicAllocator) { - unsafe { - use std::ptr::read; - // Destroy buffer - device.destroy_buffer(read(&*self.buf)); - // Free memory - alloc.free(device, read(&*self.mem)); - } - } -} |