diff options
author | tcmal <me@aria.rip> | 2024-08-25 17:44:22 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-08-25 17:44:22 +0100 |
commit | e8126750633fd9ed3e682a2c889740a1f51a86f7 (patch) | |
tree | d2da548230f9ff3703fbf7f60f52f575f43d360d /stockton-render | |
parent | 6793abcd3bb054ff4c6dc0e6b97a76d463730deb (diff) |
feat(render): rendy-memory for textures
unfortunately this involves downgrading gfx-hal
Diffstat (limited to 'stockton-render')
-rw-r--r-- | stockton-render/Cargo.toml | 5 | ||||
-rw-r--r-- | stockton-render/src/draw/buffer.rs | 4 | ||||
-rw-r--r-- | stockton-render/src/draw/context.rs | 60 | ||||
-rw-r--r-- | stockton-render/src/draw/mod.rs | 1 | ||||
-rw-r--r-- | stockton-render/src/draw/pipeline.rs | 1 | ||||
-rw-r--r-- | stockton-render/src/draw/target.rs | 22 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/chunk.rs | 16 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/image.rs | 346 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/loader.rs | 42 | ||||
-rw-r--r-- | stockton-render/src/draw/ui/pipeline.rs | 3 | ||||
-rwxr-xr-x | stockton-render/src/draw/ui/texture.rs | 3 | ||||
-rw-r--r-- | stockton-render/src/draw/utils.rs | 38 | ||||
-rw-r--r-- | stockton-render/src/error.rs | 2 | ||||
-rw-r--r-- | stockton-render/src/types.rs | 3 |
14 files changed, 460 insertions, 86 deletions
diff --git a/stockton-render/Cargo.toml b/stockton-render/Cargo.toml index 7c69be6..2840015 100644 --- a/stockton-render/Cargo.toml +++ b/stockton-render/Cargo.toml @@ -9,7 +9,7 @@ stockton-input = { path = "../stockton-input" } stockton-levels = { path = "../stockton-levels" } stockton-types = { path = "../stockton-types" } winit = "^0.21" -gfx-hal = "^0.5" +gfx-hal = "0.4.1" arrayvec = "0.4.10" nalgebra-glm = "^0.6" shaderc = "^0.7" @@ -17,12 +17,13 @@ log = "0.4.0" image = "0.23.11" legion = { version = "^0.3" } egui = "^0.2" +rendy-memory = "0.5.2" [features] default = ["vulkan"] vulkan = ["gfx-backend-vulkan"] [dependencies.gfx-backend-vulkan] -version = "^0.5" +version = "0.4.1" features = ["x11"] optional = true diff --git a/stockton-render/src/draw/buffer.rs b/stockton-render/src/draw/buffer.rs index c202d04..5d88e25 100644 --- a/stockton-render/src/draw/buffer.rs +++ b/stockton-render/src/draw/buffer.rs @@ -23,7 +23,7 @@ use std::ops::{Index, IndexMut}; use hal::prelude::*; use hal::{ buffer::Usage, - memory::{Properties, Segment}, + memory::Properties, queue::Submission, MemoryTypeId, }; @@ -134,7 +134,7 @@ impl<'a, T: Sized> StagedBuffer<'a, T> { // Map it somewhere and get a slice to that memory let staged_mapped_memory = unsafe { - let ptr = device.map_memory(&staged_memory, Segment::ALL).unwrap(); // TODO + let ptr = device.map_memory(&staged_memory, 0..size_bytes).unwrap(); // TODO std::slice::from_raw_parts_mut(ptr as *mut T, size.try_into().unwrap()) }; diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index 4d700e5..8f4b549 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -26,6 +26,7 @@ use hal::{pool::CommandPoolCreateFlags, prelude::*}; use log::debug; use na::Mat4; use winit::window::Window; +use rendy_memory::DynamicConfig; use super::{ buffer::ModifiableBuffer, @@ -35,6 +36,7 @@ use super::{ target::{SwapchainProperties, TargetChain}, texture::TextureStore, ui::{do_render as do_render_ui, ensure_textures as ensure_textures_ui, UIPipeline, UIPoint}, + utils::find_memory_type_id, }; use crate::{error, types::*, window::UIState}; use stockton_levels::prelude::*; @@ -85,6 +87,10 @@ pub struct RenderingContext<'a> { /// Buffers used for drawing the UI ui_draw_buffers: ManuallyDrop<DrawBuffers<'a, UIPoint>>, + /// Memory allocator used for any sort of textures / maps + /// Guaranteed suitable for 2D RGBA images with `Optimal` tiling and `Usage::Sampled` + texture_allocator: ManuallyDrop<DynamicAllocator>, + /// View projection matrix pub(crate) vp_matrix: Mat4, @@ -152,10 +158,54 @@ impl<'a> RenderingContext<'a> { // UI Vertex and index buffers let ui_draw_buffers = DrawBuffers::new(&mut device, &adapter)?; + // Memory allocators + let mut texture_allocator = unsafe { + use hal::{ + memory::Properties, + image::{Kind, Tiling, Usage, ViewCapabilities}, + format::Format, + }; + + // 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 as u32, 16 as u32, 1, 1), + 1, + Format::Rgba8Srgb, + Tiling::Optimal, + Usage::SAMPLED, + ViewCapabilities::empty() + ).map_err(|_| error::CreationError::OutOfMemoryError)?; + + let type_mask = device.get_image_requirements(&img).type_mask; + + device.destroy_image(img); + + let props = Properties::DEVICE_LOCAL; + + DynamicAllocator::new( + find_memory_type_id(&adapter, type_mask, props).ok_or(error::CreationError::OutOfMemoryError)?, + props, + DynamicConfig { + block_size_granularity: 4 * 32 * 32, // 32x32 image + max_chunk_size: u64::pow(2, 63), + min_device_allocation: 4 * 32 * 32, + } + ) + }; + // Texture store let texture_store = TextureStore::new( &mut device, &mut adapter, + &mut texture_allocator, &mut queue_group.queues[0], &mut cmd_pool, file, @@ -165,6 +215,7 @@ impl<'a> RenderingContext<'a> { let ui_texture_store = TextureStore::new_empty( &mut device, &mut adapter, + &mut texture_allocator, &mut queue_group.queues[0], &mut cmd_pool, 1, // TODO @@ -225,6 +276,8 @@ impl<'a> RenderingContext<'a> { ui_draw_buffers: ManuallyDrop::new(ui_draw_buffers), ui_texture_store: ManuallyDrop::new(ui_texture_store), + + texture_allocator: ManuallyDrop::new(texture_allocator), vp_matrix: Mat4::identity(), @@ -305,6 +358,7 @@ impl<'a> RenderingContext<'a> { ui, &mut self.device, &mut self.adapter, + &mut self.texture_allocator, &mut self.queue_group.queues[0], &mut self.cmd_pool, ); @@ -379,8 +433,10 @@ impl<'a> core::ops::Drop for RenderingContext<'a> { ManuallyDrop::into_inner(read(&self.draw_buffers)).deactivate(&mut self.device); ManuallyDrop::into_inner(read(&self.ui_draw_buffers)).deactivate(&mut self.device); - ManuallyDrop::into_inner(read(&self.texture_store)).deactivate(&mut self.device); - ManuallyDrop::into_inner(read(&self.ui_texture_store)).deactivate(&mut self.device); + ManuallyDrop::into_inner(read(&self.texture_store)).deactivate(&mut self.device, &mut self.texture_allocator); + ManuallyDrop::into_inner(read(&self.ui_texture_store)).deactivate(&mut self.device, &mut self.texture_allocator); + + ManuallyDrop::into_inner(read(&self.texture_allocator)).dispose(); ManuallyDrop::into_inner(read(&self.target_chain)) .deactivate(&mut self.device, &mut self.cmd_pool); diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs index c96fc10..27e41cf 100644 --- a/stockton-render/src/draw/mod.rs +++ b/stockton-render/src/draw/mod.rs @@ -29,6 +29,7 @@ mod pipeline; mod render; mod texture; mod ui; +mod utils; pub use self::camera::calc_vp_matrix_system; pub use self::context::RenderingContext; diff --git a/stockton-render/src/draw/pipeline.rs b/stockton-render/src/draw/pipeline.rs index 2e45b61..df7ee4b 100644 --- a/stockton-render/src/draw/pipeline.rs +++ b/stockton-render/src/draw/pipeline.rs @@ -196,7 +196,6 @@ impl CompletePipeline { depth_clamping: false, depth_bias: None, conservative: true, - line_width: hal::pso::State::Static(1.0), }; // Depth stencil diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs index daf9942..02a7f84 100644 --- a/stockton-render/src/draw/target.rs +++ b/stockton-render/src/draw/target.rs @@ -32,7 +32,7 @@ use na::Mat4; use super::{ buffer::ModifiableBuffer, draw_buffers::{DrawBuffers, UVPoint}, pipeline::CompletePipeline, - texture::image::LoadedImage, ui::{UIPipeline, UIPoint}, + texture::image::DedicatedLoadedImage, ui::{UIPipeline, UIPoint}, }; use crate::types::*; @@ -134,7 +134,7 @@ pub struct TargetChain { pub properties: SwapchainProperties, /// The depth buffer/image used for drawing - pub depth_buffer: ManuallyDrop<LoadedImage>, + pub depth_buffer: ManuallyDrop<DedicatedLoadedImage>, /// Resources tied to each target frame in the swapchain pub targets: Box<[TargetResources]>, @@ -192,11 +192,11 @@ impl TargetChain { .map_err(|_| TargetChainCreationError::Todo)? }; - let depth_buffer: LoadedImage = { + let depth_buffer = { use hal::format::Aspects; use hal::image::SubresourceRange; - LoadedImage::new( + DedicatedLoadedImage::new( device, adapter, properties.depth_format, @@ -306,7 +306,7 @@ impl TargetChain { // Record commands unsafe { - use hal::buffer::{IndexBufferView, SubRange}; + use hal::buffer::IndexBufferView; use hal::command::{ ClearColor, ClearDepthStencil, ClearValue, CommandBufferFlags, SubpassContents, }; @@ -332,7 +332,7 @@ impl TargetChain { let vbufref: &<back::Backend as hal::Backend>::Buffer = draw_buffers.vertex_buffer.get_buffer(); - let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into(); + let vbufs: ArrayVec<[_; 1]> = [(vbufref, 0)].into(); let ibuf = draw_buffers.index_buffer.get_buffer(); (vbufs, ibuf) @@ -363,7 +363,7 @@ impl TargetChain { target.cmd_buffer.bind_vertex_buffers(0, vbufs); target.cmd_buffer.bind_index_buffer(IndexBufferView { buffer: ibuf, - range: SubRange::WHOLE, + offset: 0, index_type: hal::IndexType::U16, }); }; @@ -391,7 +391,7 @@ impl TargetChain { // Record commands unsafe { - use hal::buffer::{IndexBufferView, SubRange}; + use hal::buffer::IndexBufferView; use hal::command::{ClearColor, ClearValue, SubpassContents}; // Colour to clear window to @@ -406,7 +406,7 @@ impl TargetChain { let vbufref: &<back::Backend as hal::Backend>::Buffer = draw_buffers.vertex_buffer.get_buffer(); - let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into(); + let vbufs: ArrayVec<[_; 1]> = [(vbufref, 0)].into(); let ibuf = draw_buffers.index_buffer.get_buffer(); (vbufs, ibuf) @@ -426,7 +426,7 @@ impl TargetChain { target.cmd_buffer.bind_vertex_buffers(0, vbufs); target.cmd_buffer.bind_index_buffer(IndexBufferView { buffer: ibuf, - range: SubRange::WHOLE, + offset: 0, index_type: hal::IndexType::U16, }); }; @@ -611,7 +611,7 @@ pub enum TargetChainCreationError { #[derive(Debug)] pub enum TargetResourcesCreationError { - ImageViewError(hal::image::ViewCreationError), + ImageViewError(hal::image::ViewError), FrameBufferNoMemory, SyncObjectsNoMemory, } diff --git a/stockton-render/src/draw/texture/chunk.rs b/stockton-render/src/draw/texture/chunk.rs index f1b88e9..b95e863 100644 --- a/stockton-render/src/draw/texture/chunk.rs +++ b/stockton-render/src/draw/texture/chunk.rs @@ -47,6 +47,7 @@ impl TextureChunk { pub fn new_empty( device: &mut Device, adapter: &mut Adapter, + allocator: &mut DynamicAllocator, command_queue: &mut CommandQueue, command_pool: &mut CommandPool, descriptor_set: DescriptorSet, @@ -64,6 +65,7 @@ impl TextureChunk { i, device, adapter, + allocator, command_queue, command_pool, ) @@ -78,6 +80,7 @@ impl TextureChunk { pub fn new<'a, I, R: TextureResolver<T>, T: LoadableImage>( device: &mut Device, adapter: &mut Adapter, + allocator: &mut DynamicAllocator, command_queue: &mut CommandQueue, command_pool: &mut CommandPool, descriptor_set: DescriptorSet, @@ -98,11 +101,11 @@ impl TextureChunk { for tex in textures { if let Some(img) = resolver.resolve(tex) { store - .put_texture(img, local_idx, device, adapter, command_queue, command_pool) + .put_texture(img, local_idx, device, adapter, allocator, command_queue, command_pool) .unwrap(); } else { // Texture not found. For now, tear everything down. - store.deactivate(device); + store.deactivate(device, allocator); return Err(error::CreationError::BadDataError); } @@ -119,6 +122,7 @@ impl TextureChunk { local_idx, device, adapter, + allocator, command_queue, command_pool, ) @@ -136,6 +140,7 @@ impl TextureChunk { idx: usize, device: &mut Device, adapter: &mut Adapter, + allocator: &mut DynamicAllocator, command_queue: &mut CommandQueue, command_pool: &mut CommandPool, ) -> Result<(), &'static str> { @@ -144,6 +149,7 @@ impl TextureChunk { image, device, adapter, + allocator, command_queue, command_pool, hal::format::Format::Rgba8Srgb, // TODO @@ -177,7 +183,7 @@ impl TextureChunk { // Store it so we can safely deactivate it when we need to // Deactivate the old image if we need to if idx < self.sampled_images.len() { - replace(&mut self.sampled_images[idx], texture).deactivate(device); + replace(&mut self.sampled_images[idx], texture).deactivate(device, allocator); } else { self.sampled_images.push(texture); } @@ -185,9 +191,9 @@ impl TextureChunk { Ok(()) } - pub fn deactivate(mut self, device: &mut Device) { + pub fn deactivate(mut self, device: &mut Device, allocator: &mut DynamicAllocator) { for img in self.sampled_images.drain(..) { - img.deactivate(device); + img.deactivate(device, allocator); } } } diff --git a/stockton-render/src/draw/texture/image.rs b/stockton-render/src/draw/texture/image.rs index 37e8ce8..bd8058b 100644 --- a/stockton-render/src/draw/texture/image.rs +++ b/stockton-render/src/draw/texture/image.rs @@ -23,13 +23,14 @@ use hal::{ buffer::Usage as BufUsage, format::{Aspects, Format, Swizzle}, image::{SubresourceRange, Usage as ImgUsage, ViewKind}, - memory::{Dependencies as MemDependencies, Properties as MemProperties, Segment}, + memory::{Dependencies as MemDependencies, Properties as MemProperties}, prelude::*, queue::Submission, MemoryTypeId, }; use image::RgbaImage; use std::{convert::TryInto, iter::once}; +use rendy_memory::{Allocator, Block}; use crate::draw::buffer::create_buffer; use crate::types::*; @@ -73,17 +74,18 @@ pub struct LoadedImage { pub image_view: ManuallyDrop<ImageView>, /// The memory backing the image - memory: ManuallyDrop<Memory>, + memory: ManuallyDrop<DynamicBlock>, } pub fn create_image_view( device: &mut Device, adapter: &Adapter, + allocator: &mut DynamicAllocator, format: Format, usage: ImgUsage, width: usize, height: usize, -) -> Result<(Memory, Image), &'static str> { +) -> Result<(DynamicBlock, Image), &'static str> { // Round up the size to align properly let initial_row_size = PIXEL_SIZE * width; let limits = adapter.physical_device.limits(); @@ -108,47 +110,37 @@ pub fn create_image_view( .map_err(|_| "Couldn't create image")?; // Allocate memory - let memory = unsafe { + let (block, _) = unsafe { let requirements = device.get_image_requirements(&image_ref); - let memory_type_id = adapter - .physical_device - .memory_properties() - .memory_types - .iter() - .enumerate() - .find(|&(id, memory_type)| { - requirements.type_mask & (1 << id) != 0 - && memory_type.properties.contains(MemProperties::DEVICE_LOCAL) - }) - .map(|(id, _)| MemoryTypeId(id)) - .ok_or("Couldn't find a memory type for image memory")?; - - let memory = device - .allocate_memory(memory_type_id, requirements.size) - .map_err(|_| "Couldn't allocate image memory")?; + allocator.alloc( + device, + requirements.size, + requirements.alignment + ) + }.map_err(|_| "Out of memory")?; + unsafe { device - .bind_image_memory(&memory, 0, &mut image_ref) + .bind_image_memory(&block.memory(), block.range().start, &mut image_ref) .map_err(|_| "Couldn't bind memory to image")?; + } - Ok(memory) - }?; - - Ok((memory, image_ref)) + Ok((block, image_ref)) } impl LoadedImage { pub fn new( device: &mut Device, adapter: &Adapter, + allocator: &mut DynamicAllocator, format: Format, usage: ImgUsage, resources: SubresourceRange, width: usize, height: usize, ) -> Result<LoadedImage, &'static str> { - let (memory, image_ref) = create_image_view(device, adapter, format, usage, width, height)?; + let (memory, image_ref) = create_image_view(device, adapter, allocator, format, usage, width, height)?; // Create ImageView and sampler let image_view = unsafe { @@ -169,6 +161,7 @@ impl LoadedImage { img: T, device: &mut Device, adapter: &Adapter, + allocator: &mut DynamicAllocator, command_queue: &mut CommandQueue, command_pool: &mut CommandPool, ) -> Result<(), &'static str> { @@ -194,7 +187,7 @@ impl LoadedImage { // Copy everything into it unsafe { let mapped_memory: *mut u8 = std::mem::transmute(device - .map_memory(&staging_memory, Segment::ALL) + .map_memory(&staging_memory, 0..total_size) .map_err(|_| "Couldn't map buffer memory")?); for y in 0..img.height() as usize { @@ -315,6 +308,7 @@ impl LoadedImage { img: T, device: &mut Device, adapter: &Adapter, + allocator: &mut DynamicAllocator, command_queue: &mut CommandQueue, command_pool: &mut CommandPool, format: Format, @@ -323,6 +317,7 @@ impl LoadedImage { let mut loaded_image = Self::new( device, adapter, + allocator, format, usage | ImgUsage::TRANSFER_DST, SubresourceRange { @@ -333,20 +328,20 @@ impl LoadedImage { img.width() as usize, img.height() as usize, )?; - loaded_image.load(img, device, adapter, command_queue, command_pool)?; + loaded_image.load(img, device, adapter, allocator, command_queue, command_pool)?; Ok(loaded_image) } /// Properly frees/destroys all the objects in this struct /// Dropping without doing this is a bad idea - pub fn deactivate(self, device: &Device) { + pub fn deactivate(self, device: &mut Device, allocator: &mut DynamicAllocator) { unsafe { use core::ptr::read; device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view))); device.destroy_image(ManuallyDrop::into_inner(read(&self.image))); - device.free_memory(ManuallyDrop::into_inner(read(&self.memory))); + allocator.free(device, ManuallyDrop::into_inner(read(&self.memory))); } } } @@ -360,6 +355,7 @@ impl SampledImage { pub fn new( device: &mut Device, adapter: &Adapter, + allocator: &mut DynamicAllocator, format: Format, usage: ImgUsage, width: usize, @@ -368,6 +364,7 @@ impl SampledImage { let image = LoadedImage::new( device, adapter, + allocator, format, usage | ImgUsage::SAMPLED, SubresourceRange { @@ -396,6 +393,7 @@ impl SampledImage { img: T, device: &mut Device, adapter: &Adapter, + allocator: &mut DynamicAllocator, command_queue: &mut CommandQueue, command_pool: &mut CommandPool, format: Format, @@ -404,6 +402,7 @@ impl SampledImage { let mut sampled_image = SampledImage::new( device, adapter, + allocator, format, usage | ImgUsage::TRANSFER_DST, img.width() as usize, @@ -411,18 +410,299 @@ impl SampledImage { )?; sampled_image .image - .load(img, device, adapter, command_queue, command_pool)?; + .load(img, device, adapter, allocator, command_queue, command_pool)?; Ok(sampled_image) } - pub fn deactivate(self, device: &mut Device) { + pub fn deactivate(self, device: &mut Device, allocator: &mut DynamicAllocator) { unsafe { use core::ptr::read; device.destroy_sampler(ManuallyDrop::into_inner(read(&self.sampler))); - ManuallyDrop::into_inner(read(&self.image)).deactivate(device); + ManuallyDrop::into_inner(read(&self.image)).deactivate(device, allocator); } } } + +/// Holds an image that's loaded into GPU memory dedicated only to that image, bypassing the memory allocator. +pub struct DedicatedLoadedImage { + /// The GPU Image handle + image: ManuallyDrop<Image>, + + /// The full view of the image + pub image_view: ManuallyDrop<ImageView>, + + /// The memory backing the image + memory: ManuallyDrop<Memory>, +} + + +impl DedicatedLoadedImage { + pub fn new( + device: &mut Device, + adapter: &Adapter, + format: Format, + usage: ImgUsage, + resources: SubresourceRange, + width: usize, + height: usize, + ) -> Result<DedicatedLoadedImage, &'static str> { + let (memory, image_ref) = { + // Round up the size to align properly + let initial_row_size = PIXEL_SIZE * width; + let limits = adapter.physical_device.limits(); + let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; + + let row_size = ((initial_row_size as u32 + row_alignment_mask) & !row_alignment_mask) as usize; + debug_assert!(row_size as usize >= initial_row_size); + + // Make the image + let mut image_ref = unsafe { + use hal::image::{Kind, Tiling, ViewCapabilities}; + + device.create_image( + Kind::D2(width as u32, height as u32, 1, 1), + 1, + format, + Tiling::Optimal, + usage, + ViewCapabilities::empty(), + ) + } + .map_err(|_| "Couldn't create image")?; + + // Allocate memory + + // Allocate memory + let memory = unsafe { + let requirements = device.get_image_requirements(&image_ref); + + let memory_type_id = adapter + .physical_device + .memory_properties() + .memory_types + .iter() + .enumerate() + .find(|&(id, memory_type)| { + requirements.type_mask & (1 << id) != 0 + && memory_type.properties.contains(MemProperties::DEVICE_LOCAL) + }) + .map(|(id, _)| MemoryTypeId(id)) + .ok_or("Couldn't find a memory type for image memory")?; + + let memory = device + .allocate_memory(memory_type_id, requirements.size) + .map_err(|_| "Couldn't allocate image memory")?; + + device + .bind_image_memory(&memory, 0, &mut image_ref) + .map_err(|_| "Couldn't bind memory to image")?; + + Ok(memory) + }?; + + Ok((memory, image_ref)) + }?; + + // Create ImageView and sampler + let image_view = unsafe { + device.create_image_view(&image_ref, ViewKind::D2, format, Swizzle::NO, resources) + } + .map_err(|_| "Couldn't create the image view!")?; + + Ok(DedicatedLoadedImage { + image: ManuallyDrop::new(image_ref), + image_view: ManuallyDrop::new(image_view), + memory: ManuallyDrop::new(memory), + }) + } + + /// Load the given image + pub fn load<T: LoadableImage>( + &mut self, + img: T, + device: &mut Device, + adapter: &Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, + ) -> Result<(), &'static str> { + let initial_row_size = PIXEL_SIZE * img.width() as usize; + let limits = adapter.physical_device.limits(); + let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment 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); + + // Make a staging buffer + let (staging_buffer, staging_memory) = create_buffer( + device, + adapter, + BufUsage::TRANSFER_SRC, + MemProperties::CPU_VISIBLE | MemProperties::COHERENT, + total_size, + ) + .map_err(|_| "Couldn't create staging buffer")?; + + // Copy everything into it + unsafe { + let mapped_memory: *mut u8 = std::mem::transmute(device + .map_memory(&staging_memory, 0..total_size) + .map_err(|_| "Couldn't map buffer memory")?); + + for y in 0..img.height() as usize { + let dest_base: isize = (y * row_size).try_into().unwrap(); + img.copy_row(y as u32, mapped_memory.offset(dest_base)); + } + + device.unmap_memory(&staging_memory); + } + + // Copy from staging to image memory + let buf = unsafe { + use hal::command::{BufferImageCopy, CommandBufferFlags}; + use hal::image::{Access, Extent, Layout, Offset, SubresourceLayers}; + use hal::memory::Barrier; + use hal::pso::PipelineStage; + + // Get a command buffer + let mut buf = command_pool.allocate_one(hal::command::Level::Primary); + buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); + + // Setup the layout of our image for copying + let image_barrier = Barrier::Image { + states: (Access::empty(), Layout::Undefined) + ..(Access::TRANSFER_WRITE, Layout::TransferDstOptimal), + target: &(*self.image), + families: None, + range: SubresourceRange { + aspects: Aspects::COLOR, + levels: 0..1, + layers: 0..1, + }, + }; + buf.pipeline_barrier( + PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER, + MemDependencies::empty(), + &[image_barrier], + ); + + // Copy from buffer to image + buf.copy_buffer_to_image( + &staging_buffer, + &(*self.image), + Layout::TransferDstOptimal, + &[BufferImageCopy { + buffer_offset: 0, + buffer_width: (row_size / PIXEL_SIZE) as u32, + buffer_height: img.height(), + image_layers: SubresourceLayers { + aspects: Aspects::COLOR, + level: 0, + layers: 0..1, + }, + image_offset: Offset { x: 0, y: 0, z: 0 }, + image_extent: Extent { + width: img.width(), + height: img.height(), + depth: 1, + }, + }], + ); + + // Setup the layout of our image for shaders + let image_barrier = Barrier::Image { + states: (Access::TRANSFER_WRITE, Layout::TransferDstOptimal) + ..(Access::SHADER_READ, Layout::ShaderReadOnlyOptimal), + target: &(*self.image), + families: None, + range: SubresourceRange { + aspects: Aspects::COLOR, + levels: 0..1, + layers: 0..1, + }, + }; + + buf.pipeline_barrier( + PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER, + MemDependencies::empty(), + &[image_barrier], + ); + + buf.finish(); + + buf + }; + + // Submit our commands and wait for them to finish + unsafe { + let setup_finished = device.create_fence(false).unwrap(); + command_queue.submit::<_, _, Semaphore, _, _>( + Submission { + command_buffers: &[&buf], + wait_semaphores: std::iter::empty::<_>(), + signal_semaphores: std::iter::empty::<_>(), + }, + Some(&setup_finished), + ); + + device + .wait_for_fence(&setup_finished, core::u64::MAX) + .unwrap(); + device.destroy_fence(setup_finished); + }; + + // Clean up temp resources + unsafe { + command_pool.free(once(buf)); + + device.free_memory(staging_memory); + device.destroy_buffer(staging_buffer); + } + + Ok(()) + } + + /// Load the given image into a new buffer + pub fn load_into_new<T: LoadableImage>( + img: T, + device: &mut Device, + adapter: &Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool, + format: Format, + usage: ImgUsage, + ) -> Result<DedicatedLoadedImage, &'static str> { + let mut loaded_image = Self::new( + device, + adapter, + format, + usage | ImgUsage::TRANSFER_DST, + SubresourceRange { + aspects: Aspects::COLOR, + levels: 0..1, + layers: 0..1, + }, + img.width() as usize, + img.height() as usize, + )?; + loaded_image.load(img, device, adapter, command_queue, command_pool)?; + + Ok(loaded_image) + } + + /// Properly frees/destroys all the objects in this struct + /// Dropping without doing this is a bad idea + pub fn deactivate(self, device: &mut Device) { + unsafe { + use core::ptr::read; + + device.destroy_image_view(ManuallyDrop::into_inner(read(&self.image_view))); + device.destroy_image(ManuallyDrop::into_inner(read(&self.image))); + device.free_memory(ManuallyDrop::into_inner(read(&self.memory))); + } + } +}
\ No newline at end of file diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs index 30c89dc..9385d96 100644 --- a/stockton-render/src/draw/texture/loader.rs +++ b/stockton-render/src/draw/texture/loader.rs @@ -47,6 +47,7 @@ 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, @@ -64,7 +65,7 @@ impl TextureStore { // Descriptor pool, where we get our sets from let mut descriptor_pool = unsafe { use hal::pso::{ - DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType, ImageDescriptorType, + DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType, }; device @@ -72,11 +73,7 @@ impl TextureStore { num_chunks, &[ DescriptorRangeDesc { - ty: DescriptorType::Image { - ty: ImageDescriptorType::Sampled { - with_sampler: false, - }, - }, + ty: DescriptorType::SampledImage, count: rounded_size, }, DescriptorRangeDesc { @@ -95,18 +92,14 @@ impl TextureStore { // Layout of our descriptor sets let descriptor_set_layout = unsafe { use hal::pso::{ - DescriptorSetLayoutBinding, DescriptorType, ImageDescriptorType, ShaderStageFlags, + DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags, }; device.create_descriptor_set_layout( &[ DescriptorSetLayoutBinding { binding: 0, - ty: DescriptorType::Image { - ty: ImageDescriptorType::Sampled { - with_sampler: false, - }, - }, + ty: DescriptorType::SampledImage, count: CHUNK_SIZE, stage_flags: ShaderStageFlags::FRAGMENT, immutable_samplers: false, @@ -140,6 +133,7 @@ impl TextureStore { chunks.push(TextureChunk::new_empty( device, adapter, + allocator, command_queue, command_pool, descriptor_set, @@ -159,6 +153,7 @@ impl TextureStore { pub fn new<T: HasTextures>( device: &mut Device, adapter: &mut Adapter, + allocator: &mut DynamicAllocator, command_queue: &mut CommandQueue, command_pool: &mut CommandPool, file: &T, @@ -177,7 +172,7 @@ impl TextureStore { // Descriptor pool, where we get our sets from let mut descriptor_pool = unsafe { use hal::pso::{ - DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType, ImageDescriptorType, + DescriptorPoolCreateFlags, DescriptorRangeDesc, DescriptorType, }; device @@ -185,11 +180,7 @@ impl TextureStore { num_chunks, &[ DescriptorRangeDesc { - ty: DescriptorType::Image { - ty: ImageDescriptorType::Sampled { - with_sampler: false, - }, - }, + ty: DescriptorType::SampledImage, count: rounded_size, }, DescriptorRangeDesc { @@ -208,18 +199,14 @@ impl TextureStore { // Layout of our descriptor sets let descriptor_set_layout = unsafe { use hal::pso::{ - DescriptorSetLayoutBinding, DescriptorType, ImageDescriptorType, ShaderStageFlags, + DescriptorSetLayoutBinding, DescriptorType, ShaderStageFlags, }; device.create_descriptor_set_layout( &[ DescriptorSetLayoutBinding { binding: 0, - ty: DescriptorType::Image { - ty: ImageDescriptorType::Sampled { - with_sampler: false, - }, - }, + ty: DescriptorType::SampledImage, count: CHUNK_SIZE, stage_flags: ShaderStageFlags::FRAGMENT, immutable_samplers: false, @@ -254,6 +241,7 @@ impl TextureStore { chunks.push(TextureChunk::new( device, adapter, + allocator, command_queue, command_pool, descriptor_set, @@ -272,12 +260,12 @@ impl TextureStore { } /// Call this before dropping - pub fn deactivate(mut self, device: &mut Device) { + 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) + chunk.deactivate(device, allocator); } self.descriptor_pool.reset(); @@ -299,6 +287,7 @@ impl TextureStore { img: T, device: &mut Device, adapter: &mut Adapter, + allocator: &mut DynamicAllocator, command_queue: &mut CommandQueue, command_pool: &mut CommandPool, ) -> Result<(), &'static str> { @@ -309,6 +298,7 @@ impl TextureStore { idx % CHUNK_SIZE, device, adapter, + allocator, command_queue, command_pool, ) diff --git a/stockton-render/src/draw/ui/pipeline.rs b/stockton-render/src/draw/ui/pipeline.rs index 878d8cd..f608173 100644 --- a/stockton-render/src/draw/ui/pipeline.rs +++ b/stockton-render/src/draw/ui/pipeline.rs @@ -97,7 +97,7 @@ impl UIPipeline { let external_dependency = SubpassDependency { flags: Dependencies::empty(), - passes: None..Some(0), + passes: SubpassRef::External..SubpassRef::Pass(0), stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT ..(PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS), @@ -198,7 +198,6 @@ impl UIPipeline { depth_clamping: false, depth_bias: None, conservative: true, - line_width: hal::pso::State::Static(1.0), }; // Depth stencil diff --git a/stockton-render/src/draw/ui/texture.rs b/stockton-render/src/draw/ui/texture.rs index ce18f02..52580e5 100755 --- a/stockton-render/src/draw/ui/texture.rs +++ b/stockton-render/src/draw/ui/texture.rs @@ -43,12 +43,13 @@ impl LoadableImage for &Texture { pub fn ensure_textures(texture_store: &mut TextureStore, ui: &mut UIState, device: &mut Device, adapter: &mut Adapter, + allocator: &mut DynamicAllocator, command_queue: &mut CommandQueue, command_pool: &mut CommandPool) { let tex = ui.ctx.texture(); if tex.version != ui.last_tex_ver { - texture_store.put_texture(0, &*tex, device, adapter, command_queue, command_pool).unwrap(); // TODO + texture_store.put_texture(0, &*tex, device, adapter, allocator, command_queue, command_pool).unwrap(); // TODO ui.last_tex_ver = tex.version; } }
\ No newline at end of file diff --git a/stockton-render/src/draw/utils.rs b/stockton-render/src/draw/utils.rs new file mode 100644 index 0000000..e175e47 --- /dev/null +++ b/stockton-render/src/draw/utils.rs @@ -0,0 +1,38 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + */ + +use crate::types::*; +use hal::{ + memory::{Properties as MemProperties}, + MemoryTypeId, + prelude::*, +}; + +pub fn find_memory_type_id(adapter: &Adapter, type_mask: u64, props: MemProperties) -> Option<MemoryTypeId> { + adapter + .physical_device + .memory_properties() + .memory_types + .iter() + .enumerate() + .find(|&(id, memory_type)| { + type_mask & (1 << id) != 0 && + memory_type.properties.contains(props) + }) + .map(|(id, _)| MemoryTypeId(id)) + +}
\ No newline at end of file diff --git a/stockton-render/src/error.rs b/stockton-render/src/error.rs index e0de55d..654df4a 100644 --- a/stockton-render/src/error.rs +++ b/stockton-render/src/error.rs @@ -41,7 +41,7 @@ pub enum CreationError { BufferNoMemory, SwapchainError(hal::window::CreationError), - ImageViewError(hal::image::ViewCreationError), + ImageViewError(hal::image::ViewError), BadDataError, } diff --git a/stockton-render/src/types.rs b/stockton-render/src/types.rs index 56a71b7..f6187c2 100644 --- a/stockton-render/src/types.rs +++ b/stockton-render/src/types.rs @@ -41,3 +41,6 @@ pub type RenderPass = <back::Backend as hal::Backend>::RenderPass; pub type Adapter = hal::adapter::Adapter<back::Backend>; pub type QueueGroup = hal::queue::QueueGroup<back::Backend>; + +pub type DynamicAllocator = rendy_memory::DynamicAllocator<back::Backend>; +pub type DynamicBlock = rendy_memory::DynamicBlock<back::Backend>;
\ No newline at end of file |