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 | d7d0f0277c437004ed476393542da39c84c5cf9f (patch) | |
tree | 65c369e00fc1416c74019fde4456b0bf90d3067c /stockton-render | |
parent | 10b3d4ac59e826b31d2114999e31893390acfb9c (diff) |
chore(render): update hal and fix some errors
Diffstat (limited to 'stockton-render')
22 files changed, 1030 insertions, 894 deletions
diff --git a/stockton-render/Cargo.toml b/stockton-render/Cargo.toml index 0348c60..ecb08f3 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.4.1" +gfx-hal = "^0.8.0" arrayvec = "0.4.10" nalgebra-glm = "^0.6" shaderc = "^0.7" @@ -17,8 +17,8 @@ log = "0.4.0" image = "0.23.11" legion = { version = "^0.3" } egui = "^0.2" -rendy-memory = "0.5.2" -rendy-descriptor = "0.5.1" +rendy-memory = { path = "../rendy-memory" } +rendy-descriptor = { path = "../rendy-descriptor" } anyhow = "1.0.40" thiserror = "1.0.25" @@ -27,6 +27,5 @@ default = ["vulkan"] vulkan = ["gfx-backend-vulkan"] [dependencies.gfx-backend-vulkan] -version = "0.4.1" -features = ["x11"] +version = "^0.8.0" optional = true diff --git a/stockton-render/src/draw/buffer.rs b/stockton-render/src/draw/buffer.rs index 34400a6..227bb12 100644 --- a/stockton-render/src/draw/buffer.rs +++ b/stockton-render/src/draw/buffer.rs @@ -1,10 +1,13 @@ use core::mem::{size_of, ManuallyDrop}; use std::convert::TryInto; -use std::iter::once; +use std::iter::{empty, once}; use std::ops::{Index, IndexMut}; -use hal::prelude::*; -use hal::{buffer::Usage, memory::Properties, queue::Submission, MemoryTypeId}; +use hal::{ + buffer::Usage, + memory::{Properties, Segment, SparseFlags}, + MemoryTypeId, +}; use crate::error::CreationError; use crate::types::*; @@ -12,14 +15,14 @@ use crate::types::*; /// Create a buffer of the given specifications, allocating more device memory. // TODO: Use a different memory allocator? pub(crate) fn create_buffer( - device: &mut Device, + device: &mut DeviceT, adapter: &Adapter, usage: Usage, properties: Properties, size: u64, -) -> Result<(Buffer, Memory), CreationError> { - let mut buffer = - unsafe { device.create_buffer(size, usage) }.map_err(CreationError::BufferError)?; +) -> Result<(BufferT, MemoryT), CreationError> { + let mut buffer = unsafe { device.create_buffer(size, usage, SparseFlags::empty()) } + .map_err(CreationError::BufferError)?; let requirements = unsafe { device.get_buffer_requirements(&buffer) }; let memory_type_id = adapter @@ -46,30 +49,30 @@ pub(crate) fn create_buffer( /// A buffer that can be modified by the CPU pub trait ModifiableBuffer: IndexMut<usize> { /// Get a handle to the underlying GPU buffer - fn get_buffer(&mut self) -> &Buffer; + fn get_buffer(&mut self) -> &BufferT; /// Commit all changes to GPU memory, returning a handle to the GPU buffer fn commit<'a>( &'a mut self, - device: &Device, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool, - ) -> &'a Buffer; + device: &DeviceT, + command_queue: &mut QueueT, + command_pool: &mut CommandPoolT, + ) -> &'a BufferT; } /// A GPU buffer that is written to using a staging buffer pub struct StagedBuffer<'a, T: Sized> { /// CPU-visible buffer - staged_buffer: ManuallyDrop<Buffer>, + staged_buffer: ManuallyDrop<BufferT>, /// CPU-visible memory - staged_memory: ManuallyDrop<Memory>, + staged_memory: ManuallyDrop<MemoryT>, /// GPU Buffer - buffer: ManuallyDrop<Buffer>, + buffer: ManuallyDrop<BufferT>, /// GPU Memory - memory: ManuallyDrop<Memory>, + memory: ManuallyDrop<MemoryT>, /// Where staged buffer is mapped in CPU memory staged_mapped_memory: &'a mut [T], @@ -84,7 +87,7 @@ pub struct StagedBuffer<'a, T: Sized> { impl<'a, T: Sized> StagedBuffer<'a, T> { /// size is the size in T pub fn new( - device: &mut Device, + device: &mut DeviceT, adapter: &Adapter, usage: Usage, size: u64, @@ -93,7 +96,7 @@ impl<'a, T: Sized> StagedBuffer<'a, T> { let size_bytes = size * size_of::<T>() as u64; // Get CPU-visible buffer - let (staged_buffer, staged_memory) = create_buffer( + let (staged_buffer, mut staged_memory) = create_buffer( device, adapter, Usage::TRANSFER_SRC, @@ -112,7 +115,15 @@ 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, 0..size_bytes).unwrap(); // TODO + let ptr = device + .map_memory( + &mut staged_memory, + Segment { + offset: 0, + size: Some(size_bytes), + }, + ) + .unwrap(); // TODO std::slice::from_raw_parts_mut(ptr as *mut T, size.try_into().unwrap()) }; @@ -129,9 +140,9 @@ impl<'a, T: Sized> StagedBuffer<'a, T> { } /// Call this before dropping - pub(crate) fn deactivate(mut self, device: &mut Device) { + pub(crate) fn deactivate(mut self, device: &mut DeviceT) { unsafe { - device.unmap_memory(&self.staged_memory); + device.unmap_memory(&mut self.staged_memory); device.free_memory(ManuallyDrop::take(&mut self.staged_memory)); device.destroy_buffer(ManuallyDrop::take(&mut self.staged_buffer)); @@ -143,16 +154,16 @@ impl<'a, T: Sized> StagedBuffer<'a, T> { } impl<'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> { - fn get_buffer(&mut self) -> &Buffer { + fn get_buffer(&mut self) -> &BufferT { &self.buffer } fn commit<'b>( &'b mut self, - device: &Device, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool, - ) -> &'b Buffer { + device: &DeviceT, + command_queue: &mut QueueT, + command_pool: &mut CommandPoolT, + ) -> &'b BufferT { // Only commit if there's changes to commit. if self.staged_is_dirty { // Copy from staged to buffer @@ -166,11 +177,11 @@ impl<'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> { buf.copy_buffer( &self.staged_buffer, &self.buffer, - &[BufferCopy { + std::iter::once(BufferCopy { src: 0, dst: 0, size: ((self.highest_used + 1) * size_of::<T>()) as u64, - }], + }), ); buf.finish(); @@ -181,15 +192,14 @@ impl<'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> { // TODO: We could use more semaphores or something? // TODO: Better error handling unsafe { - let copy_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(©_finished), - ); + let mut copy_finished = device.create_fence(false).unwrap(); + command_queue + .submit::<std::iter::Once<_>, std::iter::Empty<_>, std::iter::Empty<_>>( + once(&buf), + empty::<(&SemaphoreT, hal::pso::PipelineStage)>(), + empty::<&SemaphoreT>(), + Some(&mut copy_finished), + ); device .wait_for_fence(©_finished, core::u64::MAX) diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index 87fb0a2..21a69fa 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -3,12 +3,13 @@ //! You'll need something else to actually find/sort the faces though. use std::{ + iter::once, mem::ManuallyDrop, sync::{Arc, RwLock}, }; use arrayvec::ArrayVec; -use hal::{pool::CommandPoolCreateFlags, prelude::*}; +use hal::{memory::SparseFlags, pool::CommandPoolCreateFlags}; use log::debug; use na::Mat4; use rendy_memory::DynamicConfig; @@ -18,6 +19,7 @@ use super::{ buffer::ModifiableBuffer, draw_buffers::{DrawBuffers, UvPoint}, pipeline::CompletePipeline, + queue_negotiator::QueueNegotiator, render::do_render, target::{SwapchainProperties, TargetChain}, texture::{resolver::BasicFsResolver, TextureRepo}, @@ -41,15 +43,11 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> { instance: ManuallyDrop<back::Instance>, /// Device we're using - device: Arc<RwLock<Device>>, + device: Arc<RwLock<DeviceT>>, /// Adapter we're using adapter: Adapter, - // Render destination - /// Surface to draw to - surface: ManuallyDrop<Surface>, - /// Swapchain and stuff pub(crate) target_chain: ManuallyDrop<TargetChain>, @@ -61,10 +59,10 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> { // Command pool and buffers /// The command pool used for our buffers - cmd_pool: ManuallyDrop<CommandPool>, + cmd_pool: ManuallyDrop<CommandPoolT>, - /// The queue group our buffers belong to - queue_group: QueueGroup, + /// The queue to use for drawing + queue: Arc<RwLock<QueueT>>, /// Main Texture repo tex_repo: ManuallyDrop<TextureRepo<'a>>, @@ -93,9 +91,7 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { pub fn new(window: &Window, map: M) -> Result<Self, error::CreationError> { let map = Arc::new(RwLock::new(map)); // Create surface - let (instance, mut surface, mut adapters) = unsafe { - use hal::Instance; - + let (instance, surface, mut adapters) = unsafe { let instance = back::Instance::create("stockton", 1) .map_err(|_| error::CreationError::WindowError)?; let surface = instance @@ -109,31 +105,44 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { // TODO: Properly figure out which adapter to use let adapter = adapters.remove(0); + let mut draw_queue_negotiator = QueueNegotiator::find(&adapter, |family| { + surface.supports_queue_family(family) && family.queue_type().supports_graphics() + }) + .unwrap(); + + let mut tex_queue_negotiator = + QueueNegotiator::find(&adapter, TextureRepo::queue_family_filter).unwrap(); // Device & Queue group - let (device_lock, queue_group) = { - let family = adapter - .queue_families - .iter() - .find(|family| { - surface.supports_queue_family(family) && family.queue_type().supports_graphics() - }) - .unwrap(); - - let mut gpu = unsafe { + let (device_lock, mut queue_groups) = { + debug!( + "Using draw queue family {:?}", + draw_queue_negotiator.family_id() + ); + debug!( + "Using tex queue family {:?}", + tex_queue_negotiator.family_id() + ); + + let gpu = unsafe { adapter .physical_device - .open(&[(family, &[1.0])], hal::Features::empty()) + .open( + &[ + (draw_queue_negotiator.family(&adapter), &[1.0]), + (tex_queue_negotiator.family(&adapter), &[1.0]), + ], + hal::Features::empty(), + ) .unwrap() }; - ( - Arc::new(RwLock::new(gpu.device)), - gpu.queue_groups.pop().unwrap(), - ) + (Arc::new(RwLock::new(gpu.device)), gpu.queue_groups) }; let mut device = device_lock.write().unwrap(); + let device_props = adapter.physical_device.properties(); + // Figure out what our swapchain will look like let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface) .map_err(|_| error::CreationError::BadSurface)?; @@ -145,7 +154,10 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { // Command pool let mut cmd_pool = unsafe { - device.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL) + device.create_command_pool( + draw_queue_negotiator.family_id(), + CommandPoolCreateFlags::RESET_INDIVIDUAL, + ) } .map_err(|_| error::CreationError::OutOfMemoryError)?; @@ -179,6 +191,7 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { Format::Rgba8Srgb, Tiling::Optimal, Usage::SAMPLED, + SparseFlags::empty(), ViewCapabilities::empty(), ) .map_err(|_| error::CreationError::OutOfMemoryError)?; @@ -198,22 +211,29 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { max_chunk_size: u64::pow(2, 63), min_device_allocation: 4 * 32 * 32, }, + device_props.limits.non_coherent_atom_size as u64, ) }; drop(device); // Texture repos + debug!("Creating 3D Texture Repo"); let tex_repo = TextureRepo::new( device_lock.clone(), + tex_queue_negotiator.family_id(), + tex_queue_negotiator.get_queue(&mut queue_groups).unwrap(), &adapter, map.clone(), BasicFsResolver::new(std::path::Path::new(".")), ) .unwrap(); // TODO + debug!("Creating UI Texture Repo"); let ui_tex_repo = TextureRepo::new( device_lock.clone(), + tex_queue_negotiator.family_id(), + tex_queue_negotiator.get_queue(&mut queue_groups).unwrap(), &adapter, Arc::new(RwLock::new(UiTextures)), BasicFsResolver::new(std::path::Path::new(".")), @@ -224,18 +244,13 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { let ds_layout_lock = tex_repo.get_ds_layout(); let ui_ds_layout_lock = ui_tex_repo.get_ds_layout(); - let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); - descriptor_set_layouts.push(&*ds_layout_lock); - - let mut ui_descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); - ui_descriptor_set_layouts.push(&*ui_ds_layout_lock); // Graphics pipeline let pipeline = CompletePipeline::new( &mut device, swapchain_properties.extent, &swapchain_properties, - descriptor_set_layouts, + once(&*ds_layout_lock), )?; // UI pipeline @@ -243,19 +258,18 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { &mut device, swapchain_properties.extent, &swapchain_properties, - ui_descriptor_set_layouts, + once(&*ui_ds_layout_lock), )?; // Swapchain and associated resources let target_chain = TargetChain::new( &mut device, &adapter, - &mut surface, + surface, &pipeline, &ui_pipeline, &mut cmd_pool, swapchain_properties, - None, ) .map_err(error::CreationError::TargetChainCreationError)?; @@ -266,11 +280,11 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { Ok(RenderingContext { map, instance: ManuallyDrop::new(instance), - surface: ManuallyDrop::new(surface), device: device_lock, adapter, - queue_group, + + queue: draw_queue_negotiator.get_queue(&mut queue_groups).unwrap(), target_chain: ManuallyDrop::new(target_chain), cmd_pool: ManuallyDrop::new(cmd_pool), @@ -300,7 +314,10 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { device.wait_idle().unwrap(); - let properties = SwapchainProperties::find_best(&self.adapter, &self.surface) + let surface = ManuallyDrop::into_inner(read(&self.target_chain)) + .deactivate_with_recyling(&mut device, &mut self.cmd_pool); + + let properties = SwapchainProperties::find_best(&self.adapter, &surface) .map_err(|_| error::CreationError::BadSurface)?; use core::ptr::read; @@ -312,14 +329,11 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { ManuallyDrop::into_inner(read(&self.pipeline)).deactivate(&mut device); self.pipeline = ManuallyDrop::new({ - let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); - descriptor_set_layouts.push(&*ds_layout_handle); - CompletePipeline::new( &mut device, properties.extent, &properties, - descriptor_set_layouts, + once(&*ds_layout_handle), )? }); @@ -334,32 +348,30 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { &mut device, properties.extent, &properties, - descriptor_set_layouts, + once(&*ui_ds_layout_handle), )? }); - let old_swapchain = ManuallyDrop::into_inner(read(&self.target_chain)) - .deactivate_with_recyling(&mut device, &mut self.cmd_pool); self.target_chain = ManuallyDrop::new( TargetChain::new( &mut device, &self.adapter, - &mut self.surface, + surface, &self.pipeline, &self.ui_pipeline, &mut self.cmd_pool, properties, - Some(old_swapchain), ) .map_err(error::CreationError::TargetChainCreationError)?, ); - Ok(()) } /// Draw all vertices in the buffer pub fn draw_vertices(&mut self, ui: &mut UiState, faces: &[u32]) -> Result<(), &'static str> { let mut device = self.device.write().unwrap(); + let mut queue = self.queue.write().unwrap(); + // Ensure UI texture(s) are loaded ensure_textures_ui( &mut self.ui_tex_repo, @@ -367,7 +379,7 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { &mut device, &mut self.adapter, &mut self.texture_allocator, - &mut self.queue_group.queues[0], + &mut queue, &mut self.cmd_pool, ); @@ -375,7 +387,7 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { self.tex_repo.process_responses(); // 3D Pass - let cmd_buffer = self.target_chain.prep_next_target( + let (cmd_buffer, img) = self.target_chain.prep_next_target( &mut device, &mut self.draw_buffers, &self.pipeline, @@ -391,9 +403,9 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { ); // 2D Pass - let cmd_buffer = self - .target_chain - .target_2d_pass(&mut self.ui_draw_buffers, &self.ui_pipeline)?; + let cmd_buffer = + self.target_chain + .target_2d_pass(&mut self.ui_draw_buffers, &img, &self.ui_pipeline)?; do_render_ui( cmd_buffer, &self.ui_pipeline.pipeline_layout, @@ -403,33 +415,25 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { ); // Update our buffers before we actually start drawing - self.draw_buffers.vertex_buffer.commit( - &device, - &mut self.queue_group.queues[0], - &mut self.cmd_pool, - ); + self.draw_buffers + .vertex_buffer + .commit(&device, &mut queue, &mut self.cmd_pool); - self.draw_buffers.index_buffer.commit( - &device, - &mut self.queue_group.queues[0], - &mut self.cmd_pool, - ); + self.draw_buffers + .index_buffer + .commit(&device, &mut queue, &mut self.cmd_pool); - self.ui_draw_buffers.vertex_buffer.commit( - &device, - &mut self.queue_group.queues[0], - &mut self.cmd_pool, - ); + self.ui_draw_buffers + .vertex_buffer + .commit(&device, &mut queue, &mut self.cmd_pool); - self.ui_draw_buffers.index_buffer.commit( - &device, - &mut self.queue_group.queues[0], - &mut self.cmd_pool, - ); + self.ui_draw_buffers + .index_buffer + .commit(&device, &mut queue, &mut self.cmd_pool); // Send commands off to GPU self.target_chain - .finish_and_submit_target(&mut self.queue_group.queues[0])?; + .finish_and_submit_target(img, &mut queue)?; Ok(()) } @@ -454,16 +458,16 @@ impl<'a, M: MinBspFeatures<VulkanSystem>> core::ops::Drop for RenderingContext<' ManuallyDrop::into_inner(read(&self.texture_allocator)).dispose(); - ManuallyDrop::into_inner(read(&self.target_chain)) - .deactivate(&mut device, &mut self.cmd_pool); + ManuallyDrop::into_inner(read(&self.target_chain)).deactivate( + &mut self.instance, + &mut device, + &mut self.cmd_pool, + ); device.destroy_command_pool(ManuallyDrop::into_inner(read(&self.cmd_pool))); ManuallyDrop::into_inner(read(&self.pipeline)).deactivate(&mut device); ManuallyDrop::into_inner(read(&self.ui_pipeline)).deactivate(&mut device); - - self.instance - .destroy_surface(ManuallyDrop::into_inner(read(&self.surface))); } } } diff --git a/stockton-render/src/draw/depth_buffer.rs b/stockton-render/src/draw/depth_buffer.rs index 14b4d30..8af1514 100644 --- a/stockton-render/src/draw/depth_buffer.rs +++ b/stockton-render/src/draw/depth_buffer.rs @@ -1,15 +1,15 @@ use crate::draw::buffer::create_buffer; -use gfx_hal::{format::Aspects, memory::Properties, queue::Submission, MemoryTypeId}; +use gfx_hal::{format::Aspects, memory::Properties, MemoryTypeId}; use hal::{ buffer::Usage as BufUsage, format::{Format, Swizzle}, - image::{SubresourceRange, Usage, ViewKind}, + image::{SubresourceRange, Usage, Usage as ImgUsage, ViewKind}, memory, + memory::Segment, }; -use std::convert::TryInto; +use std::{array::IntoIter, convert::TryInto, iter::empty}; use crate::types::*; -use hal::prelude::*; use std::mem::ManuallyDrop; use super::texture::{LoadableImage, PIXEL_SIZE}; @@ -17,18 +17,18 @@ use super::texture::{LoadableImage, PIXEL_SIZE}; /// 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>, + image: ManuallyDrop<ImageT>, /// The full view of the image - pub image_view: ManuallyDrop<ImageView>, + pub image_view: ManuallyDrop<ImageViewT>, /// The memory backing the image - memory: ManuallyDrop<Memory>, + memory: ManuallyDrop<MemoryT>, } impl DedicatedLoadedImage { pub fn new( - device: &mut Device, + device: &mut DeviceT, adapter: &Adapter, format: Format, usage: Usage, @@ -39,7 +39,7 @@ impl DedicatedLoadedImage { 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 limits = adapter.physical_device.properties().limits; let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; let row_size = @@ -56,6 +56,7 @@ impl DedicatedLoadedImage { format, Tiling::Optimal, usage, + memory::SparseFlags::empty(), ViewCapabilities::empty(), ) } @@ -96,7 +97,14 @@ impl DedicatedLoadedImage { // Create ImageView and sampler let image_view = unsafe { - device.create_image_view(&image_ref, ViewKind::D2, format, Swizzle::NO, resources) + device.create_image_view( + &image_ref, + ViewKind::D2, + format, + Swizzle::NO, + ImgUsage::DEPTH_STENCIL_ATTACHMENT, + resources, + ) } .map_err(|_| "Couldn't create the image view!")?; @@ -111,13 +119,13 @@ impl DedicatedLoadedImage { pub fn load<T: LoadableImage>( &mut self, img: T, - device: &mut Device, + device: &mut DeviceT, adapter: &Adapter, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool, + command_queue: &mut QueueT, + command_pool: &mut CommandPoolT, ) -> Result<(), &'static str> { let initial_row_size = PIXEL_SIZE * img.width() as usize; - let limits = adapter.physical_device.limits(); + let limits = adapter.physical_device.properties().limits; let row_alignment_mask = limits.optimal_buffer_copy_pitch_alignment as u32 - 1; let row_size = @@ -126,7 +134,7 @@ impl DedicatedLoadedImage { debug_assert!(row_size as usize >= initial_row_size); // Make a staging buffer - let (staging_buffer, staging_memory) = create_buffer( + let (staging_buffer, mut staging_memory) = create_buffer( device, adapter, BufUsage::TRANSFER_SRC, @@ -139,7 +147,13 @@ impl DedicatedLoadedImage { unsafe { let mapped_memory: *mut u8 = std::mem::transmute( device - .map_memory(&staging_memory, 0..total_size) + .map_memory( + &mut staging_memory, + Segment { + offset: 0, + size: None, + }, + ) .map_err(|_| "Couldn't map buffer memory")?, ); @@ -148,7 +162,7 @@ impl DedicatedLoadedImage { img.copy_row(y as u32, mapped_memory.offset(dest_base)); } - device.unmap_memory(&staging_memory); + device.unmap_memory(&mut staging_memory); } // Copy from staging to image memory @@ -170,14 +184,16 @@ impl DedicatedLoadedImage { families: None, range: SubresourceRange { aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, + level_start: 0, + level_count: Some(1), + layer_start: 0, + layer_count: Some(1), }, }; buf.pipeline_barrier( PipelineStage::TOP_OF_PIPE..PipelineStage::TRANSFER, memory::Dependencies::empty(), - &[image_barrier], + IntoIter::new([image_barrier]), ); // Copy from buffer to image @@ -185,7 +201,7 @@ impl DedicatedLoadedImage { &staging_buffer, &(*self.image), Layout::TransferDstOptimal, - &[BufferImageCopy { + IntoIter::new([BufferImageCopy { buffer_offset: 0, buffer_width: (row_size / PIXEL_SIZE) as u32, buffer_height: img.height(), @@ -200,7 +216,7 @@ impl DedicatedLoadedImage { height: img.height(), depth: 1, }, - }], + }]), ); // Setup the layout of our image for shaders @@ -211,15 +227,17 @@ impl DedicatedLoadedImage { families: None, range: SubresourceRange { aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, + level_start: 0, + level_count: Some(1), + layer_start: 0, + layer_count: Some(1), }, }; buf.pipeline_barrier( PipelineStage::TRANSFER..PipelineStage::FRAGMENT_SHADER, memory::Dependencies::empty(), - &[image_barrier], + IntoIter::new([image_barrier]), ); buf.finish(); @@ -229,14 +247,12 @@ impl DedicatedLoadedImage { // 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), + let mut setup_finished = device.create_fence(false).unwrap(); + command_queue.submit( + IntoIter::new([&buf]), + empty(), + empty(), + Some(&mut setup_finished), ); device @@ -259,10 +275,10 @@ impl DedicatedLoadedImage { /// Load the given image into a new buffer pub fn load_into_new<T: LoadableImage>( img: T, - device: &mut Device, + device: &mut DeviceT, adapter: &Adapter, - command_queue: &mut CommandQueue, - command_pool: &mut CommandPool, + command_queue: &mut QueueT, + command_pool: &mut CommandPoolT, format: Format, usage: Usage, ) -> Result<DedicatedLoadedImage, &'static str> { @@ -273,8 +289,10 @@ impl DedicatedLoadedImage { usage | Usage::TRANSFER_DST, SubresourceRange { aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, + level_start: 0, + level_count: Some(1), + layer_start: 0, + layer_count: Some(1), }, img.width() as usize, img.height() as usize, @@ -286,7 +304,7 @@ impl DedicatedLoadedImage { /// Properly frees/destroys all the objects in this struct /// Dropping without doing this is a bad idea - pub fn deactivate(self, device: &mut Device) { + pub fn deactivate(self, device: &mut DeviceT) { unsafe { use core::ptr::read; diff --git a/stockton-render/src/draw/draw_buffers.rs b/stockton-render/src/draw/draw_buffers.rs index bba69df..67687dd 100644 --- a/stockton-render/src/draw/draw_buffers.rs +++ b/stockton-render/src/draw/draw_buffers.rs @@ -21,7 +21,7 @@ pub struct DrawBuffers<'a, T: Sized> { impl<'a, T> DrawBuffers<'a, T> { pub fn new( - device: &mut Device, + device: &mut DeviceT, adapter: &Adapter, ) -> Result<DrawBuffers<'a, T>, CreationError> { let vert = StagedBuffer::new(device, &adapter, Usage::VERTEX, INITIAL_VERT_SIZE)?; @@ -33,7 +33,7 @@ impl<'a, T> DrawBuffers<'a, T> { }) } - pub fn deactivate(self, device: &mut Device) { + pub fn deactivate(self, device: &mut DeviceT) { unsafe { use core::ptr::read; diff --git a/stockton-render/src/draw/macros.rs b/stockton-render/src/draw/macros.rs deleted file mode 100644 index 8558b71..0000000 --- a/stockton-render/src/draw/macros.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Helper macros, mostly for the graphics pipeline definitions - -/// Macro for easily defining buffer attribute descriptions -/// Usage: -/// ``` -/// // 0 is the binding value -/// let attributes: Vec<AttributeDesc> = pipeline_vb_attributes!(0, -/// size_of::<f32>() * 3; Rgb32Sfloat -/// size_of::<f32>() * 2; Rg32Sfloat, -/// size_of::<u32>(); R32Sint -/// ); -/// ``` -/// See the hal::pso::Format enum for possible types -#[allow(clippy::vec_init_then_push)] -macro_rules! pipeline_vb_attributes { - // Special case for single item - ( $binding:expr, $firstSize:expr; $firstType:ident ) => ({ - #![allow(clippy::vec_init_then_push)] - vec![ - AttributeDesc { - location: 0, - binding: $binding, - element: Element { - format: Format::$firstType, - offset: $firstSize as u32 - } - } - ] - }); - - // Start of recursion - ( $binding:expr, - $firstSize:expr; $firstType:ident, - $( $size:expr; $type:ident ),* - ) => ({ - use hal::pso::{AttributeDesc, Element}; - - let mut vec = Vec::new(); - - vec.push(AttributeDesc { - location: 0, - binding: $binding, - element: Element { - format: Format::$firstType, - offset: 0 - } - }); - - pipeline_vb_attributes!( - vec; $binding; 1; $firstSize, - $($size; $type),* - ); - - vec - }); - - // Middle of recursion - ( $vec:ident; $binding:expr; $location:expr; $prevSize:expr, - $firstSize:expr; $firstType:ident, - $($size:expr; $type:ident),* ) => ({ - - $vec.push(AttributeDesc { - location: $location, - binding: $binding, - element: Element { - format: Format::$firstType, - offset: $prevSize as u32 - } - }); - - pipeline_vb_attributes!( - $vec; $binding; ($location + 1); ($prevSize + $firstSize), - $($size; $type),* - ); - }); - - // End of recursion - ( $vec:ident; $binding:expr; $location:expr; $prevSize:expr, - $firstSize:expr; $firstType:ident ) => ({ - $vec.push(AttributeDesc { - location: $location, - binding: $binding, - element: Element { - format: Format::$firstType, - offset: $prevSize as u32 - } - }); - }); -} diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs index c6ee90b..e802ed5 100644 --- a/stockton-render/src/draw/mod.rs +++ b/stockton-render/src/draw/mod.rs @@ -2,14 +2,13 @@ pub mod target; -#[macro_use] -mod macros; mod buffer; mod camera; mod context; mod depth_buffer; mod draw_buffers; mod pipeline; +mod queue_negotiator; mod render; mod texture; mod ui; diff --git a/stockton-render/src/draw/pipeline.rs b/stockton-render/src/draw/pipeline.rs index 5ef636a..0a02947 100644 --- a/stockton-render/src/draw/pipeline.rs +++ b/stockton-render/src/draw/pipeline.rs @@ -10,12 +10,11 @@ const VERTEX_SOURCE: &str = include_str!("./data/stockton.vert"); const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag"); use std::{ - borrow::Borrow, + array::IntoIter, + iter::{empty, once}, mem::{size_of, ManuallyDrop}, }; -use hal::prelude::*; - use super::target::SwapchainProperties; use crate::error; use crate::types::*; @@ -24,32 +23,28 @@ use crate::types::*; /// A complete graphics pipeline and associated resources pub struct CompletePipeline { /// Our main render pass - pub(crate) renderpass: ManuallyDrop<RenderPass>, + pub(crate) renderpass: ManuallyDrop<RenderPassT>, /// The layout of our main graphics pipeline - pub(crate) pipeline_layout: ManuallyDrop<PipelineLayout>, + pub(crate) pipeline_layout: ManuallyDrop<PipelineLayoutT>, /// Our main graphics pipeline - pub(crate) pipeline: ManuallyDrop<GraphicsPipeline>, + pub(crate) pipeline: ManuallyDrop<GraphicsPipelineT>, /// The vertex shader module - pub(crate) vs_module: ManuallyDrop<ShaderModule>, + pub(crate) vs_module: ManuallyDrop<ShaderModuleT>, /// The fragment shader module - pub(crate) fs_module: ManuallyDrop<ShaderModule>, + pub(crate) fs_module: ManuallyDrop<ShaderModuleT>, } impl CompletePipeline { - pub fn new<T>( - device: &mut Device, + pub fn new<'a, T: Iterator<Item = &'a DescriptorSetLayoutT> + std::fmt::Debug>( + device: &mut DeviceT, extent: hal::image::Extent, swapchain_properties: &SwapchainProperties, set_layouts: T, - ) -> Result<Self, error::CreationError> - where - T: IntoIterator, - T::Item: Borrow<DescriptorSetLayout>, - { + ) -> Result<Self, error::CreationError> { use hal::format::Format; use hal::pso::*; @@ -88,7 +83,11 @@ impl CompletePipeline { }; unsafe { - device.create_render_pass(&[img_attachment, depth_attachment], &[subpass], &[]) + device.create_render_pass( + IntoIter::new([img_attachment, depth_attachment]), + once(subpass), + empty(), + ) } .map_err(|_| error::CreationError::OutOfMemoryError)? }; @@ -150,28 +149,6 @@ impl CompletePipeline { }, ); - // Shader set - let shaders = GraphicsShaderSet { - vertex: vs_entry, - fragment: Some(fs_entry), - hull: None, - domain: None, - geometry: None, - }; - - // Vertex buffers - let vertex_buffers: Vec<VertexBufferDesc> = vec![VertexBufferDesc { - binding: 0, - stride: (size_of::<f32>() * 6) as u32, - rate: VertexInputRate::Vertex, - }]; - - let attributes: Vec<AttributeDesc> = pipeline_vb_attributes!(0, - size_of::<f32>() * 3; Rgb32Sfloat, - size_of::<u32>(); R32Sint, - size_of::<f32>() * 2; Rg32Sfloat - ); - // Rasterizer let rasterizer = Rasterizer { polygon_mode: PolygonMode::Fill, @@ -180,6 +157,7 @@ impl CompletePipeline { depth_clamping: false, depth_bias: None, conservative: true, + line_width: State::Static(1.0), }; // Depth stencil @@ -195,9 +173,9 @@ impl CompletePipeline { // Pipeline layout let layout = unsafe { device.create_pipeline_layout( - set_layouts, + set_layouts.into_iter(), // vp matrix, 4x4 f32 - &[(ShaderStageFlags::VERTEX, 0..64)], + IntoIter::new([(ShaderStageFlags::VERTEX, 0..64)]), ) } .map_err(|_| error::CreationError::OutOfMemoryError)?; @@ -231,18 +209,54 @@ impl CompletePipeline { depth: (0.0..1.0), }), scissor: Some(extent.rect()), - blend_color: None, + blend_constants: None, depth_bounds: None, }; - // Input assembler - let input_assembler = InputAssemblerDesc::new(Primitive::TriangleList); + // Primitive assembler + let primitive_assembler = PrimitiveAssemblerDesc::Vertex { + buffers: &[VertexBufferDesc { + binding: 0, + stride: (size_of::<f32>() * 6) as u32, + rate: VertexInputRate::Vertex, + }], + attributes: &[ + AttributeDesc { + location: 0, + binding: 0, + element: Element { + format: Format::Rgb32Sfloat, + offset: 0, + }, + }, + AttributeDesc { + location: 1, + binding: 0, + element: Element { + format: Format::R32Sint, + offset: (size_of::<f32>() * 3) as u32, + }, + }, + AttributeDesc { + location: 2, + binding: 0, + element: Element { + format: Format::Rg32Sfloat, + offset: (size_of::<f32>() * 4) as u32, + }, + }, + ], + input_assembler: InputAssemblerDesc::new(Primitive::TriangleList), + vertex: vs_entry, + tessellation: None, + geometry: None, + }; // Pipeline description let pipeline_desc = GraphicsPipelineDesc { - shaders, + label: Some("3D"), rasterizer, - vertex_buffers, + fragment: Some(fs_entry), blender, depth_stencil, multisampling: None, @@ -251,8 +265,7 @@ impl CompletePipeline { subpass, flags: PipelineCreationFlags::empty(), parent: BasePipeline::None, - input_assembler, - attributes, + primitive_assembler, }; // Pipeline @@ -269,7 +282,7 @@ impl CompletePipeline { } /// Deactivate vulkan resources. Use before dropping - pub fn deactivate(self, device: &mut Device) { + pub fn deactivate(self, device: &mut DeviceT) { unsafe { use core::ptr::read; diff --git a/stockton-render/src/draw/queue_negotiator.rs b/stockton-render/src/draw/queue_negotiator.rs new file mode 100644 index 0000000..b128846 --- /dev/null +++ b/stockton-render/src/draw/queue_negotiator.rs @@ -0,0 +1,72 @@ +use crate::types::*; +use anyhow::Result; +use hal::queue::family::QueueFamilyId; +use std::sync::{Arc, RwLock}; +use thiserror::Error; + +pub struct QueueNegotiator { + family_id: QueueFamilyId, + already_allocated: Vec<Arc<RwLock<QueueT>>>, + next_share: usize, +} + +impl QueueNegotiator { + pub fn find<F: FnMut(&&QueueFamilyT) -> bool>(adapter: &Adapter, filter: F) -> Result<Self> { + let family = adapter + .queue_families + .iter() + .find(filter) + .ok_or(QueueNegotiatorError::NoSuitableFamilies)?; + + Ok(QueueNegotiator { + family_id: family.id(), + already_allocated: Vec::with_capacity(family.max_queues()), + next_share: 0, + }) + } + + pub fn family<'a>(&self, adapter: &'a Adapter) -> &'a QueueFamilyT { + adapter + .queue_families + .iter() + .find(|x| x.id() == self.family_id) + .unwrap() + } + + pub fn family_id(&self) -> QueueFamilyId { + self.family_id + } + + pub fn get_queue(&mut self, groups: &mut Vec<QueueGroup>) -> Option<Arc<RwLock<QueueT>>> { + match groups + .iter() + .position(|x| x.queues.len() > 0 && x.family == self.family_id) + { + Some(idx) => { + // At least one remaining queue + let queue = groups[idx].queues.pop().unwrap(); + let queue = Arc::new(RwLock::new(queue)); + + self.already_allocated.push(queue.clone()); + + Some(queue) + } + None => { + if self.already_allocated.len() == 0 { + return None; + } + + let queue = self.already_allocated[self.next_share].clone(); + self.next_share = (self.next_share + 1) % self.already_allocated.len(); + + Some(queue) + } + } + } +} + +#[derive(Error, Debug)] +pub enum QueueNegotiatorError { + #[error("No suitable queue families found")] + NoSuitableFamilies, +} diff --git a/stockton-render/src/draw/render.rs b/stockton-render/src/draw/render.rs index b2e9f97..2cbdef4 100644 --- a/stockton-render/src/draw/render.rs +++ b/stockton-render/src/draw/render.rs @@ -1,10 +1,11 @@ use crate::draw::draw_buffers::INITIAL_INDEX_SIZE; use crate::draw::draw_buffers::INITIAL_VERT_SIZE; use crate::draw::UvPoint; -use arrayvec::ArrayVec; use faces::FaceType; -use hal::prelude::*; -use std::convert::TryInto; +use std::{ + convert::TryInto, + iter::{empty, once}, +}; use stockton_levels::prelude::*; use stockton_types::Vector2; @@ -16,16 +17,14 @@ use super::texture::TextureRepo; fn draw_or_queue( current_chunk: usize, tex_repo: &mut TextureRepo, - cmd_buffer: &mut CommandBuffer, - pipeline_layout: &PipelineLayout, + cmd_buffer: &mut CommandBufferT, + pipeline_layout: &PipelineLayoutT, chunk_start: u32, curr_idx_idx: u32, ) { if let Some(ds) = tex_repo.attempt_get_descriptor_set(current_chunk) { - let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); - descriptor_sets.push(ds); unsafe { - cmd_buffer.bind_graphics_descriptor_sets(pipeline_layout, 0, descriptor_sets, &[]); + cmd_buffer.bind_graphics_descriptor_sets(pipeline_layout, 0, once(ds), empty()); cmd_buffer.draw_indexed(chunk_start * 3..(curr_idx_idx * 3) + 1, 0, 0..1); } } else { @@ -34,10 +33,10 @@ fn draw_or_queue( } pub fn do_render<M: MinBspFeatures<VulkanSystem>>( - cmd_buffer: &mut CommandBuffer, + cmd_buffer: &mut CommandBufferT, draw_buffers: &mut DrawBuffers<UvPoint>, tex_repo: &mut TextureRepo, - pipeline_layout: &PipelineLayout, + pipeline_layout: &PipelineLayoutT, file: &M, faces: &[u32], ) { diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs index 1ee208b..8d308d9 100644 --- a/stockton-render/src/draw/target.rs +++ b/stockton-render/src/draw/target.rs @@ -1,14 +1,19 @@ //! Resources needed for drawing on the screen, including sync objects -use core::{iter::once, mem::ManuallyDrop}; +use std::{ + array::IntoIter, + borrow::Borrow, + iter::{empty, once}, + mem::ManuallyDrop, +}; use arrayvec::ArrayVec; use hal::{ - format::{ChannelType, Format, Swizzle}, - image::{Extent, Usage as ImgUsage, ViewKind}, - prelude::*, + buffer::SubRange, + command::RenderAttachmentInfo, + format::{ChannelType, Format}, + image::{Extent, FramebufferAttachment, Usage as ImgUsage, ViewCapabilities}, pso::Viewport, - queue::Submission, window::{CompositeAlphaMode, Extent2D, PresentMode, SwapchainConfig}, }; use na::Mat4; @@ -22,13 +27,6 @@ use super::{ }; use crate::types::*; -/// Defines the colour range we use. -const COLOR_RANGE: hal::image::SubresourceRange = hal::image::SubresourceRange { - aspects: hal::format::Aspects::COLOR, - levels: 0..1, - layers: 0..1, -}; - #[derive(Debug, Clone)] pub struct SwapchainProperties { pub format: Format, @@ -49,7 +47,7 @@ pub enum NoSupportedValuesError { impl SwapchainProperties { pub fn find_best( adapter: &Adapter, - surface: &Surface, + surface: &SurfaceT, ) -> Result<SwapchainProperties, NoSupportedValuesError> { let caps = surface.capabilities(&adapter.physical_device); let formats = surface.supported_formats(&adapter.physical_device); @@ -124,8 +122,8 @@ impl SwapchainProperties { } pub struct TargetChain { - /// Swapchain we're targeting - pub swapchain: ManuallyDrop<Swapchain>, + /// Surface we're targeting + pub surface: ManuallyDrop<SurfaceT>, pub properties: SwapchainProperties, @@ -147,16 +145,14 @@ pub struct TargetChain { } impl TargetChain { - #[allow(clippy::too_many_arguments)] pub fn new( - device: &mut Device, + device: &mut DeviceT, adapter: &Adapter, - surface: &mut Surface, + mut surface: SurfaceT, pipeline: &CompletePipeline, ui_pipeline: &UiPipeline, - cmd_pool: &mut CommandPool, + cmd_pool: &mut CommandPoolT, properties: SwapchainProperties, - old_swapchain: Option<Swapchain>, ) -> Result<TargetChain, TargetChainCreationError> { let caps = surface.capabilities(&adapter.physical_device); @@ -181,13 +177,6 @@ impl TargetChain { image_usage: ImgUsage::COLOR_ATTACHMENT, }; - // Swapchain - let (swapchain, mut backbuffer) = unsafe { - device - .create_swapchain(surface, swap_config, old_swapchain) - .map_err(|_| TargetChainCreationError::Todo)? - }; - let depth_buffer = { use hal::format::Aspects; use hal::image::SubresourceRange; @@ -199,8 +188,10 @@ impl TargetChain { ImgUsage::DEPTH_STENCIL_ATTACHMENT, SubresourceRange { aspects: Aspects::DEPTH, - levels: 0..1, - layers: 0..1, + level_start: 0, + level_count: Some(1), + layer_start: 0, + layer_count: Some(1), }, properties.extent.width as usize, properties.extent.height as usize, @@ -208,17 +199,24 @@ impl TargetChain { .map_err(|_| TargetChainCreationError::Todo) }?; - let mut targets: Vec<TargetResources> = Vec::with_capacity(backbuffer.len()); - let mut sync_objects: Vec<SyncObjects> = Vec::with_capacity(backbuffer.len()); - for image in backbuffer.drain(..) { + let fat = swap_config.framebuffer_attachment(); + let mut targets: Vec<TargetResources> = + Vec::with_capacity(swap_config.image_count as usize); + let mut sync_objects: Vec<SyncObjects> = + Vec::with_capacity(swap_config.image_count as usize); + for _ in 0..swap_config.image_count { targets.push( TargetResources::new( device, cmd_pool, &pipeline.renderpass, &ui_pipeline.renderpass, - image, - &(*depth_buffer.image_view), + fat.clone(), + FramebufferAttachment { + usage: ImgUsage::DEPTH_STENCIL_ATTACHMENT, + view_caps: ViewCapabilities::empty(), + format: properties.depth_format, + }, &properties, ) .map_err(|_| TargetChainCreationError::Todo)?, @@ -228,8 +226,15 @@ impl TargetChain { .push(SyncObjects::new(device).map_err(|_| TargetChainCreationError::Todo)?); } + // Configure Swapchain + unsafe { + surface + .configure_swapchain(device, swap_config) + .map_err(|_| TargetChainCreationError::Todo)?; + } + Ok(TargetChain { - swapchain: ManuallyDrop::new(swapchain), + surface: ManuallyDrop::new(surface), targets: targets.into_boxed_slice(), sync_objects: sync_objects.into_boxed_slice(), depth_buffer: ManuallyDrop::new(depth_buffer), @@ -239,19 +244,24 @@ impl TargetChain { }) } - pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) { - let swapchain = self.deactivate_with_recyling(device, cmd_pool); + pub fn deactivate( + self, + instance: &mut InstanceT, + device: &mut DeviceT, + cmd_pool: &mut CommandPoolT, + ) { + let surface = self.deactivate_with_recyling(device, cmd_pool); unsafe { - device.destroy_swapchain(swapchain); + instance.destroy_surface(surface); } } pub fn deactivate_with_recyling( - self, - device: &mut Device, - cmd_pool: &mut CommandPool, - ) -> Swapchain { + mut self, + device: &mut DeviceT, + cmd_pool: &mut CommandPoolT, + ) -> SurfaceT { use core::ptr::read; unsafe { ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device); @@ -263,84 +273,102 @@ impl TargetChain { for i in 0..self.sync_objects.len() { read(&self.sync_objects[i]).deactivate(device); } + + self.surface.unconfigure_swapchain(device); } - unsafe { ManuallyDrop::into_inner(read(&self.swapchain)) } + unsafe { ManuallyDrop::into_inner(read(&self.surface)) } } pub fn prep_next_target<'a>( &'a mut self, - device: &mut Device, + device: &mut DeviceT, draw_buffers: &mut DrawBuffers<UvPoint>, pipeline: &CompletePipeline, vp: &Mat4, - ) -> Result<&'a mut crate::types::CommandBuffer, &'static str> { + ) -> Result< + ( + &'a mut crate::types::CommandBufferT, + <SurfaceT as PresentationSurface<back::Backend>>::SwapchainImage, + ), + &'static str, + > { self.last_syncs = (self.last_syncs + 1) % self.sync_objects.len(); let syncs = &mut self.sync_objects[self.last_syncs]; + self.last_image = (self.last_image + 1) % self.targets.len() as u32; + + let target = &mut self.targets[self.last_image as usize]; + // Get the image - let (image_index, _) = unsafe { - self.swapchain - .acquire_image(core::u64::MAX, Some(&syncs.get_image), None) + let (img, _) = unsafe { + self.surface + .acquire_image(core::u64::MAX) .map_err(|_| "FrameError::AcquireError")? }; - self.last_image = image_index; - - let target = &mut self.targets[image_index as usize]; - // Make sure whatever was last using this has finished unsafe { device .wait_for_fence(&syncs.present_complete, core::u64::MAX) .map_err(|_| "FrameError::SyncObjectError")?; device - .reset_fence(&syncs.present_complete) + .reset_fence(&mut syncs.present_complete) .map_err(|_| "FrameError::SyncObjectError")?; }; // Record commands unsafe { - use hal::buffer::IndexBufferView; use hal::command::{ ClearColor, ClearDepthStencil, ClearValue, CommandBufferFlags, SubpassContents, }; use hal::pso::ShaderStageFlags; - // Colour to clear window to - let clear_values = [ - ClearValue { - color: ClearColor { - float32: [0.0, 0.0, 0.0, 1.0], - }, - }, - ClearValue { - depth_stencil: ClearDepthStencil { - depth: 1.0, - stencil: 0, - }, - }, - ]; - // Get references to our buffers let (vbufs, ibuf) = { let vbufref: &<back::Backend as hal::Backend>::Buffer = draw_buffers.vertex_buffer.get_buffer(); - let vbufs: ArrayVec<[_; 1]> = [(vbufref, 0)].into(); + let vbufs: ArrayVec<[_; 1]> = [( + vbufref, + SubRange { + offset: 0, + size: None, + }, + )] + .into(); let ibuf = draw_buffers.index_buffer.get_buffer(); (vbufs, ibuf) }; - target.cmd_buffer.begin_primary(CommandBufferFlags::EMPTY); + target.cmd_buffer.begin_primary(CommandBufferFlags::empty()); // Main render pass / pipeline target.cmd_buffer.begin_render_pass( &pipeline.renderpass, &target.framebuffer, self.properties.viewport.rect, - clear_values.iter(), + vec![ + RenderAttachmentInfo { + image_view: img.borrow(), + clear_value: ClearValue { + color: ClearColor { + float32: [0.0, 0.0, 0.0, 1.0], + }, + }, + }, + RenderAttachmentInfo { + image_view: &*self.depth_buffer.image_view, + clear_value: ClearValue { + depth_stencil: ClearDepthStencil { + depth: 1.0, + stencil: 0, + }, + }, + }, + ] + .into_iter(), SubpassContents::Inline, ); target.cmd_buffer.bind_graphics_pipeline(&pipeline.pipeline); @@ -356,22 +384,26 @@ impl TargetChain { ); // Bind buffers - target.cmd_buffer.bind_vertex_buffers(0, vbufs); - target.cmd_buffer.bind_index_buffer(IndexBufferView { - buffer: ibuf, - offset: 0, - index_type: hal::IndexType::U16, - }); + target.cmd_buffer.bind_vertex_buffers(0, vbufs.into_iter()); + target.cmd_buffer.bind_index_buffer( + &ibuf, + SubRange { + offset: 0, + size: None, + }, + hal::IndexType::U16, + ); }; - Ok(&mut target.cmd_buffer) + Ok((&mut target.cmd_buffer, img)) } pub fn target_2d_pass<'a>( &'a mut self, draw_buffers: &mut DrawBuffers<UiPoint>, + img: &<SurfaceT as PresentationSurface<back::Backend>>::SwapchainImage, pipeline: &UiPipeline, - ) -> Result<&'a mut CommandBuffer, &'static str> { + ) -> Result<&'a mut CommandBufferT, &'static str> { let target = &mut self.targets[self.last_image as usize]; unsafe { @@ -381,28 +413,27 @@ impl TargetChain { target.cmd_buffer.pipeline_barrier( PipelineStage::BOTTOM_OF_PIPE..PipelineStage::TOP_OF_PIPE, hal::memory::Dependencies::empty(), - &[], + std::iter::empty(), ); } // Record commands unsafe { - use hal::buffer::IndexBufferView; use hal::command::{ClearColor, ClearValue, SubpassContents}; - // Colour to clear window to - let clear_values = [ClearValue { - color: ClearColor { - float32: [1.0, 0.0, 0.0, 1.5], - }, - }]; - // Get references to our buffers let (vbufs, ibuf) = { let vbufref: &<back::Backend as hal::Backend>::Buffer = draw_buffers.vertex_buffer.get_buffer(); - let vbufs: ArrayVec<[_; 1]> = [(vbufref, 0)].into(); + let vbufs: ArrayVec<[_; 1]> = [( + vbufref, + SubRange { + offset: 0, + size: None, + }, + )] + .into(); let ibuf = draw_buffers.index_buffer.get_buffer(); (vbufs, ibuf) @@ -413,18 +444,29 @@ impl TargetChain { &pipeline.renderpass, &target.framebuffer_2d, self.properties.viewport.rect, - clear_values.iter(), + vec![RenderAttachmentInfo { + image_view: img.borrow(), + clear_value: ClearValue { + color: ClearColor { + float32: [0.0, 0.0, 0.0, 1.0], + }, + }, + }] + .into_iter(), SubpassContents::Inline, ); target.cmd_buffer.bind_graphics_pipeline(&pipeline.pipeline); // Bind buffers - target.cmd_buffer.bind_vertex_buffers(0, vbufs); - target.cmd_buffer.bind_index_buffer(IndexBufferView { - buffer: ibuf, - offset: 0, - index_type: hal::IndexType::U16, - }); + target.cmd_buffer.bind_vertex_buffers(0, vbufs.into_iter()); + target.cmd_buffer.bind_index_buffer( + &ibuf, + SubRange { + offset: 0, + size: None, + }, + hal::IndexType::U16, + ); }; Ok(&mut target.cmd_buffer) @@ -432,7 +474,8 @@ impl TargetChain { pub fn finish_and_submit_target( &mut self, - command_queue: &mut CommandQueue, + img: <SurfaceT as PresentationSurface<back::Backend>>::SwapchainImage, + command_queue: &mut QueueT, ) -> Result<(), &'static str> { let syncs = &mut self.sync_objects[self.last_syncs]; let target = &mut self.targets[self.last_image as usize]; @@ -442,31 +485,16 @@ impl TargetChain { target.cmd_buffer.finish(); } - // Make submission object - let command_buffers: std::iter::Once<&CommandBuffer> = once(&target.cmd_buffer); - let wait_semaphores: std::iter::Once<(&Semaphore, hal::pso::PipelineStage)> = once(( - &syncs.get_image, - hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT, - )); - let signal_semaphores: std::iter::Once<&Semaphore> = once(&syncs.render_complete); - - let present_wait_semaphores: std::iter::Once<&Semaphore> = once(&syncs.render_complete); - - let submission = Submission { - command_buffers, - wait_semaphores, - signal_semaphores, - }; - // Submit it unsafe { - command_queue.submit(submission, Some(&syncs.present_complete)); - self.swapchain - .present( - command_queue, - self.last_image as u32, - present_wait_semaphores, - ) + command_queue.submit( + once(&*target.cmd_buffer), + empty(), + once(&*syncs.render_complete), + Some(&mut syncs.present_complete), + ); + command_queue + .present(&mut self.surface, img, Some(&mut *syncs.render_complete)) .map_err(|_| "FrameError::PresentError")?; }; @@ -477,53 +505,34 @@ impl TargetChain { /// Resources for a single target frame, including sync objects pub struct TargetResources { /// Command buffer to use when drawing - pub cmd_buffer: ManuallyDrop<CommandBuffer>, - - /// The image for this frame - pub image: ManuallyDrop<Image>, - - /// Imageviews for this frame - pub imageview: ManuallyDrop<ImageView>, + pub cmd_buffer: ManuallyDrop<CommandBufferT>, /// Framebuffer for this frame - pub framebuffer: ManuallyDrop<Framebuffer>, + pub framebuffer: ManuallyDrop<FramebufferT>, /// Framebuffer for this frame when drawing in 2D - pub framebuffer_2d: ManuallyDrop<Framebuffer>, + pub framebuffer_2d: ManuallyDrop<FramebufferT>, } impl TargetResources { pub fn new( - device: &mut Device, - cmd_pool: &mut CommandPool, - renderpass: &RenderPass, - renderpass_2d: &RenderPass, - image: Image, - depth_pass: &ImageView, + device: &mut DeviceT, + cmd_pool: &mut CommandPoolT, + renderpass: &RenderPassT, + renderpass_2d: &RenderPassT, + fat: FramebufferAttachment, + dat: FramebufferAttachment, properties: &SwapchainProperties, ) -> Result<TargetResources, TargetResourcesCreationError> { // Command Buffer let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) }; - // ImageView - let imageview = unsafe { - device - .create_image_view( - &image, - ViewKind::D2, - properties.format, - Swizzle::NO, - COLOR_RANGE.clone(), - ) - .map_err(TargetResourcesCreationError::ImageViewError)? - }; - // Framebuffer let framebuffer = unsafe { device .create_framebuffer( &renderpass, - once(&imageview).chain(once(depth_pass)), + IntoIter::new([fat.clone(), dat]), properties.extent, ) .map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? @@ -532,48 +541,39 @@ impl TargetResources { // 2D framebuffer just needs the imageview, not the depth pass let framebuffer_2d = unsafe { device - .create_framebuffer(&renderpass_2d, once(&imageview), properties.extent) + .create_framebuffer(&renderpass_2d, once(fat), properties.extent) .map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? }; Ok(TargetResources { cmd_buffer: ManuallyDrop::new(cmd_buffer), - image: ManuallyDrop::new(image), - imageview: ManuallyDrop::new(imageview), framebuffer: ManuallyDrop::new(framebuffer), framebuffer_2d: ManuallyDrop::new(framebuffer_2d), }) } - pub fn deactivate(self, device: &mut Device, cmd_pool: &mut CommandPool) { + pub fn deactivate(self, device: &mut DeviceT, cmd_pool: &mut CommandPoolT) { use core::ptr::read; unsafe { cmd_pool.free(once(ManuallyDrop::into_inner(read(&self.cmd_buffer)))); device.destroy_framebuffer(ManuallyDrop::into_inner(read(&self.framebuffer))); device.destroy_framebuffer(ManuallyDrop::into_inner(read(&self.framebuffer_2d))); - device.destroy_image_view(ManuallyDrop::into_inner(read(&self.imageview))); } } } pub struct SyncObjects { - /// Triggered when the image is ready to draw to - pub get_image: ManuallyDrop<Semaphore>, - /// Triggered when rendering is done - pub render_complete: ManuallyDrop<Semaphore>, + pub render_complete: ManuallyDrop<SemaphoreT>, /// Triggered when the image is on screen - pub present_complete: ManuallyDrop<Fence>, + pub present_complete: ManuallyDrop<FenceT>, } impl SyncObjects { - pub fn new(device: &mut Device) -> Result<Self, TargetResourcesCreationError> { + pub fn new(device: &mut DeviceT) -> Result<Self, TargetResourcesCreationError> { // Sync objects - let get_image = device - .create_semaphore() - .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; let render_complete = device .create_semaphore() .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; @@ -582,17 +582,15 @@ impl SyncObjects { .map_err(|_| TargetResourcesCreationError::SyncObjectsNoMemory)?; Ok(SyncObjects { - get_image: ManuallyDrop::new(get_image), render_complete: ManuallyDrop::new(render_complete), present_complete: ManuallyDrop::new(present_complete), }) } - pub fn deactivate(self, device: &mut Device) { + pub fn deactivate(self, device: &mut DeviceT) { use core::ptr::read; unsafe { - device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.get_image))); device.destroy_semaphore(ManuallyDrop::into_inner(read(&self.render_complete))); device.destroy_fence(ManuallyDrop::into_inner(read(&self.present_complete))); } @@ -606,7 +604,7 @@ pub enum TargetChainCreationError { #[derive(Debug)] pub enum TargetResourcesCreationError { - ImageViewError(hal::image::ViewError), + ImageViewError, FrameBufferNoMemory, SyncObjectsNoMemory, } 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 diff --git a/stockton-render/src/draw/ui/pipeline.rs b/stockton-render/src/draw/ui/pipeline.rs index c10d83f..757c978 100644 --- a/stockton-render/src/draw/ui/pipeline.rs +++ b/stockton-render/src/draw/ui/pipeline.rs @@ -10,12 +10,11 @@ const VERTEX_SOURCE: &str = include_str!("./data/stockton.vert"); const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag"); use std::{ - borrow::Borrow, + array::IntoIter, + iter::once, mem::{size_of, ManuallyDrop}, }; -use hal::prelude::*; - use crate::draw::target::SwapchainProperties; use crate::error; use crate::types::*; @@ -23,32 +22,28 @@ use crate::types::*; /// A complete 2D graphics pipeline and associated resources pub struct UiPipeline { /// Our main render pass - pub(crate) renderpass: ManuallyDrop<RenderPass>, + pub(crate) renderpass: ManuallyDrop<RenderPassT>, /// The layout of our main graphics pipeline - pub(crate) pipeline_layout: ManuallyDrop<PipelineLayout>, + pub(crate) pipeline_layout: ManuallyDrop<PipelineLayoutT>, /// Our main graphics pipeline - pub(crate) pipeline: ManuallyDrop<GraphicsPipeline>, + pub(crate) pipeline: ManuallyDrop<GraphicsPipelineT>, /// The vertex shader module - pub(crate) vs_module: ManuallyDrop<ShaderModule>, + pub(crate) vs_module: ManuallyDrop<ShaderModuleT>, /// The fragment shader module - pub(crate) fs_module: ManuallyDrop<ShaderModule>, + pub(crate) fs_module: ManuallyDrop<ShaderModuleT>, } impl UiPipeline { - pub fn new<T>( - device: &mut Device, + pub fn new<'a, T: Iterator<Item = &'a DescriptorSetLayoutT>>( + device: &mut DeviceT, extent: hal::image::Extent, swapchain_properties: &SwapchainProperties, set_layouts: T, - ) -> Result<Self, error::CreationError> - where - T: IntoIterator + std::fmt::Debug, - T::Item: Borrow<DescriptorSetLayout>, - { + ) -> Result<Self, error::CreationError> { use hal::format::Format; use hal::pso::*; @@ -81,7 +76,7 @@ impl UiPipeline { let external_dependency = SubpassDependency { flags: Dependencies::empty(), - passes: SubpassRef::External..SubpassRef::Pass(0), + passes: None..Some(0), stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT ..(PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS), @@ -90,7 +85,11 @@ impl UiPipeline { }; unsafe { - device.create_render_pass(&[img_attachment], &[subpass], &[external_dependency]) + device.create_render_pass( + IntoIter::new([img_attachment]), + IntoIter::new([subpass]), + IntoIter::new([external_dependency]), + ) } .map_err(|_| error::CreationError::OutOfMemoryError)? }; @@ -152,28 +151,6 @@ impl UiPipeline { }, ); - // Shader set - let shaders = GraphicsShaderSet { - vertex: vs_entry, - fragment: Some(fs_entry), - hull: None, - domain: None, - geometry: None, - }; - - // Vertex buffers - let vertex_buffers: Vec<VertexBufferDesc> = vec![VertexBufferDesc { - binding: 0, - stride: ((size_of::<f32>() * 4) + (size_of::<u8>() * 4)) as u32, - rate: VertexInputRate::Vertex, - }]; - - let attributes: Vec<AttributeDesc> = pipeline_vb_attributes!(0, - size_of::<f32>() * 2; Rg32Sfloat, - size_of::<f32>() * 2; Rg32Sfloat, - size_of::<u8>() * 4; R32Uint - ); - // Rasterizer let rasterizer = Rasterizer { polygon_mode: PolygonMode::Fill, @@ -182,6 +159,7 @@ impl UiPipeline { depth_clamping: false, depth_bias: None, conservative: true, + line_width: State::Static(1.0), }; // Depth stencil @@ -191,10 +169,9 @@ impl UiPipeline { stencil: None, }; - log::debug!("ui set layouts: {:?}", set_layouts); // Pipeline layout let layout = unsafe { - device.create_pipeline_layout(set_layouts, &[(ShaderStageFlags::VERTEX, 0..8)]) + device.create_pipeline_layout(set_layouts, once((ShaderStageFlags::VERTEX, 0..8))) } .map_err(|_| error::CreationError::OutOfMemoryError)?; @@ -227,18 +204,55 @@ impl UiPipeline { depth: (0.0..1.0), }), scissor: Some(extent.rect()), - blend_color: None, + blend_constants: None, depth_bounds: None, }; - // Input assembler - let input_assembler = InputAssemblerDesc::new(Primitive::TriangleList); + // Primitive assembler + let primitive_assembler = PrimitiveAssemblerDesc::Vertex { + buffers: &[VertexBufferDesc { + binding: 0, + stride: (size_of::<f32>() * 6) as u32, + rate: VertexInputRate::Vertex, + }], + attributes: &[ + AttributeDesc { + location: 0, + binding: 0, + element: Element { + format: Format::Rg32Sfloat, + offset: 0, + }, + }, + AttributeDesc { + location: 1, + binding: 0, + element: Element { + format: Format::Rg32Sfloat, + offset: (size_of::<f32>() * 2) as u32, + }, + }, + AttributeDesc { + location: 2, + binding: 0, + element: Element { + format: Format::R32Uint, + offset: (size_of::<f32>() * 4) as u32, + }, + }, + ], + input_assembler: InputAssemblerDesc::new(Primitive::TriangleList), + vertex: vs_entry, + tessellation: None, + geometry: None, + }; // Pipeline description let pipeline_desc = GraphicsPipelineDesc { - shaders, + label: Some("UI Pipeline"), + primitive_assembler, rasterizer, - vertex_buffers, + fragment: Some(fs_entry), blender, depth_stencil, multisampling: None, @@ -247,8 +261,6 @@ impl UiPipeline { subpass, flags: PipelineCreationFlags::empty(), parent: BasePipeline::None, - input_assembler, - attributes, }; // Pipeline @@ -265,7 +277,7 @@ impl UiPipeline { } /// Deactivate vulkan resources. Use before dropping - pub fn deactivate(self, device: &mut Device) { + pub fn deactivate(self, device: &mut DeviceT) { unsafe { use core::ptr::read; diff --git a/stockton-render/src/draw/ui/render.rs b/stockton-render/src/draw/ui/render.rs index 757b3a2..62a13bd 100644 --- a/stockton-render/src/draw/ui/render.rs +++ b/stockton-render/src/draw/ui/render.rs @@ -1,18 +1,16 @@ use crate::draw::texture::TextureRepo; -use arrayvec::ArrayVec; -use hal::prelude::*; use hal::pso::ShaderStageFlags; use super::UiPoint; use crate::draw::draw_buffers::DrawBuffers; use crate::types::*; use crate::UiState; -use std::convert::TryInto; +use std::{array::IntoIter, convert::TryInto, iter::empty}; use stockton_types::Vector2; pub fn do_render( - cmd_buffer: &mut CommandBuffer, - pipeline_layout: &PipelineLayout, + cmd_buffer: &mut CommandBufferT, + pipeline_layout: &PipelineLayoutT, draw_buffers: &mut DrawBuffers<UiPoint>, tex_repo: &mut TextureRepo, ui: &mut UiState, @@ -49,11 +47,13 @@ pub fn do_render( // TODO: *Properly* deal with textures if let Some(ds) = tex_repo.attempt_get_descriptor_set(0) { - let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); - descriptor_sets.push(ds); - unsafe { - cmd_buffer.bind_graphics_descriptor_sets(pipeline_layout, 0, descriptor_sets, &[]); + cmd_buffer.bind_graphics_descriptor_sets( + pipeline_layout, + 0, + IntoIter::new([ds]), + empty(), + ); // Call draw cmd_buffer.draw_indexed(0..tris.indices.len() as u32, 0, 0..1); } diff --git a/stockton-render/src/draw/ui/texture.rs b/stockton-render/src/draw/ui/texture.rs index 7cf207f..0ec4873 100755 --- a/stockton-render/src/draw/ui/texture.rs +++ b/stockton-render/src/draw/ui/texture.rs @@ -46,11 +46,11 @@ impl LoadableImage for &Texture { pub fn ensure_textures( _tex_repo: &mut TextureRepo, ui: &mut UiState, - _device: &mut Device, + _device: &mut DeviceT, _adapter: &mut Adapter, _allocator: &mut DynamicAllocator, - _command_queue: &mut CommandQueue, - _command_pool: &mut CommandPool, + _command_queue: &mut QueueT, + _command_pool: &mut CommandPoolT, ) { let tex = ui.ctx.texture(); diff --git a/stockton-render/src/draw/utils.rs b/stockton-render/src/draw/utils.rs index df62bb8..2ab984b 100644 --- a/stockton-render/src/draw/utils.rs +++ b/stockton-render/src/draw/utils.rs @@ -3,7 +3,7 @@ use hal::{memory::Properties as MemProperties, prelude::*, MemoryTypeId}; pub fn find_memory_type_id( adapter: &Adapter, - type_mask: u64, + type_mask: u32, props: MemProperties, ) -> Option<MemoryTypeId> { adapter diff --git a/stockton-render/src/error.rs b/stockton-render/src/error.rs index 3441bb2..7c9abd4 100644 --- a/stockton-render/src/error.rs +++ b/stockton-render/src/error.rs @@ -1,6 +1,7 @@ //! Error types use super::draw::target::TargetChainCreationError; +use thiserror::Error; /// An error encountered creating a rendering context. #[derive(Debug)] @@ -23,8 +24,8 @@ pub enum CreationError { BufferError(hal::buffer::CreationError), BufferNoMemory, - SwapchainError(hal::window::CreationError), - ImageViewError(hal::image::ViewError), + SwapchainError, + ImageViewError, BadDataError, } @@ -34,3 +35,15 @@ pub enum CreationError { /// You'll likely need to exit or create a new context. #[derive(Debug, Clone)] pub enum FrameError {} + +#[derive(Error, Debug)] +pub enum LockPoisoned { + #[error("Device lock poisoned")] + Device, + + #[error("Map lock poisoned")] + Map, + + #[error("Queue lock poisoned")] + Queue, +} diff --git a/stockton-render/src/types.rs b/stockton-render/src/types.rs index 4a79602..797ced9 100644 --- a/stockton-render/src/types.rs +++ b/stockton-render/src/types.rs @@ -1,28 +1,28 @@ //! Convenience module to reference types that are stored in the backend's enum -use thiserror::Error; - -pub type Device = <back::Backend as hal::Backend>::Device; -pub type Gpu = hal::adapter::Gpu<back::Backend>; -pub type Buffer = <back::Backend as hal::Backend>::Buffer; -pub type Memory = <back::Backend as hal::Backend>::Memory; -pub type Swapchain = <back::Backend as hal::Backend>::Swapchain; -pub type Surface = <back::Backend as hal::Backend>::Surface; -pub type Semaphore = <back::Backend as hal::Backend>::Semaphore; -pub type Fence = <back::Backend as hal::Backend>::Fence; -pub type CommandPool = <back::Backend as hal::Backend>::CommandPool; -pub type CommandBuffer = <back::Backend as hal::Backend>::CommandBuffer; -pub type CommandQueue = <back::Backend as hal::Backend>::CommandQueue; -pub type DescriptorSetLayout = <back::Backend as hal::Backend>::DescriptorSetLayout; -pub type DescriptorSet = <back::Backend as hal::Backend>::DescriptorSet; -pub type PipelineLayout = <back::Backend as hal::Backend>::PipelineLayout; -pub type GraphicsPipeline = <back::Backend as hal::Backend>::GraphicsPipeline; -pub type ShaderModule = <back::Backend as hal::Backend>::ShaderModule; -pub type Sampler = <back::Backend as hal::Backend>::Sampler; -pub type Image = <back::Backend as hal::Backend>::Image; -pub type ImageView = <back::Backend as hal::Backend>::ImageView; -pub type Framebuffer = <back::Backend as hal::Backend>::Framebuffer; -pub type RenderPass = <back::Backend as hal::Backend>::RenderPass; +pub use hal::prelude::*; + +pub type InstanceT = <back::Backend as hal::Backend>::Instance; +pub type DeviceT = <back::Backend as hal::Backend>::Device; +pub type BufferT = <back::Backend as hal::Backend>::Buffer; +pub type MemoryT = <back::Backend as hal::Backend>::Memory; +pub type SurfaceT = <back::Backend as hal::Backend>::Surface; +pub type SemaphoreT = <back::Backend as hal::Backend>::Semaphore; +pub type FenceT = <back::Backend as hal::Backend>::Fence; +pub type CommandPoolT = <back::Backend as hal::Backend>::CommandPool; +pub type CommandBufferT = <back::Backend as hal::Backend>::CommandBuffer; +pub type QueueT = <back::Backend as hal::Backend>::Queue; +pub type QueueFamilyT = <back::Backend as hal::Backend>::QueueFamily; +pub type DescriptorSetLayoutT = <back::Backend as hal::Backend>::DescriptorSetLayout; +pub type DescriptorSetT = <back::Backend as hal::Backend>::DescriptorSet; +pub type PipelineLayoutT = <back::Backend as hal::Backend>::PipelineLayout; +pub type GraphicsPipelineT = <back::Backend as hal::Backend>::GraphicsPipeline; +pub type ShaderModuleT = <back::Backend as hal::Backend>::ShaderModule; +pub type SamplerT = <back::Backend as hal::Backend>::Sampler; +pub type ImageT = <back::Backend as hal::Backend>::Image; +pub type ImageViewT = <back::Backend as hal::Backend>::ImageView; +pub type FramebufferT = <back::Backend as hal::Backend>::Framebuffer; +pub type RenderPassT = <back::Backend as hal::Backend>::RenderPass; pub type Adapter = hal::adapter::Adapter<back::Backend>; pub type QueueGroup = hal::queue::QueueGroup<back::Backend>; @@ -32,45 +32,3 @@ pub type DynamicAllocator = rendy_memory::DynamicAllocator<back::Backend>; pub type DynamicBlock = rendy_memory::DynamicBlock<back::Backend>; pub type RDescriptorSet = rendy_descriptor::DescriptorSet<back::Backend>; - -#[derive(Error, Debug)] -pub enum LockPoisoned { - #[error("Device lock poisoned")] - Device, - - #[error("Map lock poisoned")] - Map, - - #[error("Other lock poisoned")] - Other, -} - -#[derive(Error, Debug)] -pub enum HalErrorWrapper { - #[error("Device Creation Error: {0}")] - DeviceCreationError(#[from] hal::device::CreationError), - - #[error("Buffer Creation Error: {0}")] - BufferCreationError(#[from] hal::buffer::CreationError), - - #[error("Image Creation Error: {0}")] - ImageCreationError(#[from] hal::image::CreationError), - - #[error("View Error: {0}")] - ImageViewError(#[from] hal::image::ViewError), - - #[error("Out of memory on {0}")] - OutOfMemory(#[from] hal::device::OutOfMemory), - - #[error("Device Lost: {0}")] - DeviceLost(#[from] hal::device::DeviceLost), - - #[error("Allocation Error: {0}")] - AllocationError(#[from] hal::device::AllocationError), - - #[error("Bind Error: {0}")] - BindError(#[from] hal::device::BindError), - - #[error("Map Error: {0}")] - MapError(#[from] hal::device::MapError), -} |