/*
* Copyright (C) Oscar Shrimpton 2020
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
//! Deals with loading textures into GPU memory
use super::chunk::TextureChunk;
use crate::draw::texture::chunk::CHUNK_SIZE;
use crate::draw::texture::image::LoadableImage;
use crate::draw::texture::resolver::BasicFSResolver;
use core::mem::ManuallyDrop;
use std::path::Path;
use log::debug;
use hal::prelude::*;
use stockton_levels::prelude::*;
use crate::error;
use crate::types::*;
/// Stores all loaded textures in GPU memory.
/// When rendering, the descriptor sets are bound to the buffer
/// The descriptor set layout should have the same count of textures as this does.
/// All descriptors will be properly initialised images.
pub struct TextureStore {
descriptor_pool: ManuallyDrop,
pub(crate) descriptor_set_layout: ManuallyDrop,
chunks: Box<[TextureChunk]>,
}
impl TextureStore {
pub fn new_empty(
device: &mut Device,
adapter: &mut Adapter,
allocator: &mut DynamicAllocator,
command_queue: &mut CommandQueue,
command_pool: &mut CommandPool,
size: usize,
) -> Result {
// Figure out how many textures in this file / how many chunks needed
let num_chunks = {
let mut x = size / CHUNK_SIZE;
if size % CHUNK_SIZE != 0 {
x += 1;
}
x
};
let rounded_size = num_chunks * CHUNK_SIZE;
// Descriptor pool, where we get our sets from
let mut descriptor_pool = unsafe {
use hal::pso::{DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType};
device
.create_descriptor_pool(
num_chunks,
&[
DescriptorRangeDesc {
ty: DescriptorType::SampledImage,
count: rounded_size,
},
DescriptorRangeDesc {
ty: DescriptorType::Sampler,
count: rounded_size,
},
],
DescriptorPoolCreateFlags::empty(),
)
.map_err(|e| {
println!("{:?}", e);
error::CreationError::OutOfMemoryError
})?
};
// Layout of our descriptor sets
let descriptor_set_layout = unsafe {
use hal::pso::{DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags};
device.create_descriptor_set_layout(
&[
DescriptorSetLayoutBinding {
binding: 0,
ty: DescriptorType::SampledImage,
count: CHUNK_SIZE,
stage_flags: ShaderStageFlags::FRAGMENT,
immutable_samplers: false,
},
DescriptorSetLayoutBinding {
binding: 1,
ty: DescriptorType::Sampler,
count: CHUNK_SIZE,
stage_flags: ShaderStageFlags::FRAGMENT,
immutable_samplers: false,
},
],
&[],
)
}
.map_err(|_| error::CreationError::OutOfMemoryError)?;
log::debug!("texture ds layout: {:?}", descriptor_set_layout);
// Create texture chunks
debug!("Starting to load textures...");
let mut chunks = Vec::with_capacity(num_chunks);
for i in 0..num_chunks {
debug!("Chunk {} / {}", i + 1, num_chunks);
let descriptor_set = unsafe {
descriptor_pool
.allocate_set(&descriptor_set_layout)
.map_err(|_| error::CreationError::OutOfMemoryError)?
};
chunks.push(TextureChunk::new_empty(
device,
adapter,
allocator,
command_queue,
command_pool,
descriptor_set,
)?);
}
debug!("All textures loaded.");
Ok(TextureStore {
descriptor_pool: ManuallyDrop::new(descriptor_pool),
descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout),
chunks: chunks.into_boxed_slice(),
})
}
/// Create a new texture store for the given file, loading all textures from it.
pub fn new(
device: &mut Device,
adapter: &mut Adapter,
allocator: &mut DynamicAllocator,
command_queue: &mut CommandQueue,
command_pool: &mut CommandPool,
file: &T,
) -> Result {
// Figure out how many textures in this file / how many chunks needed
let size = file.textures_iter().count();
let num_chunks = {
let mut x = size / CHUNK_SIZE;
if size % CHUNK_SIZE != 0 {
x += 1;
}
x
};
let rounded_size = num_chunks * CHUNK_SIZE;
// Descriptor pool, where we get our sets from
let mut descriptor_pool = unsafe {
use hal::pso::{DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType};
device
.create_descriptor_pool(
num_chunks,
&[
DescriptorRangeDesc {
ty: DescriptorType::SampledImage,
count: rounded_size,
},
DescriptorRangeDesc {
ty: DescriptorType::Sampler,
count: rounded_size,
},
],
DescriptorPoolCreateFlags::empty(),
)
.map_err(|e| {
println!("{:?}", e);
error::CreationError::OutOfMemoryError
})?
};
// Layout of our descriptor sets
let descriptor_set_layout = unsafe {
use hal::pso::{DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags};
device.create_descriptor_set_layout(
&[
DescriptorSetLayoutBinding {
binding: 0,
ty: DescriptorType::SampledImage,
count: CHUNK_SIZE,
stage_flags: ShaderStageFlags::FRAGMENT,
immutable_samplers: false,
},
DescriptorSetLayoutBinding {
binding: 1,
ty: DescriptorType::Sampler,
count: CHUNK_SIZE,
stage_flags: ShaderStageFlags::FRAGMENT,
immutable_samplers: false,
},
],
&[],
)
}
.map_err(|_| error::CreationError::OutOfMemoryError)?;
// TODO: Proper way to set up resolver
let mut resolver = BasicFSResolver::new(Path::new("."));
// Create texture chunks
debug!("Starting to load textures...");
let mut chunks = Vec::with_capacity(num_chunks);
for i in 0..num_chunks {
debug!("Chunk {} / {}", i + 1, num_chunks);
let descriptor_set = unsafe {
descriptor_pool
.allocate_set(&descriptor_set_layout)
.map_err(|_| error::CreationError::OutOfMemoryError)?
};
chunks.push(TextureChunk::new(
device,
adapter,
allocator,
command_queue,
command_pool,
descriptor_set,
file.textures_iter().skip(i * CHUNK_SIZE).take(CHUNK_SIZE),
&mut resolver,
)?);
}
debug!("All textures loaded.");
Ok(TextureStore {
descriptor_pool: ManuallyDrop::new(descriptor_pool),
descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout),
chunks: chunks.into_boxed_slice(),
})
}
/// Call this before dropping
pub fn deactivate(mut self, device: &mut Device, allocator: &mut DynamicAllocator) {
unsafe {
use core::ptr::read;
for chunk in self.chunks.into_vec().drain(..) {
chunk.deactivate(device, allocator);
}
self.descriptor_pool.reset();
device.destroy_descriptor_set_layout(ManuallyDrop::into_inner(read(
&self.descriptor_set_layout,
)));
device.destroy_descriptor_pool(ManuallyDrop::into_inner(read(&self.descriptor_pool)));
}
}
/// Get the descriptor set for a given chunk
pub fn get_chunk_descriptor_set(&self, idx: usize) -> &DescriptorSet {
&self.chunks[idx].descriptor_set
}
pub fn put_texture(
&mut self,
idx: usize,
img: T,
device: &mut Device,
adapter: &mut Adapter,
allocator: &mut DynamicAllocator,
command_queue: &mut CommandQueue,
command_pool: &mut CommandPool,
) -> Result<(), &'static str> {
// TODO: Resizing, etc?
let chunk = &mut self.chunks[idx / CHUNK_SIZE];
chunk.put_texture(
img,
idx % CHUNK_SIZE,
device,
adapter,
allocator,
command_queue,
command_pool,
)
}
}