diff options
author | tcmal <me@aria.rip> | 2024-08-25 17:44:23 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-08-25 17:44:23 +0100 |
commit | a82e16c92a026b2fbe3a40a21d7e690242e32ba6 (patch) | |
tree | 2b56390408a5ad858d638b5e2a26dd670dbe6bc7 | |
parent | 439219e74090c7158f8dbc33fed4107a5eb7c003 (diff) |
WIP refactor(render): add draw passes
-rw-r--r-- | stockton-render/src/culling.rs | 2 | ||||
-rw-r--r-- | stockton-render/src/draw/camera.rs | 6 | ||||
-rw-r--r-- | stockton-render/src/draw/context.rs | 307 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_buffers.rs | 4 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/cons.rs | 66 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/level.rs (renamed from stockton-render/src/draw/pipeline.rs) | 208 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/mod.rs | 43 | ||||
-rw-r--r-- | stockton-render/src/draw/mod.rs | 3 | ||||
-rw-r--r-- | stockton-render/src/draw/queue_negotiator.rs | 85 | ||||
-rw-r--r-- | stockton-render/src/draw/render.rs | 121 | ||||
-rw-r--r-- | stockton-render/src/draw/target.rs | 254 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/load.rs | 2 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/loader.rs | 14 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/repo.rs | 8 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/resolver.rs | 4 | ||||
-rw-r--r-- | stockton-render/src/draw/ui/render.rs | 2 | ||||
-rw-r--r-- | stockton-render/src/error.rs | 7 | ||||
-rw-r--r-- | stockton-render/src/lib.rs | 37 | ||||
-rw-r--r-- | stockton-render/src/window.rs | 28 |
19 files changed, 473 insertions, 728 deletions
diff --git a/stockton-render/src/culling.rs b/stockton-render/src/culling.rs index a5e942f..c10605f 100644 --- a/stockton-render/src/culling.rs +++ b/stockton-render/src/culling.rs @@ -2,7 +2,7 @@ #![allow(dead_code)] use stockton_levels::prelude::*; -use stockton_levels::traits::tree::{BspNode, BspNodeValue}; +use stockton_levels::parts::tree::{BspNode, BspNodeValue}; use stockton_types::Vector3; /// Get the visible faces according to visdata and frustum culling diff --git a/stockton-render/src/draw/camera.rs b/stockton-render/src/draw/camera.rs index d39febd..a9d10f4 100644 --- a/stockton-render/src/draw/camera.rs +++ b/stockton-render/src/draw/camera.rs @@ -4,7 +4,7 @@ use legion::maybe_changed; use nalgebra_glm::look_at_lh; use nalgebra_glm::perspective_lh_zo; -use stockton_levels::prelude::{MinBspFeatures, VulkanSystem}; +use stockton_levels::prelude::MinRenderFeatures; use crate::Renderer; use stockton_types::components::{CameraSettings, Transform}; @@ -24,10 +24,10 @@ fn euler_to_direction(euler: &Vector3) -> Vector3 { #[system(for_each)] #[filter(maybe_changed::<Transform>() | maybe_changed::<CameraSettings>())] -pub fn calc_vp_matrix<M: 'static + MinBspFeatures<VulkanSystem>>( +pub fn calc_vp_matrix<M: 'static + MinRenderFeatures>( transform: &Transform, settings: &CameraSettings, - #[resource] renderer: &mut Renderer<'static, M>, + #[resource] renderer: &mut Renderer<M>, ) { let ratio = renderer.context.target_chain.properties.extent.width as f32 / renderer.context.target_chain.properties.extent.height as f32; diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index 4231a2e..2d3d92d 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -1,32 +1,22 @@ //! Deals with all the Vulkan/HAL details. -//! In the end, this takes in a depth-sorted list of faces and a map file and renders them. -//! You'll need something else to actually find/sort the faces though. +//! This relies on draw passes for the actual drawing logic. use std::{ - iter::once, mem::ManuallyDrop, + ptr::read, sync::{Arc, RwLock}, }; use anyhow::{Context, Result}; -use arrayvec::ArrayVec; use hal::pool::CommandPoolCreateFlags; use log::debug; use na::Mat4; use winit::window::Window; use super::{ - buffer::ModifiableBuffer, - draw_buffers::{DrawBuffers, UvPoint}, - pipeline::CompletePipeline, + draw_passes::{DrawPass, IntoDrawPass, LevelDrawPass}, queue_negotiator::{DrawQueue, QueueNegotiator}, - render::do_render, target::{SwapchainProperties, TargetChain}, - texture::{resolver::FsResolver, TexLoadQueue, TextureLoadConfig, TextureRepo}, - ui::{ - do_render as do_render_ui, ensure_textures as ensure_textures_ui, UiPipeline, UiPoint, - UiTextures, - }, }; use crate::{ error::{EnvironmentError, LockPoisoned}, @@ -38,7 +28,7 @@ use stockton_levels::prelude::*; /// Contains all the hal related stuff. /// In the end, this takes in a depth-sorted list of faces and a map file and renders them. // TODO: Settings for clear colour, buffer sizes, etc -pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> { +pub struct RenderingContext<M: 'static + MinRenderFeatures, DP = LevelDrawPass<M>> { pub map: Arc<RwLock<M>>, // Parents for most of these things @@ -54,12 +44,6 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> { /// Swapchain and stuff pub(crate) target_chain: ManuallyDrop<TargetChain>, - /// Graphics pipeline and associated objects - pipeline: ManuallyDrop<CompletePipeline>, - - /// 2D Graphics pipeline and associated objects - ui_pipeline: ManuallyDrop<UiPipeline>, - // Command pool and buffers /// The command pool used for our buffers cmd_pool: ManuallyDrop<CommandPoolT>, @@ -67,17 +51,8 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> { /// The queue to use for drawing queue: Arc<RwLock<QueueT>>, - /// Main Texture repo - tex_repo: ManuallyDrop<TextureRepo<'a>>, - - /// UI Texture repo - ui_tex_repo: ManuallyDrop<TextureRepo<'a>>, - - /// Buffers used for drawing - draw_buffers: ManuallyDrop<DrawBuffers<'a, UvPoint>>, - - /// Buffers used for drawing the UI - ui_draw_buffers: ManuallyDrop<DrawBuffers<'a, UiPoint>>, + /// Deals with drawing logic, and holds any data required for drawing. + draw_pass: ManuallyDrop<DP>, /// View projection matrix pub(crate) vp_matrix: Mat4, @@ -85,9 +60,18 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures<VulkanSystem>> { pub(crate) pixels_per_point: f32, } -impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { +impl<M: 'static + MinRenderFeatures, DP> RenderingContext<M, DP> +where + DP: DrawPass<Input = M>, +{ + // TODO: Arbitrary drawpass input /// Create a new RenderingContext for the given window. - pub fn new(window: &Window, ui: &mut UiState, map: M) -> Result<Self> { + pub fn new<ILDP: IntoDrawPass<DP>>( + window: &Window, + _ui: &mut UiState, + map: M, + idp: ILDP, + ) -> Result<Self> { let map = Arc::new(RwLock::new(map)); // Create surface let (instance, surface, mut adapters) = unsafe { @@ -104,46 +88,65 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { // TODO: Properly figure out which adapter to use let adapter = adapters.remove(0); + // Queue Negotiator + let mut queue_families_specs = Vec::new(); let (mut queue_negotiator, surface) = { let dq: DrawQueue = DrawQueue { surface }; - let qn = QueueNegotiator::find(&adapter, &[&dq, &TexLoadQueue]) - .context("Error creating draw queue negotiator")?; + let mut qn = QueueNegotiator::new(); + + // Draw Queue + qn.find(&adapter, &dq) + .context("Couldn't find draw queue family")?; + queue_families_specs.push( + qn.family_spec::<DrawQueue>(&adapter.queue_families, 1) + .context("Couldn't find draw queue family")?, + ); + + // Auxiliary queues for DP + queue_families_specs.extend( + DP::find_aux_queues(&adapter, &mut qn) + .context("Level pass couldn't populate queue negotiator")?, + ); (qn, dq.surface) }; // Device & Queue groups let (device_lock, mut queue_groups) = { - let (df, dqs) = queue_negotiator - .family_spec::<DrawQueue>(&adapter.queue_families, 1) - .ok_or(EnvironmentError::NoSuitableFamilies)?; - let (tf, tqs) = queue_negotiator - .family_spec::<TexLoadQueue>(&adapter.queue_families, 2) - .ok_or(EnvironmentError::NoSuitableFamilies)?; + // TODO: This sucks, but hal is restrictive on how we can pass this specific argument. + let queue_families_specs_real: Vec<_> = queue_families_specs + .iter() + .map(|(qf, ns)| (*qf, ns.as_slice())) + .collect(); let gpu = unsafe { adapter .physical_device - .open( - &[(df, dqs.as_slice()), (tf, tqs.as_slice())], - hal::Features::empty(), - ) + .open(queue_families_specs_real.as_slice(), hal::Features::empty()) .context("Error opening logical device")? }; (Arc::new(RwLock::new(gpu.device)), gpu.queue_groups) }; + // Figure out what our swapchain will look like + let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface) + .context("Error getting properties for swapchain")?; + + // Draw pass + let dp = idp.init( + device_lock.clone(), + &mut queue_negotiator, + &swapchain_properties, + )?; + + // Lock device let mut device = device_lock .write() .map_err(|_| LockPoisoned::Device) .context("Error getting device lock")?; - // Figure out what our swapchain will look like - let swapchain_properties = SwapchainProperties::find_best(&adapter, &surface) - .context("Error getting properties for swapchain")?; - debug!("Detected swapchain properties: {:?}", swapchain_properties); // Command pool @@ -157,92 +160,18 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { } .context("Error creating draw command pool")?; - // Vertex and index buffers - let draw_buffers = - DrawBuffers::new(&mut device, &adapter).context("Error creating 3D draw buffers")?; - - // UI Vertex and index buffers - let ui_draw_buffers = - DrawBuffers::new(&mut device, &adapter).context("Error creating UI draw buffers")?; - - // We have to unlock device for creating texture repos - drop(device); - - // Texture repos - debug!("Creating 3D Texture Repo"); - let tex_repo = TextureRepo::new( - device_lock.clone(), - queue_negotiator - .family::<TexLoadQueue>() - .ok_or(EnvironmentError::NoQueues)?, - queue_negotiator - .get_queue::<TexLoadQueue>(&mut queue_groups) - .ok_or(EnvironmentError::NoQueues) - .context("Error getting 3D texture loader queue")?, - &adapter, - TextureLoadConfig { - resolver: FsResolver::new(std::path::Path::new("."), map.clone()), - filter: hal::image::Filter::Linear, - wrap_mode: hal::image::WrapMode::Tile, - }, - ) - .context("Error creating 3D Texture repo")?; // TODO - - debug!("Creating UI Texture Repo"); - let ui_tex_repo = TextureRepo::new( - device_lock.clone(), - queue_negotiator - .family::<TexLoadQueue>() - .ok_or(EnvironmentError::NoQueues)?, - queue_negotiator - .get_queue::<TexLoadQueue>(&mut queue_groups) - .ok_or(EnvironmentError::NoQueues) - .context("Error getting UI texture loader queue")?, - &adapter, - TextureLoadConfig { - resolver: UiTextures::new(ui.ctx().clone()), - filter: hal::image::Filter::Linear, - wrap_mode: hal::image::WrapMode::Clamp, - }, - ) - .context("Error creating UI texture repo")?; // TODO - - let mut device = device_lock.write().map_err(|_| LockPoisoned::Device)?; - - let ds_layout_lock = tex_repo.get_ds_layout()?; - let ui_ds_layout_lock = ui_tex_repo.get_ds_layout()?; - - // Graphics pipeline - let pipeline = CompletePipeline::new( - &mut device, - swapchain_properties.extent, - &swapchain_properties, - once(&*ds_layout_lock), - )?; - - // UI pipeline - let ui_pipeline = UiPipeline::new( - &mut device, - swapchain_properties.extent, - &swapchain_properties, - once(&*ui_ds_layout_lock), - )?; - // Swapchain and associated resources let target_chain = TargetChain::new( &mut device, &adapter, surface, - &pipeline, - &ui_pipeline, &mut cmd_pool, swapchain_properties, ) .context("Error creating target chain")?; + // Unlock device drop(device); - drop(ds_layout_lock); - drop(ui_ds_layout_lock); Ok(RenderingContext { map, @@ -256,18 +185,10 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { .ok_or(EnvironmentError::NoQueues) .context("Error getting draw queue")?, + draw_pass: ManuallyDrop::new(dp), target_chain: ManuallyDrop::new(target_chain), cmd_pool: ManuallyDrop::new(cmd_pool), - pipeline: ManuallyDrop::new(pipeline), - ui_pipeline: ManuallyDrop::new(ui_pipeline), - - tex_repo: ManuallyDrop::new(tex_repo), - ui_tex_repo: ManuallyDrop::new(ui_tex_repo), - - draw_buffers: ManuallyDrop::new(draw_buffers), - ui_draw_buffers: ManuallyDrop::new(ui_draw_buffers), - vp_matrix: Mat4::identity(), // pixels_per_point: window.scale_factor() as f32, @@ -295,47 +216,13 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { let properties = SwapchainProperties::find_best(&self.adapter, &surface) .context("Error finding best swapchain properties")?; - use core::ptr::read; - - // Graphics pipeline - // TODO: Recycle - let ds_layout_handle = self.tex_repo.get_ds_layout()?; - let ui_ds_layout_handle = self.tex_repo.get_ds_layout()?; - - ManuallyDrop::into_inner(read(&self.pipeline)).deactivate(&mut device); - self.pipeline = ManuallyDrop::new({ - CompletePipeline::new( - &mut device, - properties.extent, - &properties, - once(&*ds_layout_handle), - ) - .context("Error creating 3D Pipeline")? - }); - - // 2D Graphics pipeline - // TODO: Recycle - ManuallyDrop::into_inner(read(&self.ui_pipeline)).deactivate(&mut device); - self.ui_pipeline = ManuallyDrop::new({ - let mut descriptor_set_layouts: ArrayVec<[_; 1]> = ArrayVec::new(); - descriptor_set_layouts.push(&*ui_ds_layout_handle); - - UiPipeline::new( - &mut device, - properties.extent, - &properties, - once(&*ui_ds_layout_handle), - ) - .context("Error creating UI Pipeline")? - }); + // TODO: Notify draw passes self.target_chain = ManuallyDrop::new( TargetChain::new( &mut device, &self.adapter, surface, - &self.pipeline, - &self.ui_pipeline, &mut self.cmd_pool, properties, ) @@ -345,7 +232,7 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { } /// Draw all vertices in the buffer - pub fn draw_vertices(&mut self, ui: &mut UiState, faces: &[u32]) -> Result<()> { + pub fn draw_next_frame(&mut self) -> Result<()> { let mut device = self .device .write() @@ -356,95 +243,26 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> RenderingContext<'a, M> { .write() .map_err(|_| LockPoisoned::Map) .context("Error getting map lock")?; + let map = self.map.read().or(Err(LockPoisoned::Map))?; - // Ensure UI texture(s) are loaded - ensure_textures_ui(&mut self.ui_tex_repo, ui)?; - - // Get any textures that just finished loading - self.ui_tex_repo.process_responses(); - self.tex_repo.process_responses(); - - // 3D Pass - let (cmd_buffer, img) = self - .target_chain - .prep_next_target( - &mut device, - &mut self.draw_buffers, - &self.pipeline, - &self.vp_matrix, - ) - .context("Error preparing next target")?; - - do_render( - cmd_buffer, - &mut self.draw_buffers, - &mut self.tex_repo, - &self.pipeline.pipeline_layout, - &*self - .map - .read() - .map_err(|_| LockPoisoned::Map) - .context("Error getting map read lock")?, - faces, - )?; - - // 2D Pass - let cmd_buffer = self - .target_chain - .target_2d_pass(&mut self.ui_draw_buffers, &img, &self.ui_pipeline) - .context("Error switching to 2D pass")?; - - do_render_ui( - cmd_buffer, - &self.ui_pipeline.pipeline_layout, - &mut self.ui_draw_buffers, - &mut self.ui_tex_repo, - ui, - )?; - - // Update our buffers before we actually start drawing - self.draw_buffers - .vertex_buffer - .commit(&device, &mut queue, &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 queue, &mut self.cmd_pool)?; - - self.ui_draw_buffers - .index_buffer - .commit(&device, &mut queue, &mut self.cmd_pool)?; - - // Send commands off to GPU + // Level draw pass self.target_chain - .finish_and_submit_target(img, &mut queue) - .context("Error finishing and submitting target")?; + .do_draw_with(&mut device, &mut queue, &*self.draw_pass, &*map) + .context("Error preparing next target")?; Ok(()) } } -impl<'a, M: MinBspFeatures<VulkanSystem>> core::ops::Drop for RenderingContext<'a, M> { +impl<M: 'static + MinRenderFeatures, LDP> core::ops::Drop for RenderingContext<M, LDP> { fn drop(&mut self) { { self.device.write().unwrap().wait_idle().unwrap(); } unsafe { - use core::ptr::read; - - ManuallyDrop::into_inner(read(&self.tex_repo)).deactivate(&mut self.device); - ManuallyDrop::into_inner(read(&self.ui_tex_repo)).deactivate(&mut self.device); - let mut device = self.device.write().unwrap(); - ManuallyDrop::into_inner(read(&self.draw_buffers)).deactivate(&mut device); - ManuallyDrop::into_inner(read(&self.ui_draw_buffers)).deactivate(&mut device); - ManuallyDrop::into_inner(read(&self.target_chain)).deactivate( &mut self.instance, &mut device, @@ -452,9 +270,6 @@ impl<'a, M: MinBspFeatures<VulkanSystem>> core::ops::Drop for RenderingContext<' ); 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); } } } diff --git a/stockton-render/src/draw/draw_buffers.rs b/stockton-render/src/draw/draw_buffers.rs index fba3eed..2d2fce4 100644 --- a/stockton-render/src/draw/draw_buffers.rs +++ b/stockton-render/src/draw/draw_buffers.rs @@ -22,9 +22,9 @@ pub struct DrawBuffers<'a, T: Sized> { impl<'a, T> DrawBuffers<'a, T> { pub fn new(device: &mut DeviceT, adapter: &Adapter) -> Result<DrawBuffers<'a, T>> { - let vert = StagedBuffer::new(device, &adapter, Usage::VERTEX, INITIAL_VERT_SIZE) + let vert = StagedBuffer::new(device, adapter, Usage::VERTEX, INITIAL_VERT_SIZE) .context("Error creating vertex buffer")?; - let index = StagedBuffer::new(device, &adapter, Usage::INDEX, INITIAL_INDEX_SIZE) + let index = StagedBuffer::new(device, adapter, Usage::INDEX, INITIAL_INDEX_SIZE) .context("Error creating index buffer")?; Ok(DrawBuffers { diff --git a/stockton-render/src/draw/draw_passes/cons.rs b/stockton-render/src/draw/draw_passes/cons.rs new file mode 100644 index 0000000..274bd76 --- /dev/null +++ b/stockton-render/src/draw/draw_passes/cons.rs @@ -0,0 +1,66 @@ +//! Code for using multiple draw passes in place of just one +//! Note that this can be extended to an arbitrary amount of draw passes. + +use super::{DrawPass, DrawPassInput}; +use crate::{draw::queue_negotiator::QueueNegotiator, types::*}; +use anyhow::Result; + +/// One draw pass, then another. +struct ConsDrawPass<A: DrawPass, B: DrawPass> { + a: A, + b: B, +} + +impl<A: DrawPass, B: DrawPass> DrawPass for ConsDrawPass<A, B> { + type Input = ConsDrawPassInput<A::Input, B::Input>; + + fn queue_draw(&self, input: &Self::Input, cmd_buffer: &mut CommandBufferT) -> Result<()> { + self.a.queue_draw(&input.a, cmd_buffer)?; + self.b.queue_draw(&input.b, cmd_buffer)?; + + Ok(()) + } + + fn find_aux_queues<'a>( + adapter: &'a Adapter, + queue_negotiator: &mut QueueNegotiator, + ) -> Result<Vec<(&'a QueueFamilyT, Vec<f32>)>> { + let mut vec = Vec::new(); + + vec.extend(A::find_aux_queues(adapter, queue_negotiator)?); + vec.extend(B::find_aux_queues(adapter, queue_negotiator)?); + + Ok(vec) + } +} + +/// Input for a ConsDrawPass. +struct ConsDrawPassInput<A, B> { + pub a: A, + pub b: B, +} + +impl<A: DrawPassInput, B: DrawPassInput> DrawPassInput for ConsDrawPassInput<A, B> {} + +/// A draw pass that does nothing. Can be used at the end of sequences if there's an odd number of draw passes. +struct NilDrawPass; + +impl DrawPass for NilDrawPass { + type Input = NilDrawPassInput; + + fn queue_draw(&self, _input: &Self::Input, _cmd_buffer: &mut CommandBufferT) -> Result<()> { + Ok(()) + } + + fn find_aux_queues<'a>( + _adapter: &'a Adapter, + _queue_negotiator: &mut QueueNegotiator, + ) -> Result<Vec<(&'a QueueFamilyT, Vec<f32>)>> { + Ok(vec![]) + } +} + +/// Input for a NilDrawPass. +struct NilDrawPassInput; + +impl DrawPassInput for NilDrawPassInput {} diff --git a/stockton-render/src/draw/pipeline.rs b/stockton-render/src/draw/draw_passes/level.rs index 84b541f..afcb703 100644 --- a/stockton-render/src/draw/pipeline.rs +++ b/stockton-render/src/draw/draw_passes/level.rs @@ -1,25 +1,189 @@ -//! A complete graphics pipeline +//! Minimal code for drawing any level, based on traits from stockton-levels -/// Entry point name for shaders -const ENTRY_NAME: &str = "main"; - -/// Source for vertex shader. TODO -const VERTEX_SOURCE: &str = include_str!("./data/stockton.vert"); - -/// Source for fragment shader. TODO -const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag"); +use super::{DrawPass, DrawPassInput, IntoDrawPass}; +use crate::{ + draw::{queue_negotiator::QueueNegotiator, target::SwapchainProperties, texture::TextureRepo}, + error::EnvironmentError, + types::*, +}; +use stockton_levels::features::MinRenderFeatures; +use stockton_types::*; use std::{ array::IntoIter, iter::{empty, once}, + marker::PhantomData, mem::{size_of, ManuallyDrop}, + sync::{Arc, RwLock}, }; -use super::target::SwapchainProperties; -use crate::{error::EnvironmentError, types::*}; use anyhow::{Context, Result}; -// TODO: Generalise so we can use for UI also +/// The Vertexes that go to the shader +#[derive(Debug, Clone, Copy)] +struct UvPoint(pub Vector3, pub i32, pub Vector2); + +/// Draw a level +pub struct LevelDrawPass<M: MinRenderFeatures> { + pipeline: CompletePipeline, + repo: TextureRepo, + _d: PhantomData<M>, +} + +/// Any map can be used as draw pass input. +/// TODO: Restrict this based on the type of the renderer. +impl<T: MinRenderFeatures> DrawPassInput for T {} + +impl<M: MinRenderFeatures> DrawPass for LevelDrawPass<M> { + type Input = M; + + fn queue_draw( + &self, + _file: &Self::Input, + _cmd_buffer: &mut crate::types::CommandBufferT, + ) -> anyhow::Result<()> { + todo!() + // // Get visible faces + // // let faces = get_visible_faces( + // // pos, + // // &*self + // // .context + // // .map + // // .read() + // // .map_err(|_| LockPoisoned::Map) + // // .context("Error getting read lock on map")?, + // // ); + // let faces: Vec<u32> = { + // let map = &*self + // .context + // .map + // .read() + // .map_err(|_| LockPoisoned::Map) + // .context("Error getting read lock on map")?; + + // map.iter_faces().map(|x| x.index(map)).collect() + // }; + + // // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once. + // let mut current_chunk = file + // .get_face(0) + // .ok_or(LevelError::BadReference)? + // .texture_idx(file) as usize + // / 8; + // let mut chunk_start = 0; + + // let mut curr_vert_idx: usize = 0; + // let mut curr_idx_idx: usize = 0; + + // for face in faces.iter().map(|idx| file.get_face(*idx)) { + // if let Some(face) = face { + // if current_chunk != face.texture_idx(file) as usize / 8 { + // // Last index was last of group, so draw it all if textures are loaded. + // draw_or_queue( + // current_chunk, + // self.tex_repo, + // cmd_buffer, + // self.pipeline.pipeline_layout, + // chunk_start as u32, + // curr_idx_idx as u32, + // )?; + + // // Next group of same-chunked faces starts here. + // chunk_start = curr_idx_idx; + // current_chunk = face.texture_idx(file) as usize / 8; + // } + + // match face.geometry(file) { + // Geometry::Vertices(v1, v2, v3) => { + // for v in [v1, v2, v3] { + // let uvp = + // UvPoint(v.position, face.texture_idx(file).try_into()?, v.tex); + + // draw_buffers.vertex_buffer[curr_vert_idx] = uvp; + // curr_vert_idx += 1; + // } + // draw_buffers.index_buffer[curr_idx_idx] = ( + // curr_vert_idx as u16 - 2, + // curr_vert_idx as u16 - 1, + // curr_vert_idx as u16, + // ); + // curr_idx_idx += 1; + // } + // } + + // if curr_vert_idx >= INITIAL_VERT_SIZE.try_into()? + // || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into()? + // { + // println!("out of vertex buffer space!"); + // break; + // } + // } else { + // anyhow::bail!(LevelError::BadReference); + // } + // } + + // // Draw the final group of chunks + // draw_or_queue( + // current_chunk, + // self.tex_repo, + // cmd_buffer, + // self.pipeline.pipeline_layout, + // chunk_start as u32, + // curr_idx_idx as u32, + // )?; + + // Ok(()) + } + + fn find_aux_queues<'a>( + _adapter: &'a Adapter, + _queue_negotiator: &mut QueueNegotiator, + ) -> Result<Vec<(&'a QueueFamilyT, Vec<f32>)>> { + todo!() + // queue_negotiator.find(TexLoadQueue) + } +} + +impl<M: MinRenderFeatures> IntoDrawPass<LevelDrawPass<M>> for () { + fn init( + self, + _device_lock: Arc<RwLock<DeviceT>>, + _queue_negotiator: &mut QueueNegotiator, + _swapchain_properties: &SwapchainProperties, + ) -> Result<LevelDrawPass<M>> { + todo!() + // let repo = TextureRepo::new( + // device_lock.clone(), + // queue_negotiator + // .family() + // .ok_or(EnvironmentError::NoQueues)?, + // ); + // let pipeline = { + // let device = device_lock.write().or(Err(LockPoisoned::Device))?; + // CompletePipeline::new( + // device, + // swapchain_properties.extent, + // swapchain_properties, + // std::iter::empty(), + // )? + // }; + // Ok(LevelDrawPass { + // pipeline, + // repo, + // _d: PhantomData, + // }) + } +} + +/// Entry point name for shaders +const ENTRY_NAME: &str = "main"; + +/// Source for vertex shader. TODO +const VERTEX_SOURCE: &str = include_str!("../data/stockton.vert"); + +/// Source for fragment shader. TODO +const FRAGMENT_SOURCE: &str = include_str!("../data/stockton.frag"); + /// A complete graphics pipeline and associated resources pub struct CompletePipeline { /// Our main render pass @@ -297,3 +461,23 @@ impl CompletePipeline { } } } + +fn draw_or_queue( + current_chunk: usize, + tex_repo: &mut TextureRepo, + cmd_buffer: &mut CommandBufferT, + pipeline_layout: &PipelineLayoutT, + chunk_start: u32, + curr_idx_idx: u32, +) -> Result<()> { + if let Some(ds) = tex_repo.attempt_get_descriptor_set(current_chunk) { + unsafe { + 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 { + tex_repo.queue_load(current_chunk)? + } + + Ok(()) +} diff --git a/stockton-render/src/draw/draw_passes/mod.rs b/stockton-render/src/draw/draw_passes/mod.rs new file mode 100644 index 0000000..76dd8d6 --- /dev/null +++ b/stockton-render/src/draw/draw_passes/mod.rs @@ -0,0 +1,43 @@ +//! Traits and common draw passes. + +mod cons; +mod level; +use std::sync::{Arc, RwLock}; + +pub use level::LevelDrawPass; + +use super::{queue_negotiator::QueueNegotiator, target::SwapchainProperties}; +use crate::types::*; +use anyhow::Result; + +/// Type can be used as input to a draw pass. This requires it being available from only the resources at draw time. +pub trait DrawPassInput {} + +/// One of several 'passes' that draw on each frame. +pub trait DrawPass { + /// Extra input required for this draw pass. + type Input: DrawPassInput; + + /// Queue any necessary draw commands to cmd_buffer + /// This should assume the command buffer isn't in the middle of a renderpass, and should leave it as such. + fn queue_draw(&self, input: &Self::Input, cmd_buffer: &mut CommandBufferT) -> Result<()>; + + /// This function should ask the queue negotatior to find families for any auxilary operations this draw pass needs to perform + /// For example, .find(&TexLoadQueue) + /// It should return then call .family_spec for each queue type negotiated and return the results. + fn find_aux_queues<'a>( + adapter: &'a Adapter, + queue_negotiator: &mut QueueNegotiator, + ) -> Result<Vec<(&'a QueueFamilyT, Vec<f32>)>>; +} + +/// A type that can be made into a specific draw pass type. +/// This allows extra data to be used in initialisation without the Renderer needing to worry about it. +pub trait IntoDrawPass<O: DrawPass> { + fn init( + self, + device: Arc<RwLock<DeviceT>>, + queue_negotiator: &mut QueueNegotiator, + swapchain_properties: &SwapchainProperties, + ) -> Result<O>; +} diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs index e802ed5..5e1d2cd 100644 --- a/stockton-render/src/draw/mod.rs +++ b/stockton-render/src/draw/mod.rs @@ -7,9 +7,8 @@ mod camera; mod context; mod depth_buffer; mod draw_buffers; -mod pipeline; +pub mod draw_passes; mod queue_negotiator; -mod render; mod texture; mod ui; mod utils; diff --git a/stockton-render/src/draw/queue_negotiator.rs b/stockton-render/src/draw/queue_negotiator.rs index c5d751b..b39678f 100644 --- a/stockton-render/src/draw/queue_negotiator.rs +++ b/stockton-render/src/draw/queue_negotiator.rs @@ -1,73 +1,72 @@ use crate::{error::EnvironmentError, types::*}; + use anyhow::{Error, Result}; use hal::queue::family::QueueFamilyId; -use std::any::TypeId; -use std::collections::HashMap; -use std::sync::{Arc, RwLock}; +use std::{ + any::TypeId, + collections::HashMap, + sync::{Arc, RwLock}, +}; +/// Used to find appropriate queue families and share queues from them as needed. pub struct QueueNegotiator { family_ids: HashMap<TypeId, QueueFamilyId>, already_allocated: HashMap<TypeId, (Vec<Arc<RwLock<QueueT>>>, usize)>, } +/// Can be used to select a specific queue family pub trait QueueFamilySelector: 'static { + /// Check if the given family is suitable fn is_suitable(&self, family: &QueueFamilyT) -> bool; +} - fn get_type_id_self(&self) -> TypeId { - TypeId::of::<Self>() +impl QueueNegotiator { + pub fn new() -> Self { + QueueNegotiator { + family_ids: HashMap::new(), + already_allocated: HashMap::new(), + } } - fn get_type_id() -> TypeId - where - Self: Sized, - { - TypeId::of::<Self>() - } -} + pub fn find<T: QueueFamilySelector>(&mut self, adapter: &Adapter, filter: &T) -> Result<()> { + if self.family_ids.contains_key(&TypeId::of::<T>()) { + return Ok(()); + } -impl QueueNegotiator { - pub fn find(adapter: &Adapter, stacks: &[&dyn QueueFamilySelector]) -> Result<Self> { - let mut families = HashMap::new(); - for filter in stacks { - let candidates: Vec<&QueueFamilyT> = adapter - .queue_families - .iter() - .filter(|x| filter.is_suitable(*x)) - .collect(); - - if candidates.len() == 0 { - return Err(Error::new(EnvironmentError::NoSuitableFamilies)); - } + let candidates: Vec<&QueueFamilyT> = adapter + .queue_families + .iter() + .filter(|x| filter.is_suitable(*x)) + .collect(); - // Prefer using unique families - let family = match candidates - .iter() - .filter(|x| !families.values().any(|y| *y == x.id())) - .next() - { - Some(x) => *x, - None => candidates[0], - }; - - families.insert(filter.get_type_id_self(), family.id()); + if candidates.is_empty() { + return Err(Error::new(EnvironmentError::NoSuitableFamilies)); } - Ok(QueueNegotiator { - family_ids: families, - already_allocated: HashMap::new(), - }) + // Prefer using unique families + let family = match candidates + .iter() + .find(|x| !self.family_ids.values().any(|y| *y == x.id())) + { + Some(x) => *x, + None => candidates[0], + }; + + self.family_ids.insert(TypeId::of::<T>(), family.id()); + + Ok(()) } pub fn get_queue<T: QueueFamilySelector>( &mut self, groups: &mut Vec<QueueGroup>, ) -> Option<Arc<RwLock<QueueT>>> { - let tid = T::get_type_id(); + let tid = TypeId::of::<T>(); let family_id = self.family_ids.get(&tid)?; match groups .iter() - .position(|x| x.queues.len() > 0 && x.family == *family_id) + .position(|x| !x.queues.is_empty() && x.family == *family_id) { Some(idx) => { // At least one remaining queue @@ -96,7 +95,7 @@ impl QueueNegotiator { } fn add_to_allocated<T: QueueFamilySelector>(&mut self, queue: Arc<RwLock<QueueT>>) { - let tid = T::get_type_id(); + let tid = TypeId::of::<T>(); match self.already_allocated.get_mut(&tid) { None => { self.already_allocated.insert(tid, (vec![queue], 0)); diff --git a/stockton-render/src/draw/render.rs b/stockton-render/src/draw/render.rs deleted file mode 100644 index ac18dea..0000000 --- a/stockton-render/src/draw/render.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::draw::draw_buffers::INITIAL_INDEX_SIZE; -use crate::draw::draw_buffers::INITIAL_VERT_SIZE; -use crate::draw::UvPoint; -use faces::FaceType; -use std::{ - convert::TryInto, - iter::{empty, once}, -}; -use stockton_levels::prelude::*; -use stockton_types::Vector2; - -use crate::draw::draw_buffers::DrawBuffers; -use crate::types::*; -use anyhow::Result; - -use super::texture::TextureRepo; - -fn draw_or_queue( - current_chunk: usize, - tex_repo: &mut TextureRepo, - cmd_buffer: &mut CommandBufferT, - pipeline_layout: &PipelineLayoutT, - chunk_start: u32, - curr_idx_idx: u32, -) -> Result<()> { - if let Some(ds) = tex_repo.attempt_get_descriptor_set(current_chunk) { - unsafe { - 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 { - tex_repo.queue_load(current_chunk)? - } - - Ok(()) -} - -pub fn do_render<M: MinBspFeatures<VulkanSystem>>( - cmd_buffer: &mut CommandBufferT, - draw_buffers: &mut DrawBuffers<UvPoint>, - tex_repo: &mut TextureRepo, - pipeline_layout: &PipelineLayoutT, - file: &M, - faces: &[u32], -) -> Result<()> { - // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once. - let mut current_chunk = file.get_face(0).texture_idx as usize / 8; - let mut chunk_start = 0; - - let mut curr_vert_idx: usize = 0; - let mut curr_idx_idx: usize = 0; - - for face in faces.iter().map(|idx| file.get_face(*idx)) { - if current_chunk != face.texture_idx as usize / 8 { - // Last index was last of group, so draw it all if textures are loaded. - draw_or_queue( - current_chunk, - tex_repo, - cmd_buffer, - pipeline_layout, - chunk_start as u32, - curr_idx_idx as u32, - )?; - - // Next group of same-chunked faces starts here. - chunk_start = curr_idx_idx; - current_chunk = face.texture_idx as usize / 8; - } - - if face.face_type == FaceType::Polygon || face.face_type == FaceType::Mesh { - // 2 layers of indirection - let base = face.vertices_idx.start; - - for idx in face.meshverts_idx.clone().step_by(3) { - let start_idx: u16 = curr_vert_idx.try_into()?; - - for idx2 in idx..idx + 3 { - let vert = &file.resolve_meshvert(idx2 as u32, base); - let uv = Vector2::new(vert.tex.u[0], vert.tex.v[0]); - - let uvp = UvPoint(vert.position, face.texture_idx.try_into()?, uv); - draw_buffers.vertex_buffer[curr_vert_idx] = uvp; - - curr_vert_idx += 1; - } - - draw_buffers.index_buffer[curr_idx_idx] = (start_idx, start_idx + 1, start_idx + 2); - - curr_idx_idx += 1; - - if curr_vert_idx >= INITIAL_VERT_SIZE.try_into()? - || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into()? - { - println!("out of vertex buffer space!"); - break; - } - } - } else { - // TODO: Other types of faces - } - - if curr_vert_idx >= INITIAL_VERT_SIZE.try_into()? - || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into()? - { - println!("out of vertex buffer space!"); - break; - } - } - - // Draw the final group of chunks - draw_or_queue( - current_chunk, - tex_repo, - cmd_buffer, - pipeline_layout, - chunk_start as u32, - curr_idx_idx as u32, - )?; - - Ok(()) -} diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs index 23c15cc..754bad3 100644 --- a/stockton-render/src/draw/target.rs +++ b/stockton-render/src/draw/target.rs @@ -1,30 +1,18 @@ //! Resources needed for drawing on the screen, including sync objects use std::{ - array::IntoIter, - borrow::Borrow, iter::{empty, once}, mem::ManuallyDrop, }; -use arrayvec::ArrayVec; use hal::{ - buffer::SubRange, - command::RenderAttachmentInfo, format::{ChannelType, Format, ImageFeature}, - image::{Extent, FramebufferAttachment, Usage as ImgUsage, ViewCapabilities}, + image::{Extent, Usage as ImgUsage}, pso::Viewport, window::{CompositeAlphaMode, Extent2D, PresentMode, SwapchainConfig}, }; -use na::Mat4; - -use super::{ - buffer::ModifiableBuffer, - depth_buffer::DedicatedLoadedImage, - draw_buffers::{DrawBuffers, UvPoint}, - pipeline::CompletePipeline, - ui::{UiPipeline, UiPoint}, -}; + +use super::{depth_buffer::DedicatedLoadedImage, draw_passes::DrawPass}; use crate::{error::EnvironmentError, types::*}; use anyhow::{Context, Result}; @@ -139,8 +127,6 @@ impl TargetChain { device: &mut DeviceT, adapter: &Adapter, mut surface: SurfaceT, - pipeline: &CompletePipeline, - ui_pipeline: &UiPipeline, cmd_pool: &mut CommandPoolT, properties: SwapchainProperties, ) -> Result<TargetChain> { @@ -189,7 +175,7 @@ impl TargetChain { .context("Error creating depth buffer")? }; - let fat = swap_config.framebuffer_attachment(); + 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> = @@ -197,20 +183,8 @@ impl TargetChain { for _ in 0..swap_config.image_count { targets.push( - TargetResources::new( - device, - cmd_pool, - &pipeline.renderpass, - &ui_pipeline.renderpass, - fat.clone(), - FramebufferAttachment { - usage: ImgUsage::DEPTH_STENCIL_ATTACHMENT, - view_caps: ViewCapabilities::empty(), - format: properties.depth_format, - }, - &properties, - ) - .context("Error creating target resources")?, + TargetResources::new(device, cmd_pool, &properties) + .context("Error creating target resources")?, ); sync_objects.push(SyncObjects::new(device).context("Error creating sync objects")?); @@ -270,16 +244,13 @@ impl TargetChain { unsafe { ManuallyDrop::into_inner(read(&self.surface)) } } - pub fn prep_next_target<'a>( + pub fn do_draw_with<'a, DP: DrawPass>( &'a mut self, device: &mut DeviceT, - draw_buffers: &mut DrawBuffers<UvPoint>, - pipeline: &CompletePipeline, - vp: &Mat4, - ) -> Result<( - &'a mut crate::types::CommandBufferT, - <SurfaceT as PresentationSurface<back::Backend>>::SwapchainImage, - )> { + command_queue: &mut QueueT, + dp: &DP, + dpi: &DP::Input, + ) -> Result<()> { self.last_syncs = (self.last_syncs + 1) % self.sync_objects.len(); self.last_image = (self.last_image + 1) % self.targets.len() as u32; @@ -304,169 +275,9 @@ impl TargetChain { }; // Record commands + dp.queue_draw(dpi, &mut target.cmd_buffer) + .context("Error in draw pass")?; unsafe { - use hal::command::{ - ClearColor, ClearDepthStencil, ClearValue, CommandBufferFlags, SubpassContents, - }; - use hal::pso::ShaderStageFlags; - - // 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, - SubRange { - offset: 0, - size: None, - }, - )] - .into(); - let ibuf = draw_buffers.index_buffer.get_buffer(); - - (vbufs, ibuf) - }; - - 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, - 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); - - // VP Matrix - let vp = &*(vp.data.as_slice() as *const [f32] as *const [u32]); - - target.cmd_buffer.push_graphics_constants( - &pipeline.pipeline_layout, - ShaderStageFlags::VERTEX, - 0, - vp, - ); - - // Bind buffers - 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, 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 CommandBufferT> { - let target = &mut self.targets[self.last_image as usize]; - - unsafe { - use hal::pso::PipelineStage; - target.cmd_buffer.end_render_pass(); - - 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::command::{ClearColor, ClearValue, SubpassContents}; - - // 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, - SubRange { - offset: 0, - size: None, - }, - )] - .into(); - let ibuf = draw_buffers.index_buffer.get_buffer(); - - (vbufs, ibuf) - }; - - // Main render pass / pipeline - target.cmd_buffer.begin_render_pass( - &pipeline.renderpass, - &target.framebuffer_2d, - self.properties.viewport.rect, - 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.into_iter()); - target.cmd_buffer.bind_index_buffer( - &ibuf, - SubRange { - offset: 0, - size: None, - }, - hal::IndexType::U16, - ); - }; - - Ok(&mut target.cmd_buffer) - } - - pub fn finish_and_submit_target( - &mut self, - img: <SurfaceT as PresentationSurface<back::Backend>>::SwapchainImage, - command_queue: &mut QueueT, - ) -> Result<()> { - let syncs = &mut self.sync_objects[self.last_syncs]; - let target = &mut self.targets[self.last_image as usize]; - - unsafe { - target.cmd_buffer.end_render_pass(); target.cmd_buffer.finish(); } @@ -491,59 +302,26 @@ impl TargetChain { pub struct TargetResources { /// Command buffer to use when drawing pub cmd_buffer: ManuallyDrop<CommandBufferT>, - - /// Framebuffer for this frame - pub framebuffer: ManuallyDrop<FramebufferT>, - - /// Framebuffer for this frame when drawing in 2D - pub framebuffer_2d: ManuallyDrop<FramebufferT>, } impl TargetResources { pub fn new( - device: &mut DeviceT, + _device: &mut DeviceT, cmd_pool: &mut CommandPoolT, - renderpass: &RenderPassT, - renderpass_2d: &RenderPassT, - fat: FramebufferAttachment, - dat: FramebufferAttachment, - properties: &SwapchainProperties, + _properties: &SwapchainProperties, ) -> Result<TargetResources> { // Command Buffer let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) }; - // Framebuffer - let framebuffer = unsafe { - device - .create_framebuffer( - &renderpass, - IntoIter::new([fat.clone(), dat]), - properties.extent, - ) - .context("Error creating colour framebuffer")? - }; - - // 2D framebuffer just needs the imageview, not the depth pass - let framebuffer_2d = unsafe { - device - .create_framebuffer(&renderpass_2d, once(fat), properties.extent) - .context("Error creating depth framebuffer")? - }; - Ok(TargetResources { cmd_buffer: ManuallyDrop::new(cmd_buffer), - framebuffer: ManuallyDrop::new(framebuffer), - framebuffer_2d: ManuallyDrop::new(framebuffer_2d), }) } - pub fn deactivate(self, device: &mut DeviceT, cmd_pool: &mut CommandPoolT) { + 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))); } } } diff --git a/stockton-render/src/draw/texture/load.rs b/stockton-render/src/draw/texture/load.rs index e278fa2..1f33ad5 100644 --- a/stockton-render/src/draw/texture/load.rs +++ b/stockton-render/src/draw/texture/load.rs @@ -112,7 +112,7 @@ where unsafe { device - .bind_image_memory(&block.memory(), block.range().start, &mut image_ref) + .bind_image_memory(block.memory(), block.range().start, &mut image_ref) .context("Error binding memory to image")?; } diff --git a/stockton-render/src/draw/texture/loader.rs b/stockton-render/src/draw/texture/loader.rs index ebb2bcc..e6c19db 100644 --- a/stockton-render/src/draw/texture/loader.rs +++ b/stockton-render/src/draw/texture/loader.rs @@ -234,7 +234,7 @@ impl<R: TextureResolver> TextureLoader<R> { let props = MemProps::DEVICE_LOCAL; DynamicAllocator::new( - find_memory_type_id(&adapter, type_mask, props) + find_memory_type_id(adapter, type_mask, props) .ok_or(TextureLoaderError::NoMemoryTypes) .context("Couldn't create tex memory allocator")?, props, @@ -249,7 +249,7 @@ impl<R: TextureResolver> TextureLoader<R> { let (staging_memory_type, mut staging_allocator) = { let props = MemProps::CPU_VISIBLE | MemProps::COHERENT; - let t = find_memory_type_id(&adapter, u32::MAX, props) + let t = find_memory_type_id(adapter, u32::MAX, props) .ok_or(TextureLoaderError::NoMemoryTypes) .context("Couldn't create staging memory allocator")?; ( @@ -557,7 +557,7 @@ impl<R: TextureResolver> TextureLoader<R> { staging_memory_type, obcpa, img_data, - &config, + config, )?; buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); @@ -590,8 +590,8 @@ impl<R: TextureResolver> TextureLoader<R> { image_layers: LAYERS, image_offset: Offset { x: 0, y: 0, z: 0 }, image_extent: Extent { - width: width, - height: height, + width, + height, depth: 1, }, }), @@ -678,9 +678,9 @@ impl<R: TextureResolver> TextureLoader<R> { read(&*self.blank_image).deactivate(&mut device, &mut *self.tex_allocator); // Destroy fences - let vec: Vec<_> = self.buffers.drain(..).collect(); - vec.into_iter() + self.buffers + .drain(..) .map(|(f, _)| device.destroy_fence(f)) .for_each(|_| {}); diff --git a/stockton-render/src/draw/texture/repo.rs b/stockton-render/src/draw/texture/repo.rs index 2df7bd3..e427eef 100644 --- a/stockton-render/src/draw/texture/repo.rs +++ b/stockton-render/src/draw/texture/repo.rs @@ -12,7 +12,6 @@ use std::{ array::IntoIter, collections::HashMap, iter::empty, - marker::PhantomData, mem::ManuallyDrop, sync::{ mpsc::{channel, Receiver, Sender}, @@ -33,17 +32,15 @@ use log::debug; /// Whenever a texture is needed, the whole block its in is loaded. pub const BLOCK_SIZE: usize = 8; -pub struct TextureRepo<'a> { +pub struct TextureRepo { joiner: ManuallyDrop<JoinHandle<Result<TextureLoaderRemains>>>, ds_layout: Arc<RwLock<DescriptorSetLayoutT>>, req_send: Sender<LoaderRequest>, resp_recv: Receiver<TexturesBlock<DynamicBlock>>, blocks: HashMap<BlockRef, Option<TexturesBlock<DynamicBlock>>>, - - _a: PhantomData<&'a ()>, } -impl<'a> TextureRepo<'a> { +impl TextureRepo { pub fn new<R: 'static + TextureResolver + Send + Sync>( device_lock: Arc<RwLock<DeviceT>>, family: QueueFamilyId, @@ -114,7 +111,6 @@ impl<'a> TextureRepo<'a> { blocks: HashMap::new(), req_send, resp_recv, - _a: PhantomData::default(), }) } diff --git a/stockton-render/src/draw/texture/resolver.rs b/stockton-render/src/draw/texture/resolver.rs index 1ecb7a0..1dbc62d 100644 --- a/stockton-render/src/draw/texture/resolver.rs +++ b/stockton-render/src/draw/texture/resolver.rs @@ -1,7 +1,7 @@ //! Resolves a texture in a BSP File to an image use crate::draw::texture::image::LoadableImage; -use stockton_levels::prelude::HasTextures; +use stockton_levels::{parts::IsTexture, prelude::HasTextures}; use std::{ mem::drop, @@ -37,7 +37,7 @@ impl<'a, T: HasTextures> TextureResolver for FsResolver<'a, T> { fn resolve(&mut self, tex: u32) -> Option<Self::Image> { let map = self.map_lock.read().unwrap(); let tex = map.get_texture(tex)?; - let path = self.path.join(&tex.name); + let path = self.path.join(&tex.name()); drop(tex); drop(map); diff --git a/stockton-render/src/draw/ui/render.rs b/stockton-render/src/draw/ui/render.rs index a88f811..a3e0f04 100644 --- a/stockton-render/src/draw/ui/render.rs +++ b/stockton-render/src/draw/ui/render.rs @@ -64,7 +64,7 @@ pub fn do_render( if let Some(ds) = tex_repo.attempt_get_descriptor_set(0) { unsafe { cmd_buffer.push_graphics_constants( - &pipeline_layout, + pipeline_layout, ShaderStageFlags::VERTEX, 0, &[screen.x.to_bits(), screen.y.to_bits()], diff --git a/stockton-render/src/error.rs b/stockton-render/src/error.rs index 73726b2..6c895eb 100644 --- a/stockton-render/src/error.rs +++ b/stockton-render/src/error.rs @@ -46,6 +46,13 @@ pub enum EnvironmentError { NoQueues, } +/// Indicates an issue with the level object being used +#[derive(Debug, Error)] +pub enum LevelError { + #[error("Referential Integrity broken")] + BadReference, +} + pub fn full_error_display(err: anyhow::Error) -> String { let cont = err .chain() diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs index 97cf6d3..4eb51e5 100644 --- a/stockton-render/src/lib.rs +++ b/stockton-render/src/lib.rs @@ -8,17 +8,14 @@ extern crate nalgebra_glm as na; #[macro_use] extern crate legion; -mod culling; pub mod draw; pub mod error; pub mod systems; mod types; pub mod window; -use culling::get_visible_faces; use draw::RenderingContext; use error::full_error_display; -use error::LockPoisoned; use legion::world::SubWorld; use legion::IntoQuery; use std::sync::mpsc::{Receiver, Sender}; @@ -26,7 +23,7 @@ use std::sync::Arc; use std::sync::RwLock; pub use window::{UiState, WindowEvent}; -use anyhow::{Context, Result}; +use anyhow::Result; use log::error; use stockton_levels::prelude::*; use stockton_types::components::{CameraSettings, Transform}; @@ -38,9 +35,9 @@ use std::sync::mpsc::channel; /// Renders a world to a window when you tell it to. /// Also takes ownership of the window and channels window events to be processed outside winit's event loop. -pub struct Renderer<'a, M: 'static + MinBspFeatures<VulkanSystem>> { +pub struct Renderer<M: 'static + MinRenderFeatures> { /// All the vulkan stuff - pub(crate) context: RenderingContext<'a, M>, + pub(crate) context: RenderingContext<M>, /// For getting events from the winit event loop pub window_events: Receiver<WindowEvent>, @@ -49,7 +46,7 @@ pub struct Renderer<'a, M: 'static + MinBspFeatures<VulkanSystem>> { pub update_control_flow: Arc<RwLock<ControlFlow>>, } -impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> Renderer<'a, M> { +impl<M: 'static + MinRenderFeatures> Renderer<M> { /// Create a new Renderer. pub fn new(window: &Window, ui: &mut UiState, file: M) -> Result<(Self, Sender<WindowEvent>)> { let (tx, rx) = channel(); @@ -57,7 +54,7 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> Renderer<'a, M> { Ok(( Renderer { - context: RenderingContext::new(window, ui, file)?, + context: RenderingContext::new(window, ui, file, ())?, window_events: rx, update_control_flow, }, @@ -66,24 +63,14 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> Renderer<'a, M> { } /// Render a single frame of the given map. - fn render(&mut self, ui: &mut UiState, pos: Vector3) -> Result<()> { - // Get visible faces - let faces = get_visible_faces( - pos, - &*self - .context - .map - .read() - .map_err(|_| LockPoisoned::Map) - .context("Error getting read lock on map")?, - ); - - // Then draw them - if self.context.draw_vertices(ui, &faces).is_err() { + fn render(&mut self, _ui: &mut UiState, _pos: Vector3) -> Result<()> { + // Try to draw + if self.context.draw_next_frame().is_err() { + // Probably the surface changed unsafe { self.context.handle_surface_change()? }; // If it fails twice, then error - self.context.draw_vertices(ui, &faces)?; + self.context.draw_next_frame()?; } Ok(()) @@ -98,8 +85,8 @@ impl<'a, M: 'static + MinBspFeatures<VulkanSystem>> Renderer<'a, M> { #[system] #[read_component(Transform)] #[read_component(CameraSettings)] -pub fn do_render<T: 'static + MinBspFeatures<VulkanSystem>>( - #[resource] renderer: &mut Renderer<'static, T>, +pub fn do_render<T: 'static + MinRenderFeatures>( + #[resource] renderer: &mut Renderer<T>, #[resource] ui: &mut UiState, world: &SubWorld, ) { diff --git a/stockton-render/src/window.rs b/stockton-render/src/window.rs index 2496f3c..7b63bc3 100644 --- a/stockton-render/src/window.rs +++ b/stockton-render/src/window.rs @@ -2,7 +2,7 @@ use crate::{error::full_error_display, Renderer}; use egui::{Modifiers, Rect, Vec2}; use legion::systems::Runnable; use log::debug; -use stockton_levels::prelude::{MinBspFeatures, VulkanSystem}; +use stockton_levels::prelude::MinRenderFeatures; use egui::{CtxRef, Event, Output, Pos2, RawInput}; use epaint::ClippedShape; @@ -93,10 +93,7 @@ impl UiState { } } - pub fn populate_initial_state<T: MinBspFeatures<VulkanSystem>>( - &mut self, - renderer: &Renderer<T>, - ) { + pub fn populate_initial_state<T: MinRenderFeatures>(&mut self, renderer: &Renderer<T>) { let props = &renderer.context.target_chain.properties; self.set_dimensions(props.extent.width, props.extent.height); self.set_pixels_per_point(Some(renderer.context.pixels_per_point)); @@ -112,15 +109,15 @@ impl UiState { } #[inline] - fn begin_frame(&mut self) -> () { + fn begin_frame(&mut self) { #[allow(deprecated)] let new_raw_input = RawInput { scroll_delta: Vec2::new(0.0, 0.0), zoom_delta: 0.0, screen_size: self.raw_input.screen_size, - screen_rect: self.raw_input.screen_rect.clone(), - pixels_per_point: self.raw_input.pixels_per_point.clone(), - time: self.raw_input.time.clone(), + screen_rect: self.raw_input.screen_rect, + pixels_per_point: self.raw_input.pixels_per_point, + time: self.raw_input.time, predicted_dt: self.raw_input.predicted_dt, modifiers: self.modifiers, events: Vec::new(), @@ -180,11 +177,8 @@ impl UiState { #[system] /// A system to process the window events sent to renderer by the winit event loop. -pub fn _process_window_events< - T: 'static + InputManager, - M: 'static + MinBspFeatures<VulkanSystem>, ->( - #[resource] renderer: &mut Renderer<'static, M>, +pub fn _process_window_events<T: 'static + InputManager, M: 'static + MinRenderFeatures>( + #[resource] renderer: &mut Renderer<M>, #[resource] manager: &mut T, #[resource] mouse: &mut Mouse, #[resource] ui_state: &mut UiState, @@ -243,9 +237,7 @@ pub fn _process_window_events< manager.handle_frame(&actions_buf[0..actions_buf_cursor]); } -pub fn process_window_events_system< - T: 'static + InputManager, - M: 'static + MinBspFeatures<VulkanSystem>, ->() -> impl Runnable { +pub fn process_window_events_system<T: 'static + InputManager, M: 'static + MinRenderFeatures>( +) -> impl Runnable { _process_window_events_system::<T, M>(Vec::with_capacity(4)) } |