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.rs62
-rw-r--r--stockton-render/src/draw/texture/image.rs41
-rw-r--r--stockton-render/src/draw/texture/load.rs191
-rw-r--r--stockton-render/src/draw/texture/loader.rs712
-rw-r--r--stockton-render/src/draw/texture/mod.rs18
-rw-r--r--stockton-render/src/draw/texture/repo.rs201
-rw-r--r--stockton-render/src/draw/texture/resolver.rs55
-rw-r--r--stockton-render/src/draw/texture/staging_buffer.rs59
8 files changed, 0 insertions, 1339 deletions
diff --git a/stockton-render/src/draw/texture/block.rs b/stockton-render/src/draw/texture/block.rs
deleted file mode 100644
index 5ac3a94..0000000
--- a/stockton-render/src/draw/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/draw/texture/image.rs b/stockton-render/src/draw/texture/image.rs
deleted file mode 100644
index 0e272e9..0000000
--- a/stockton-render/src/draw/texture/image.rs
+++ /dev/null
@@ -1,41 +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;
- fn copy_row(&self, y: u32, ptr: *mut u8);
- unsafe fn copy_into(&self, ptr: *mut u8, row_size: usize);
-}
-
-impl LoadableImage for RgbaImage {
- fn width(&self) -> u32 {
- self.width()
- }
-
- fn height(&self) -> u32 {
- self.height()
- }
-
- fn copy_row(&self, y: u32, ptr: *mut u8) {
- let row_size_bytes = self.width() as usize * PIXEL_SIZE;
- let raw: &Vec<u8> = self.as_raw();
- let row = &raw[y as usize * row_size_bytes..(y as usize + 1) * row_size_bytes];
-
- unsafe {
- copy_nonoverlapping(row.as_ptr(), ptr, row.len());
- }
- }
-
- 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));
- }
- }
-}
diff --git a/stockton-render/src/draw/texture/load.rs b/stockton-render/src/draw/texture/load.rs
deleted file mode 100644
index 1f33ad5..0000000
--- a/stockton-render/src/draw/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/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs
deleted file mode 100644
index e6c19db..0000000
--- a/stockton-render/src/draw/texture/loader.rs
+++ /dev/null
@@ -1,712 +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::{draw::utils::find_memory_type_id, error::LockPoisoned, types::*};
-
-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: QueueFamilyId,
- queue_lock: Arc<RwLock<QueueT>>,
- ds_layout: Arc<RwLock<DescriptorSetLayoutT>>,
- request_channel: Receiver<LoaderRequest>,
- return_channel: 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: &mut DynamicAllocator,
- tex_allocator: &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/draw/texture/mod.rs b/stockton-render/src/draw/texture/mod.rs
deleted file mode 100644
index aef1b03..0000000
--- a/stockton-render/src/draw/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/draw/texture/repo.rs b/stockton-render/src/draw/texture/repo.rs
deleted file mode 100644
index e427eef..0000000
--- a/stockton-render/src/draw/texture/repo.rs
+++ /dev/null
@@ -1,201 +0,0 @@
-use super::{
- block::TexturesBlock,
- load::TextureLoadConfig,
- loader::{BlockRef, LoaderRequest, TextureLoader, TextureLoaderRemains, NUM_SIMULTANEOUS_CMDS},
- resolver::TextureResolver,
-};
-use crate::draw::queue_negotiator::QueueFamilySelector;
-use crate::error::LockPoisoned;
-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: &mut 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/draw/texture/resolver.rs b/stockton-render/src/draw/texture/resolver.rs
deleted file mode 100644
index 4b61c41..0000000
--- a/stockton-render/src/draw/texture/resolver.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-//! Resolves a texture in a BSP File to an image
-
-use crate::draw::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/draw/texture/staging_buffer.rs b/stockton-render/src/draw/texture/staging_buffer.rs
deleted file mode 100644
index 8d2ae17..0000000
--- a/stockton-render/src/draw/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));
- }
- }
-}