diff options
Diffstat (limited to 'stockton-render/src/draw/texture')
-rw-r--r-- | stockton-render/src/draw/texture/block.rs | 11 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/load.rs | 464 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/loader.rs | 125 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/repo.rs | 75 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/staging_buffer.rs | 23 |
5 files changed, 414 insertions, 284 deletions
diff --git a/stockton-render/src/draw/texture/block.rs b/stockton-render/src/draw/texture/block.rs index 7735f5c..5ac3a94 100644 --- a/stockton-render/src/draw/texture/block.rs +++ b/stockton-render/src/draw/texture/block.rs @@ -2,7 +2,6 @@ use super::{loader::BlockRef, repo::BLOCK_SIZE}; use crate::types::*; use arrayvec::ArrayVec; -use hal::prelude::*; use rendy_memory::{Allocator, Block}; use std::{iter::once, mem::ManuallyDrop}; @@ -15,7 +14,7 @@ pub struct TexturesBlock<B: Block<back::Backend>> { impl<B: Block<back::Backend>> TexturesBlock<B> { pub fn deactivate<T: Allocator<back::Backend, Block = B>>( mut self, - device: &mut Device, + device: &mut DeviceT, tex_alloc: &mut T, desc_alloc: &mut DescriptorAllocator, ) { @@ -36,9 +35,9 @@ impl<B: Block<back::Backend>> TexturesBlock<B> { pub struct LoadedImage<B: Block<back::Backend>> { pub mem: ManuallyDrop<B>, - pub img: ManuallyDrop<Image>, - pub img_view: ManuallyDrop<ImageView>, - pub sampler: ManuallyDrop<Sampler>, + pub img: ManuallyDrop<ImageT>, + pub img_view: ManuallyDrop<ImageViewT>, + pub sampler: ManuallyDrop<SamplerT>, pub row_size: usize, pub height: u32, pub width: u32, @@ -47,7 +46,7 @@ pub struct LoadedImage<B: Block<back::Backend>> { impl<B: Block<back::Backend>> LoadedImage<B> { pub fn deactivate<T: Allocator<back::Backend, Block = B>>( self, - device: &mut Device, + device: &mut DeviceT, alloc: &mut T, ) { unsafe { diff --git a/stockton-render/src/draw/texture/load.rs b/stockton-render/src/draw/texture/load.rs index 7ca07cb..be1aa27 100644 --- a/stockton-render/src/draw/texture/load.rs +++ b/stockton-render/src/draw/texture/load.rs @@ -2,7 +2,7 @@ use super::{ block::LoadedImage, block::TexturesBlock, loader::TextureLoader, repo::BLOCK_SIZE, resolver::TextureResolver, staging_buffer::StagingBuffer, LoadableImage, PIXEL_SIZE, }; -use crate::types::*; +use crate::{error::LockPoisoned, types::*}; use stockton_levels::prelude::*; use anyhow::{Context, Result}; @@ -11,17 +11,22 @@ use hal::{ command::{BufferImageCopy, CommandBufferFlags}, format::{Aspects, Format, Swizzle}, image::{ - Extent, Filter, Layout, Offset, SamplerDesc, SubresourceLayers, SubresourceRange, + Access, Extent, Filter, Layout, Offset, SamplerDesc, SubresourceLayers, SubresourceRange, Usage as ImgUsage, ViewKind, WrapMode, }, - memory::{Barrier, Dependencies}, - prelude::*, - pso::{Descriptor, DescriptorSetWrite, PipelineStage, ShaderStageFlags}, - queue::Submission, + memory::{Barrier, Dependencies, SparseFlags}, + pso::{Descriptor, DescriptorSetWrite, ImageDescriptorType, PipelineStage, ShaderStageFlags}, + MemoryTypeId, }; +use image::{Rgba, RgbaImage}; use rendy_descriptor::{DescriptorRanges, DescriptorSetLayoutBinding, DescriptorType}; use rendy_memory::{Allocator, Block}; -use std::mem::ManuallyDrop; +use std::{ + array::IntoIter, + iter::{empty, once}, + mem::ManuallyDrop, + sync::{Arc, RwLock}, +}; use thiserror::Error; #[derive(Error, Debug)] @@ -29,16 +34,27 @@ pub enum TextureLoadError { #[error("No available resources")] NoResources, - #[error("Texture is not in map")] - NotInMap(usize), - #[error("Texture could not be resolved")] ResolveFailed(usize), } +const FORMAT: Format = Format::Rgba8Srgb; +const RESOURCES: SubresourceRange = SubresourceRange { + aspects: Aspects::COLOR, + level_start: 0, + level_count: Some(1), + layer_start: 0, + layer_count: Some(1), +}; +const LAYERS: SubresourceLayers = SubresourceLayers { + aspects: Aspects::COLOR, + level: 0, + layers: 0..1, +}; + pub struct QueuedLoad<B: Block<back::Backend>> { - pub fence: Fence, - pub buf: CommandBuffer, + pub fence: FenceT, + pub buf: CommandBufferT, pub block: TexturesBlock<B>, pub staging_bufs: ArrayVec<[StagingBuffer; BLOCK_SIZE]>, } @@ -47,7 +63,7 @@ impl<B: Block<back::Backend>> QueuedLoad<B> { pub fn dissolve( self, ) -> ( - (Fence, CommandBuffer), + (FenceT, CommandBufferT), ArrayVec<[StagingBuffer; BLOCK_SIZE]>, TexturesBlock<B>, ) { @@ -56,18 +72,6 @@ impl<B: Block<back::Backend>> QueuedLoad<B> { } impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R, I> { - const FORMAT: Format = Format::Rgba8Srgb; - const RESOURCES: SubresourceRange = SubresourceRange { - aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, - }; - const LAYERS: SubresourceLayers = SubresourceLayers { - aspects: Aspects::COLOR, - level: 0, - layers: 0..1, - }; - pub(crate) unsafe fn attempt_queue_load( &mut self, block_ref: usize, @@ -78,17 +82,21 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader< .map_err(|_| LockPoisoned::Device) .context("Error getting device lock")?; - let textures = self.textures.read().unwrap(); + let textures = self + .textures + .read() + .map_err(|_| LockPoisoned::Map) + .context("Error getting map lock")?; // Get assets to use - let (fence, mut buf) = self + let (mut fence, mut buf) = self .buffers .pop_front() .ok_or(TextureLoadError::NoResources) .context("Error getting resources to use")?; // Create descriptor set - let descriptor_set = { + let mut descriptor_set = { let mut v: ArrayVec<[RDescriptorSet; 1]> = ArrayVec::new(); self.descriptor_allocator .allocate( @@ -97,7 +105,11 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader< DescriptorRanges::from_bindings(&[ DescriptorSetLayoutBinding { binding: 0, - ty: DescriptorType::SampledImage, + ty: DescriptorType::Image { + ty: ImageDescriptorType::Sampled { + with_sampler: false, + }, + }, count: BLOCK_SIZE, stage_flags: ShaderStageFlags::FRAGMENT, immutable_samplers: false, @@ -113,7 +125,6 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader< 1, &mut v, ) - .map_err::<HalErrorWrapper, _>(|e| e.into()) .context("Error creating descriptor set")?; v.pop().unwrap() @@ -122,7 +133,6 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader< // Get a command buffer buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); - let mut copy_cmds: ArrayVec<[_; BLOCK_SIZE]> = ArrayVec::new(); let mut imgs: ArrayVec<[_; BLOCK_SIZE]> = ArrayVec::new(); let mut staging_bufs: ArrayVec<[_; BLOCK_SIZE]> = ArrayVec::new(); @@ -131,135 +141,85 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader< // Get texture and Resolve image let tex = textures.get_texture(tex_idx as u32); if tex.is_none() { - break; // Past the end - // TODO: We should actually write blank descriptors + // 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 tex = tex.ok_or(TextureLoadError::NotInMap(tex_idx))?; + + let tex = tex.unwrap(); let img_data = self .resolver .resolve(tex) .ok_or(TextureLoadError::ResolveFailed(tex_idx))?; + let array_offset = tex_idx % BLOCK_SIZE; - // Calculate buffer size - let (row_size, total_size) = - tex_size_info(&img_data, self.optimal_buffer_copy_pitch_alignment); - - // Create staging buffer - let mut staging_buffer = StagingBuffer::new( + let (staging_buffer, img) = load_image( &mut device, &mut self.staging_allocator, - total_size as u64, + &mut self.tex_allocator, self.staging_memory_type, - ) - .context("Error creating staging buffer")?; - - // Write to staging buffer - let mapped_memory = staging_buffer - .map_memory(&mut device) - .map_err::<HalErrorWrapper, _>(|e| e.into()) - .context("Error mapping staged memory")?; - - img_data.copy_into(mapped_memory, row_size); - - staging_buffer.unmap_memory(&mut device); - - // Create image - let (img_mem, img) = create_image_view( - &mut device, - &mut *self.tex_allocator, - Self::FORMAT, - ImgUsage::SAMPLED, - &img_data, - ) - .context("Error creating image")?; - - // Create image view - let img_view = device - .create_image_view( - &img, - ViewKind::D2, - Self::FORMAT, - Swizzle::NO, - Self::RESOURCES, - ) - .map_err::<HalErrorWrapper, _>(|e| e.into()) - .context("Error creating image view")?; - - // Queue copy from buffer to image - copy_cmds.push(BufferImageCopy { - buffer_offset: 0, - buffer_width: (row_size / super::PIXEL_SIZE) as u32, - buffer_height: img_data.height(), - image_layers: Self::LAYERS, - image_offset: Offset { x: 0, y: 0, z: 0 }, - image_extent: Extent { - width: img_data.width(), - height: img_data.height(), - depth: 1, - }, - }); - - // Create sampler - let sampler = device - .create_sampler(&SamplerDesc::new(Filter::Nearest, WrapMode::Tile)) - .map_err::<HalErrorWrapper, _>(|e| e.into()) - .context("Error creating sampler")?; + self.optimal_buffer_copy_pitch_alignment, + img_data, + )?; // Write to descriptor set { - device.write_descriptor_sets(vec![ - DescriptorSetWrite { - set: descriptor_set.raw(), - binding: 0, - array_offset: tex_idx % BLOCK_SIZE, - descriptors: Some(Descriptor::Image( - &img_view, - Layout::ShaderReadOnlyOptimal, - )), - }, - DescriptorSetWrite { - set: descriptor_set.raw(), - binding: 1, - array_offset: tex_idx % BLOCK_SIZE, - descriptors: Some(Descriptor::Sampler(&sampler)), - }, - ]); + 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(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(), - }); + imgs.push(img); staging_bufs.push(staging_buffer); } - // Add start pipeline barriers - for li in imgs.iter() { - use hal::image::Access; - - buf.pipeline_barrier( - PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER, - Dependencies::empty(), - &[Barrier::Image { - states: (Access::empty(), Layout::Undefined) - ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal), - target: &*li.img, - families: None, - range: SubresourceRange { - aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, - }, - }], - ); - } + // 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()) { @@ -267,7 +227,7 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader< &*sb.buf, &*li.img, Layout::TransferDstOptimal, - &[BufferImageCopy { + once(BufferImageCopy { buffer_offset: 0, buffer_width: (li.row_size / super::PIXEL_SIZE) as u32, buffer_height: li.height, @@ -282,36 +242,29 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader< height: li.height, depth: 1, }, - }], - ); - } - for li in imgs.iter() { - use hal::image::Access; - - buf.pipeline_barrier( - PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER, - Dependencies::empty(), - &[Barrier::Image { - states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal) - ..(Access::SHADER_READ, Layout::ShaderReadOnlyOptimal), - target: &*li.img, - families: None, - range: Self::RESOURCES, - }], + }), ); } + 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 - self.gpu.queue_groups[self.cmd_queue_idx].queues[0].submit::<_, _, Semaphore, _, _>( - Submission { - command_buffers: &[&buf], - signal_semaphores: std::iter::empty(), - wait_semaphores: std::iter::empty(), - }, - Some(&fence), - ); + { + let mut queue = self.queue.write().map_err(|_| LockPoisoned::Queue)?; + + queue.submit(IntoIter::new([&buf]), empty(), empty(), Some(&mut fence)); + } Ok(QueuedLoad { staging_bufs, @@ -324,6 +277,107 @@ impl<'a, T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader< }, }) } + + pub(crate) unsafe fn get_blank_image( + device: &mut DeviceT, + buf: &mut CommandBufferT, + queue_lock: &Arc<RwLock<QueueT>>, + staging_allocator: &mut DynamicAllocator, + tex_allocator: &mut DynamicAllocator, + staging_memory_type: MemoryTypeId, + obcpa: u64, + ) -> Result<LoadedImage<DynamicBlock>> { + let img_data = RgbaImage::from_pixel(1, 1, Rgba([0, 0, 0, 0])); + + 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, + )?; + + 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: width, + height: 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) + } } pub fn tex_size_info<T: LoadableImage>(img: &T, obcpa: hal::buffer::Offset) -> (usize, usize) { @@ -338,12 +392,12 @@ pub fn tex_size_info<T: LoadableImage>(img: &T, obcpa: hal::buffer::Offset) -> ( } fn create_image_view<T, I>( - device: &mut Device, + device: &mut DeviceT, allocator: &mut T, format: Format, usage: ImgUsage, img: &I, -) -> Result<(T::Block, Image)> +) -> Result<(T::Block, ImageT)> where T: Allocator<back::Backend>, I: LoadableImage, @@ -358,10 +412,10 @@ where format, Tiling::Optimal, usage, + SparseFlags::empty(), ViewCapabilities::empty(), ) } - .map_err::<HalErrorWrapper, _>(|e| e.into()) .context("Error creating image")?; // Allocate memory @@ -370,15 +424,83 @@ where allocator.alloc(device, requirements.size, requirements.alignment) } - .map_err::<HalErrorWrapper, _>(|e| e.into()) .context("Error allocating memory")?; unsafe { device .bind_image_memory(&block.memory(), block.range().start, &mut image_ref) - .map_err::<HalErrorWrapper, _>(|e| e.into()) .context("Error binding memory to image")?; } Ok((block, image_ref)) } + +unsafe fn load_image<I: LoadableImage>( + device: &mut DeviceT, + staging_allocator: &mut DynamicAllocator, + tex_allocator: &mut DynamicAllocator, + staging_memory_type: MemoryTypeId, + obcpa: u64, + img_data: I, +) -> 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(Filter::Nearest, WrapMode::Tile)) + .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/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs index f505de5..a23d633 100644 --- a/stockton-render/src/draw/texture/loader.rs +++ b/stockton-render/src/draw/texture/loader.rs @@ -1,17 +1,17 @@ //! Manages the loading/unloading of textures use super::{ - block::TexturesBlock, + block::{LoadedImage, TexturesBlock}, load::{QueuedLoad, TextureLoadError}, resolver::TextureResolver, LoadableImage, }; -use crate::{draw::utils::find_memory_type_id, types::*}; +use crate::{draw::utils::find_memory_type_id, error::LockPoisoned, types::*}; use std::{ collections::VecDeque, marker::PhantomData, - mem::ManuallyDrop, + mem::{drop, ManuallyDrop}, sync::{ mpsc::{Receiver, Sender}, Arc, RwLock, @@ -23,7 +23,9 @@ use std::{ use anyhow::{Context, Result}; use arrayvec::ArrayVec; use hal::{ - format::Format, memory::Properties as MemProps, prelude::*, queue::family::QueueFamilyId, + format::Format, + memory::{Properties as MemProps, SparseFlags}, + queue::family::QueueFamilyId, MemoryTypeId, }; use log::*; @@ -40,23 +42,20 @@ 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<T, R, I> { - /// Handle to the device we're using - pub(crate) device: Arc<RwLock<Device>>, - /// Blocks for which commands have been queued and are done loading once the fence is triggered. pub(crate) commands_queued: ArrayVec<[QueuedLoad<DynamicBlock>; NUM_SIMULTANEOUS_CMDS]>, /// The command buffers used and a fence to go with them - pub(crate) buffers: VecDeque<(Fence, CommandBuffer)>, + pub(crate) buffers: VecDeque<(FenceT, CommandBufferT)>, /// The command pool buffers were allocated from - pub(crate) pool: ManuallyDrop<CommandPool>, + pub(crate) pool: ManuallyDrop<CommandPoolT>, /// The GPU we're submitting to - pub(crate) gpu: ManuallyDrop<Gpu>, + pub(crate) device: Arc<RwLock<DeviceT>>, - /// The index of the command queue being used - pub(crate) cmd_queue_idx: usize, + /// The command queue being used + pub(crate) queue: Arc<RwLock<QueueT>>, /// The memory allocator being used for textures pub(crate) tex_allocator: ManuallyDrop<DynamicAllocator>, @@ -67,7 +66,7 @@ pub struct TextureLoader<T, R, I> { /// Allocator for descriptor sets pub(crate) descriptor_allocator: ManuallyDrop<DescriptorAllocator>, - pub(crate) ds_layout: Arc<RwLock<DescriptorSetLayout>>, + pub(crate) ds_layout: Arc<RwLock<DescriptorSetLayoutT>>, /// Type ID for staging memory pub(crate) staging_memory_type: MemoryTypeId, @@ -88,6 +87,9 @@ pub struct TextureLoader<T, R, I> { /// The channel blocks are returned to. pub(crate) return_channel: Sender<TexturesBlock<DynamicBlock>>, + /// A filler image for descriptors that aren't needed but still need to be written to + pub(crate) blank_image: ManuallyDrop<LoadedImage<DynamicBlock>>, + pub(crate) _li: PhantomData<I>, } @@ -121,18 +123,20 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R } } fn main(&mut self) -> Result<bool> { - let mut device = self.device.write().unwrap(); - + 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) } - .map_err::<HalErrorWrapper, _>(|e| e.into()) .context("Error checking fence status")?; if signalled { let (assets, mut staging_bufs, block) = self.commands_queued.remove(i).dissolve(); - debug!("Done loading texture block {:?}", block.id); + debug!("Load finished for texture block {:?}", block.id); // Destroy staging buffers while staging_bufs.len() > 0 { @@ -155,11 +159,15 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R 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) => {} + Some(TextureLoadError::NoResources) => { + debug!("No resources, trying again later"); + } _ => return Err(x).context("Error queuing texture load"), }, } @@ -172,29 +180,21 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R } pub fn new( - device_lock: Arc<RwLock<Device>>, adapter: &Adapter, + device_lock: Arc<RwLock<DeviceT>>, family: QueueFamilyId, - gpu: Gpu, - ds_layout: Arc<RwLock<DescriptorSetLayout>>, + queue_lock: Arc<RwLock<QueueT>>, + ds_layout: Arc<RwLock<DescriptorSetLayoutT>>, request_channel: Receiver<LoaderRequest>, return_channel: Sender<TexturesBlock<DynamicBlock>>, texs: Arc<RwLock<T>>, resolver: R, ) -> Result<Self> { - let device = device_lock + let mut device = device_lock .write() .map_err(|_| LockPoisoned::Device) .context("Error getting device lock")?; - - // Pool - let mut pool = unsafe { - use hal::pool::CommandPoolCreateFlags; - - device.create_command_pool(family, CommandPoolCreateFlags::RESET_INDIVIDUAL) - } - .map_err::<HalErrorWrapper, _>(|e| e.into()) - .context("Error creating command pool")?; + let device_props = adapter.physical_device.properties(); let type_mask = unsafe { use hal::image::{Kind, Tiling, Usage, ViewCapabilities}; @@ -214,9 +214,9 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R Format::Rgba8Srgb, Tiling::Optimal, Usage::SAMPLED, + SparseFlags::empty(), ViewCapabilities::empty(), ) - .map_err::<HalErrorWrapper, _>(|e| e.into()) .context("Error creating test image to get buffer settings")?; let type_mask = device.get_image_requirements(&img).type_mask; @@ -226,8 +226,10 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R type_mask }; + debug!("Using type mask {:?}", type_mask); + // Tex Allocator - let tex_allocator = { + let mut tex_allocator = { let props = MemProps::DEVICE_LOCAL; DynamicAllocator::new( @@ -239,10 +241,11 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R 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, staging_allocator) = { + let (staging_memory_type, mut staging_allocator) = { let props = MemProps::CPU_VISIBLE | MemProps::COHERENT; let t = find_memory_type_id(&adapter, type_mask, props) .ok_or(TextureLoaderError::NoMemoryTypes)?; @@ -256,20 +259,28 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R max_chunk_size: u64::pow(2, 63), min_device_allocation: 4 * 32 * 32, }, + device_props.limits.non_coherent_atom_size as u64, ), ) }; - let buffers = { + // 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) - .map_err::<HalErrorWrapper, _>(|e| e.into()) - .context("Error creating fence")?, + device.create_fence(false).context("Error creating fence")?, pool.allocate_one(hal::command::Level::Primary), )); }; @@ -278,21 +289,30 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R data }; - let cmd_queue_idx = gpu - .queue_groups - .iter() - .position(|x| x.family == family) - .unwrap(); + 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, + ) + } + .context("Error creating blank image")?; - std::mem::drop(device); + drop(device); Ok(TextureLoader { - device: device_lock, commands_queued: ArrayVec::new(), buffers, pool: ManuallyDrop::new(pool), - gpu: ManuallyDrop::new(gpu), - cmd_queue_idx, + device: device_lock, + queue: queue_lock, ds_layout, tex_allocator: ManuallyDrop::new(tex_allocator), @@ -300,15 +320,13 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R descriptor_allocator: ManuallyDrop::new(DescriptorAllocator::new()), staging_memory_type, - optimal_buffer_copy_pitch_alignment: adapter - .physical_device - .limits() - .optimal_buffer_copy_pitch_alignment, + optimal_buffer_copy_pitch_alignment, request_channel, return_channel, textures: texs, resolver, + blank_image: ManuallyDrop::new(blank_image), _li: PhantomData::default(), }) } @@ -354,6 +372,9 @@ impl<T: HasTextures, R: TextureResolver<I>, I: LoadableImage> TextureLoader<T, R sleep(Duration::from_secs(0)); } + // Destroy blank image + read(&*self.blank_image).deactivate(&mut device, &mut *self.tex_allocator); + // Destroy fences let vec: Vec<_> = self.buffers.drain(..).collect(); diff --git a/stockton-render/src/draw/texture/repo.rs b/stockton-render/src/draw/texture/repo.rs index 2316dc4..c37da11 100644 --- a/stockton-render/src/draw/texture/repo.rs +++ b/stockton-render/src/draw/texture/repo.rs @@ -6,12 +6,14 @@ use super::{ resolver::TextureResolver, LoadableImage, }; +use crate::error::LockPoisoned; use crate::types::*; use std::{ + array::IntoIter, collections::HashMap, + iter::empty, marker::PhantomData, - mem::drop, mem::ManuallyDrop, sync::{ mpsc::{channel, Receiver, Sender}, @@ -22,12 +24,10 @@ use std::{ use anyhow::{Context, Result}; use hal::{ - prelude::*, - pso::{DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags}, - Features, + pso::{DescriptorSetLayoutBinding, DescriptorType, ImageDescriptorType, ShaderStageFlags}, + queue::family::QueueFamilyId, }; use log::debug; -use thiserror::Error; /// The number of textures in one 'block' /// The textures of the loaded file are divided into blocks of this size. @@ -36,7 +36,7 @@ pub const BLOCK_SIZE: usize = 8; pub struct TextureRepo<'a> { joiner: ManuallyDrop<JoinHandle<Result<TextureLoaderRemains>>>, - ds_layout: Arc<RwLock<DescriptorSetLayout>>, + ds_layout: Arc<RwLock<DescriptorSetLayoutT>>, req_send: Sender<LoaderRequest>, resp_recv: Receiver<TexturesBlock<DynamicBlock>>, blocks: HashMap<BlockRef, Option<TexturesBlock<DynamicBlock>>>, @@ -44,55 +44,43 @@ pub struct TextureRepo<'a> { _a: PhantomData<&'a ()>, } -#[derive(Error, Debug)] -pub enum TextureRepoError { - #[error("No suitable queue family")] - NoQueueFamilies, - - #[error("Lock poisoned")] - LockPoisoned, -} - impl<'a> TextureRepo<'a> { + pub fn queue_family_filter(family: &&QueueFamilyT) -> bool { + family.queue_type().supports_transfer() && family.max_queues() >= NUM_SIMULTANEOUS_CMDS + } + pub fn new< T: 'static + HasTextures + Send + Sync, R: 'static + TextureResolver<I> + Send + Sync, I: 'static + LoadableImage + Send, >( - device_lock: Arc<RwLock<Device>>, + device_lock: Arc<RwLock<DeviceT>>, + family: QueueFamilyId, + queue: Arc<RwLock<QueueT>>, adapter: &Adapter, texs_lock: Arc<RwLock<T>>, resolver: R, ) -> Result<Self> { + // Create Channels let (req_send, req_recv) = channel(); let (resp_send, resp_recv) = channel(); - let family = adapter - .queue_families - .iter() - .find(|family| { - family.queue_type().supports_transfer() - && family.max_queues() >= NUM_SIMULTANEOUS_CMDS - }) - .ok_or(TextureRepoError::NoQueueFamilies)?; - - let gpu = unsafe { - adapter - .physical_device - .open(&[(family, &[1.0])], Features::empty())? - }; - let device = device_lock .write() - .map_err(|_| TextureRepoError::LockPoisoned) + .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::SampledImage, + ty: DescriptorType::Image { + ty: ImageDescriptorType::Sampled { + with_sampler: false, + }, + }, count: BLOCK_SIZE, stage_flags: ShaderStageFlags::FRAGMENT, immutable_samplers: false, @@ -104,22 +92,23 @@ impl<'a> TextureRepo<'a> { stage_flags: ShaderStageFlags::FRAGMENT, immutable_samplers: false, }, - ], - &[], + ]), + empty(), ) } - .map_err::<HalErrorWrapper, _>(|e| e.into()) .context("Error creating descriptor set layout")?, )); + debug!("Created descriptor set layout {:?}", ds_lock); + drop(device); let joiner = { let loader = TextureLoader::new( - device_lock, adapter, - family.id(), - gpu, + device_lock.clone(), + family, + queue, ds_lock.clone(), req_recv, resp_send, @@ -140,7 +129,7 @@ impl<'a> TextureRepo<'a> { }) } - pub fn get_ds_layout(&self) -> RwLockReadGuard<DescriptorSetLayout> { + pub fn get_ds_layout(&self) -> RwLockReadGuard<DescriptorSetLayoutT> { self.ds_layout.read().unwrap() } @@ -162,7 +151,7 @@ impl<'a> TextureRepo<'a> { Ok(()) } - pub fn attempt_get_descriptor_set(&mut self, block_id: BlockRef) -> Option<&DescriptorSet> { + 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())) @@ -176,7 +165,7 @@ impl<'a> TextureRepo<'a> { } } - pub fn deactivate(mut self, device_lock: &mut Arc<RwLock<Device>>) { + pub fn deactivate(mut self, device_lock: &mut Arc<RwLock<DeviceT>>) { unsafe { use std::ptr::read; diff --git a/stockton-render/src/draw/texture/staging_buffer.rs b/stockton-render/src/draw/texture/staging_buffer.rs index 4adc974..8d2ae17 100644 --- a/stockton-render/src/draw/texture/staging_buffer.rs +++ b/stockton-render/src/draw/texture/staging_buffer.rs @@ -1,13 +1,14 @@ +#![allow(mutable_transmutes)] use crate::types::*; use std::mem::ManuallyDrop; use anyhow::{Context, Result}; -use hal::{device::MapError, prelude::*, MemoryTypeId}; +use hal::{device::MapError, memory::SparseFlags, MemoryTypeId}; use rendy_memory::{Allocator, Block}; pub struct StagingBuffer { - pub buf: ManuallyDrop<Buffer>, + pub buf: ManuallyDrop<BufferT>, pub mem: ManuallyDrop<DynamicBlock>, } @@ -15,24 +16,21 @@ impl StagingBuffer { const USAGE: hal::buffer::Usage = hal::buffer::Usage::TRANSFER_SRC; pub fn new( - device: &mut Device, + device: &mut DeviceT, alloc: &mut DynamicAllocator, size: u64, _memory_type_id: MemoryTypeId, ) -> Result<StagingBuffer> { - let mut buffer = unsafe { device.create_buffer(size, Self::USAGE) } - .map_err::<HalErrorWrapper, _>(|e| e.into()) + 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) - .map_err::<HalErrorWrapper, _>(|e| e.into()) .context("Error allocating staging memory")?; unsafe { device.bind_buffer_memory(memory.memory(), 0, &mut buffer) } - .map_err::<HalErrorWrapper, _>(|e| e.into()) .context("Error binding staging memory to buffer")?; Ok(StagingBuffer { @@ -41,14 +39,15 @@ impl StagingBuffer { }) } - pub unsafe fn map_memory(&mut self, device: &mut Device) -> Result<*mut u8, MapError> { - device.map_memory(self.mem.memory(), self.mem.range()) + 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 Device) { - device.unmap_memory(self.mem.memory()); // TODO: What if the same Memory is mapped in multiple places? + pub unsafe fn unmap_memory(&mut self, device: &mut DeviceT) { + self.mem.unmap(device); } - pub fn deactivate(self, device: &mut Device, alloc: &mut DynamicAllocator) { + pub fn deactivate(self, device: &mut DeviceT, alloc: &mut DynamicAllocator) { unsafe { use std::ptr::read; // Destroy buffer |