aboutsummaryrefslogtreecommitdiff
path: root/stockton-render/src/draw/texture
diff options
context:
space:
mode:
Diffstat (limited to 'stockton-render/src/draw/texture')
-rw-r--r--stockton-render/src/draw/texture/block.rs11
-rw-r--r--stockton-render/src/draw/texture/load.rs464
-rw-r--r--stockton-render/src/draw/texture/loader.rs125
-rw-r--r--stockton-render/src/draw/texture/repo.rs75
-rw-r--r--stockton-render/src/draw/texture/staging_buffer.rs23
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