From a82e16c92a026b2fbe3a40a21d7e690242e32ba6 Mon Sep 17 00:00:00 2001 From: tcmal Date: Sun, 25 Aug 2024 17:44:23 +0100 Subject: WIP refactor(render): add draw passes --- stockton-render/src/culling.rs | 2 +- stockton-render/src/draw/camera.rs | 6 +- stockton-render/src/draw/context.rs | 307 ++++------------ stockton-render/src/draw/draw_buffers.rs | 4 +- stockton-render/src/draw/draw_passes/cons.rs | 66 ++++ stockton-render/src/draw/draw_passes/level.rs | 483 ++++++++++++++++++++++++++ stockton-render/src/draw/draw_passes/mod.rs | 43 +++ stockton-render/src/draw/mod.rs | 3 +- stockton-render/src/draw/pipeline.rs | 299 ---------------- stockton-render/src/draw/queue_negotiator.rs | 85 +++-- stockton-render/src/draw/render.rs | 121 ------- stockton-render/src/draw/target.rs | 254 +------------- stockton-render/src/draw/texture/load.rs | 2 +- stockton-render/src/draw/texture/loader.rs | 14 +- stockton-render/src/draw/texture/repo.rs | 8 +- stockton-render/src/draw/texture/resolver.rs | 4 +- stockton-render/src/draw/ui/render.rs | 2 +- stockton-render/src/error.rs | 7 + stockton-render/src/lib.rs | 37 +- stockton-render/src/window.rs | 28 +- 20 files changed, 760 insertions(+), 1015 deletions(-) create mode 100644 stockton-render/src/draw/draw_passes/cons.rs create mode 100644 stockton-render/src/draw/draw_passes/level.rs create mode 100644 stockton-render/src/draw/draw_passes/mod.rs delete mode 100644 stockton-render/src/draw/pipeline.rs delete mode 100644 stockton-render/src/draw/render.rs 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::() | maybe_changed::())] -pub fn calc_vp_matrix>( +pub fn calc_vp_matrix( transform: &Transform, settings: &CameraSettings, - #[resource] renderer: &mut Renderer<'static, M>, + #[resource] renderer: &mut Renderer, ) { 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> { +pub struct RenderingContext> { pub map: Arc>, // Parents for most of these things @@ -54,12 +44,6 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures> { /// Swapchain and stuff pub(crate) target_chain: ManuallyDrop, - /// Graphics pipeline and associated objects - pipeline: ManuallyDrop, - - /// 2D Graphics pipeline and associated objects - ui_pipeline: ManuallyDrop, - // Command pool and buffers /// The command pool used for our buffers cmd_pool: ManuallyDrop, @@ -67,17 +51,8 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures> { /// The queue to use for drawing queue: Arc>, - /// Main Texture repo - tex_repo: ManuallyDrop>, - - /// UI Texture repo - ui_tex_repo: ManuallyDrop>, - - /// Buffers used for drawing - draw_buffers: ManuallyDrop>, - - /// Buffers used for drawing the UI - ui_draw_buffers: ManuallyDrop>, + /// Deals with drawing logic, and holds any data required for drawing. + draw_pass: ManuallyDrop, /// View projection matrix pub(crate) vp_matrix: Mat4, @@ -85,9 +60,18 @@ pub struct RenderingContext<'a, M: 'static + MinBspFeatures> { pub(crate) pixels_per_point: f32, } -impl<'a, M: 'static + MinBspFeatures> RenderingContext<'a, M> { +impl RenderingContext +where + DP: DrawPass, +{ + // TODO: Arbitrary drawpass input /// Create a new RenderingContext for the given window. - pub fn new(window: &Window, ui: &mut UiState, map: M) -> Result { + pub fn new>( + window: &Window, + _ui: &mut UiState, + map: M, + idp: ILDP, + ) -> Result { let map = Arc::new(RwLock::new(map)); // Create surface let (instance, surface, mut adapters) = unsafe { @@ -104,46 +88,65 @@ impl<'a, M: 'static + MinBspFeatures> 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::(&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::(&adapter.queue_families, 1) - .ok_or(EnvironmentError::NoSuitableFamilies)?; - let (tf, tqs) = queue_negotiator - .family_spec::(&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> 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::() - .ok_or(EnvironmentError::NoQueues)?, - queue_negotiator - .get_queue::(&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::() - .ok_or(EnvironmentError::NoQueues)?, - queue_negotiator - .get_queue::(&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> 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> 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> 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> 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> core::ops::Drop for RenderingContext<'a, M> { +impl core::ops::Drop for RenderingContext { 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> 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> { - 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: A, + b: B, +} + +impl DrawPass for ConsDrawPass { + type Input = ConsDrawPassInput; + + 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)>> { + 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 { + pub a: A, + pub b: B, +} + +impl DrawPassInput for ConsDrawPassInput {} + +/// 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)>> { + Ok(vec![]) + } +} + +/// Input for a NilDrawPass. +struct NilDrawPassInput; + +impl DrawPassInput for NilDrawPassInput {} diff --git a/stockton-render/src/draw/draw_passes/level.rs b/stockton-render/src/draw/draw_passes/level.rs new file mode 100644 index 0000000..afcb703 --- /dev/null +++ b/stockton-render/src/draw/draw_passes/level.rs @@ -0,0 +1,483 @@ +//! Minimal code for drawing any level, based on traits from stockton-levels + +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 anyhow::{Context, Result}; + +/// 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 { + pipeline: CompletePipeline, + repo: TextureRepo, + _d: PhantomData, +} + +/// Any map can be used as draw pass input. +/// TODO: Restrict this based on the type of the renderer. +impl DrawPassInput for T {} + +impl DrawPass for LevelDrawPass { + 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 = { + // 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)>> { + todo!() + // queue_negotiator.find(TexLoadQueue) + } +} + +impl IntoDrawPass> for () { + fn init( + self, + _device_lock: Arc>, + _queue_negotiator: &mut QueueNegotiator, + _swapchain_properties: &SwapchainProperties, + ) -> Result> { + 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 + pub(crate) renderpass: ManuallyDrop, + + /// The layout of our main graphics pipeline + pub(crate) pipeline_layout: ManuallyDrop, + + /// Our main graphics pipeline + pub(crate) pipeline: ManuallyDrop, + + /// The vertex shader module + pub(crate) vs_module: ManuallyDrop, + + /// The fragment shader module + pub(crate) fs_module: ManuallyDrop, +} + +impl CompletePipeline { + pub fn new<'a, T: Iterator + std::fmt::Debug>( + device: &mut DeviceT, + extent: hal::image::Extent, + swapchain_properties: &SwapchainProperties, + set_layouts: T, + ) -> Result { + use hal::format::Format; + use hal::pso::*; + + // Renderpass + let renderpass = { + use hal::{image::Layout, pass::*}; + + let img_attachment = Attachment { + format: Some(swapchain_properties.format), + samples: 1, + ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store), + stencil_ops: AttachmentOps::new( + AttachmentLoadOp::Clear, + AttachmentStoreOp::DontCare, + ), + layouts: Layout::Undefined..Layout::ColorAttachmentOptimal, + }; + + let depth_attachment = Attachment { + format: Some(swapchain_properties.depth_format), + samples: 1, + ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare), + stencil_ops: AttachmentOps::new( + AttachmentLoadOp::DontCare, + AttachmentStoreOp::DontCare, + ), + layouts: Layout::Undefined..Layout::DepthStencilAttachmentOptimal, + }; + + let subpass = SubpassDesc { + colors: &[(0, Layout::ColorAttachmentOptimal)], + depth_stencil: Some(&(1, Layout::DepthStencilAttachmentOptimal)), + inputs: &[], + resolves: &[], + preserves: &[], + }; + + unsafe { + device.create_render_pass( + IntoIter::new([img_attachment, depth_attachment]), + once(subpass), + empty(), + ) + } + .context("Error creating render pass")? + }; + + // Subpass + let subpass = hal::pass::Subpass { + index: 0, + main_pass: &renderpass, + }; + + // Shader modules + let (vs_module, fs_module) = { + let mut compiler = shaderc::Compiler::new().ok_or(EnvironmentError::NoShaderC)?; + + let vertex_compile_artifact = compiler + .compile_into_spirv( + VERTEX_SOURCE, + shaderc::ShaderKind::Vertex, + "vertex.vert", + ENTRY_NAME, + None, + ) + .context("Error compiling vertex shader")?; + + let fragment_compile_artifact = compiler + .compile_into_spirv( + FRAGMENT_SOURCE, + shaderc::ShaderKind::Fragment, + "fragment.frag", + ENTRY_NAME, + None, + ) + .context("Error compiling fragment shader")?; + + // Make into shader module + unsafe { + ( + device + .create_shader_module(vertex_compile_artifact.as_binary()) + .context("Error creating vertex shader module")?, + device + .create_shader_module(fragment_compile_artifact.as_binary()) + .context("Error creating fragment shader module")?, + ) + } + }; + + // Shader entry points (ShaderStage) + let (vs_entry, fs_entry) = ( + EntryPoint:: { + entry: ENTRY_NAME, + module: &vs_module, + specialization: Specialization::default(), + }, + EntryPoint:: { + entry: ENTRY_NAME, + module: &fs_module, + specialization: Specialization::default(), + }, + ); + + // Rasterizer + let rasterizer = Rasterizer { + polygon_mode: PolygonMode::Fill, + cull_face: Face::BACK, + front_face: FrontFace::CounterClockwise, + depth_clamping: false, + depth_bias: None, + conservative: true, + line_width: State::Static(1.0), + }; + + // Depth stencil + let depth_stencil = DepthStencilDesc { + depth: Some(DepthTest { + fun: Comparison::Less, + write: true, + }), + depth_bounds: false, + stencil: None, + }; + + // Pipeline layout + let layout = unsafe { + device.create_pipeline_layout( + set_layouts.into_iter(), + // vp matrix, 4x4 f32 + IntoIter::new([(ShaderStageFlags::VERTEX, 0..64)]), + ) + } + .context("Error creating pipeline layout")?; + + // Colour blending + let blender = { + let blend_state = BlendState { + color: BlendOp::Add { + src: Factor::One, + dst: Factor::Zero, + }, + alpha: BlendOp::Add { + src: Factor::One, + dst: Factor::Zero, + }, + }; + + BlendDesc { + logic_op: Some(LogicOp::Copy), + targets: vec![ColorBlendDesc { + mask: ColorMask::ALL, + blend: Some(blend_state), + }], + } + }; + + // Baked states + let baked_states = BakedStates { + viewport: Some(Viewport { + rect: extent.rect(), + depth: (0.0..1.0), + }), + scissor: Some(extent.rect()), + blend_constants: None, + depth_bounds: None, + }; + + // Primitive assembler + let primitive_assembler = PrimitiveAssemblerDesc::Vertex { + buffers: &[VertexBufferDesc { + binding: 0, + stride: (size_of::() * 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::() * 3) as u32, + }, + }, + AttributeDesc { + location: 2, + binding: 0, + element: Element { + format: Format::Rg32Sfloat, + offset: (size_of::() * 4) as u32, + }, + }, + ], + input_assembler: InputAssemblerDesc::new(Primitive::TriangleList), + vertex: vs_entry, + tessellation: None, + geometry: None, + }; + + // Pipeline description + let pipeline_desc = GraphicsPipelineDesc { + label: Some("3D"), + rasterizer, + fragment: Some(fs_entry), + blender, + depth_stencil, + multisampling: None, + baked_states, + layout: &layout, + subpass, + flags: PipelineCreationFlags::empty(), + parent: BasePipeline::None, + primitive_assembler, + }; + + // Pipeline + let pipeline = unsafe { device.create_graphics_pipeline(&pipeline_desc, None) } + .context("Error creating graphics pipeline")?; + + Ok(CompletePipeline { + renderpass: ManuallyDrop::new(renderpass), + pipeline_layout: ManuallyDrop::new(layout), + pipeline: ManuallyDrop::new(pipeline), + vs_module: ManuallyDrop::new(vs_module), + fs_module: ManuallyDrop::new(fs_module), + }) + } + + /// Deactivate vulkan resources. Use before dropping + pub fn deactivate(self, device: &mut DeviceT) { + unsafe { + use core::ptr::read; + + device.destroy_render_pass(ManuallyDrop::into_inner(read(&self.renderpass))); + + device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.vs_module))); + device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module))); + + device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); + + device.destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); + } + } +} + +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)>>; +} + +/// 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 { + fn init( + self, + device: Arc>, + queue_negotiator: &mut QueueNegotiator, + swapchain_properties: &SwapchainProperties, + ) -> Result; +} 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/pipeline.rs b/stockton-render/src/draw/pipeline.rs deleted file mode 100644 index 84b541f..0000000 --- a/stockton-render/src/draw/pipeline.rs +++ /dev/null @@ -1,299 +0,0 @@ -//! A complete graphics pipeline - -/// 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 std::{ - array::IntoIter, - iter::{empty, once}, - mem::{size_of, ManuallyDrop}, -}; - -use super::target::SwapchainProperties; -use crate::{error::EnvironmentError, types::*}; -use anyhow::{Context, Result}; - -// TODO: Generalise so we can use for UI also -/// A complete graphics pipeline and associated resources -pub struct CompletePipeline { - /// Our main render pass - pub(crate) renderpass: ManuallyDrop, - - /// The layout of our main graphics pipeline - pub(crate) pipeline_layout: ManuallyDrop, - - /// Our main graphics pipeline - pub(crate) pipeline: ManuallyDrop, - - /// The vertex shader module - pub(crate) vs_module: ManuallyDrop, - - /// The fragment shader module - pub(crate) fs_module: ManuallyDrop, -} - -impl CompletePipeline { - pub fn new<'a, T: Iterator + std::fmt::Debug>( - device: &mut DeviceT, - extent: hal::image::Extent, - swapchain_properties: &SwapchainProperties, - set_layouts: T, - ) -> Result { - use hal::format::Format; - use hal::pso::*; - - // Renderpass - let renderpass = { - use hal::{image::Layout, pass::*}; - - let img_attachment = Attachment { - format: Some(swapchain_properties.format), - samples: 1, - ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store), - stencil_ops: AttachmentOps::new( - AttachmentLoadOp::Clear, - AttachmentStoreOp::DontCare, - ), - layouts: Layout::Undefined..Layout::ColorAttachmentOptimal, - }; - - let depth_attachment = Attachment { - format: Some(swapchain_properties.depth_format), - samples: 1, - ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare), - stencil_ops: AttachmentOps::new( - AttachmentLoadOp::DontCare, - AttachmentStoreOp::DontCare, - ), - layouts: Layout::Undefined..Layout::DepthStencilAttachmentOptimal, - }; - - let subpass = SubpassDesc { - colors: &[(0, Layout::ColorAttachmentOptimal)], - depth_stencil: Some(&(1, Layout::DepthStencilAttachmentOptimal)), - inputs: &[], - resolves: &[], - preserves: &[], - }; - - unsafe { - device.create_render_pass( - IntoIter::new([img_attachment, depth_attachment]), - once(subpass), - empty(), - ) - } - .context("Error creating render pass")? - }; - - // Subpass - let subpass = hal::pass::Subpass { - index: 0, - main_pass: &renderpass, - }; - - // Shader modules - let (vs_module, fs_module) = { - let mut compiler = shaderc::Compiler::new().ok_or(EnvironmentError::NoShaderC)?; - - let vertex_compile_artifact = compiler - .compile_into_spirv( - VERTEX_SOURCE, - shaderc::ShaderKind::Vertex, - "vertex.vert", - ENTRY_NAME, - None, - ) - .context("Error compiling vertex shader")?; - - let fragment_compile_artifact = compiler - .compile_into_spirv( - FRAGMENT_SOURCE, - shaderc::ShaderKind::Fragment, - "fragment.frag", - ENTRY_NAME, - None, - ) - .context("Error compiling fragment shader")?; - - // Make into shader module - unsafe { - ( - device - .create_shader_module(vertex_compile_artifact.as_binary()) - .context("Error creating vertex shader module")?, - device - .create_shader_module(fragment_compile_artifact.as_binary()) - .context("Error creating fragment shader module")?, - ) - } - }; - - // Shader entry points (ShaderStage) - let (vs_entry, fs_entry) = ( - EntryPoint:: { - entry: ENTRY_NAME, - module: &vs_module, - specialization: Specialization::default(), - }, - EntryPoint:: { - entry: ENTRY_NAME, - module: &fs_module, - specialization: Specialization::default(), - }, - ); - - // Rasterizer - let rasterizer = Rasterizer { - polygon_mode: PolygonMode::Fill, - cull_face: Face::BACK, - front_face: FrontFace::CounterClockwise, - depth_clamping: false, - depth_bias: None, - conservative: true, - line_width: State::Static(1.0), - }; - - // Depth stencil - let depth_stencil = DepthStencilDesc { - depth: Some(DepthTest { - fun: Comparison::Less, - write: true, - }), - depth_bounds: false, - stencil: None, - }; - - // Pipeline layout - let layout = unsafe { - device.create_pipeline_layout( - set_layouts.into_iter(), - // vp matrix, 4x4 f32 - IntoIter::new([(ShaderStageFlags::VERTEX, 0..64)]), - ) - } - .context("Error creating pipeline layout")?; - - // Colour blending - let blender = { - let blend_state = BlendState { - color: BlendOp::Add { - src: Factor::One, - dst: Factor::Zero, - }, - alpha: BlendOp::Add { - src: Factor::One, - dst: Factor::Zero, - }, - }; - - BlendDesc { - logic_op: Some(LogicOp::Copy), - targets: vec![ColorBlendDesc { - mask: ColorMask::ALL, - blend: Some(blend_state), - }], - } - }; - - // Baked states - let baked_states = BakedStates { - viewport: Some(Viewport { - rect: extent.rect(), - depth: (0.0..1.0), - }), - scissor: Some(extent.rect()), - blend_constants: None, - depth_bounds: None, - }; - - // Primitive assembler - let primitive_assembler = PrimitiveAssemblerDesc::Vertex { - buffers: &[VertexBufferDesc { - binding: 0, - stride: (size_of::() * 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::() * 3) as u32, - }, - }, - AttributeDesc { - location: 2, - binding: 0, - element: Element { - format: Format::Rg32Sfloat, - offset: (size_of::() * 4) as u32, - }, - }, - ], - input_assembler: InputAssemblerDesc::new(Primitive::TriangleList), - vertex: vs_entry, - tessellation: None, - geometry: None, - }; - - // Pipeline description - let pipeline_desc = GraphicsPipelineDesc { - label: Some("3D"), - rasterizer, - fragment: Some(fs_entry), - blender, - depth_stencil, - multisampling: None, - baked_states, - layout: &layout, - subpass, - flags: PipelineCreationFlags::empty(), - parent: BasePipeline::None, - primitive_assembler, - }; - - // Pipeline - let pipeline = unsafe { device.create_graphics_pipeline(&pipeline_desc, None) } - .context("Error creating graphics pipeline")?; - - Ok(CompletePipeline { - renderpass: ManuallyDrop::new(renderpass), - pipeline_layout: ManuallyDrop::new(layout), - pipeline: ManuallyDrop::new(pipeline), - vs_module: ManuallyDrop::new(vs_module), - fs_module: ManuallyDrop::new(fs_module), - }) - } - - /// Deactivate vulkan resources. Use before dropping - pub fn deactivate(self, device: &mut DeviceT) { - unsafe { - use core::ptr::read; - - device.destroy_render_pass(ManuallyDrop::into_inner(read(&self.renderpass))); - - device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.vs_module))); - device.destroy_shader_module(ManuallyDrop::into_inner(read(&self.fs_module))); - - device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); - - device.destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); - } - } -} 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, already_allocated: HashMap>>, 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::() +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::() - } -} + pub fn find(&mut self, adapter: &Adapter, filter: &T) -> Result<()> { + if self.family_ids.contains_key(&TypeId::of::()) { + return Ok(()); + } -impl QueueNegotiator { - pub fn find(adapter: &Adapter, stacks: &[&dyn QueueFamilySelector]) -> Result { - 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::(), family.id()); + + Ok(()) } pub fn get_queue( &mut self, groups: &mut Vec, ) -> Option>> { - let tid = T::get_type_id(); + let tid = TypeId::of::(); 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(&mut self, queue: Arc>) { - let tid = T::get_type_id(); + let tid = TypeId::of::(); 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>( - cmd_buffer: &mut CommandBufferT, - draw_buffers: &mut DrawBuffers, - 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 { @@ -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 = Vec::with_capacity(swap_config.image_count as usize); let mut sync_objects: Vec = @@ -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, - pipeline: &CompletePipeline, - vp: &Mat4, - ) -> Result<( - &'a mut crate::types::CommandBufferT, - >::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: &::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, - img: &>::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: &::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: >::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, - - /// Framebuffer for this frame - pub framebuffer: ManuallyDrop, - - /// Framebuffer for this frame when drawing in 2D - pub framebuffer_2d: ManuallyDrop, } 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 { // 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 TextureLoader { 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 TextureLoader { 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 TextureLoader { staging_memory_type, obcpa, img_data, - &config, + config, )?; buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); @@ -590,8 +590,8 @@ impl TextureLoader { 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 TextureLoader { 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>>, ds_layout: Arc>, req_send: Sender, resp_recv: Receiver>, blocks: HashMap>>, - - _a: PhantomData<&'a ()>, } -impl<'a> TextureRepo<'a> { +impl TextureRepo { pub fn new( device_lock: Arc>, 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 { 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> { +pub struct Renderer { /// All the vulkan stuff - pub(crate) context: RenderingContext<'a, M>, + pub(crate) context: RenderingContext, /// For getting events from the winit event loop pub window_events: Receiver, @@ -49,7 +46,7 @@ pub struct Renderer<'a, M: 'static + MinBspFeatures> { pub update_control_flow: Arc>, } -impl<'a, M: 'static + MinBspFeatures> Renderer<'a, M> { +impl Renderer { /// Create a new Renderer. pub fn new(window: &Window, ui: &mut UiState, file: M) -> Result<(Self, Sender)> { let (tx, rx) = channel(); @@ -57,7 +54,7 @@ impl<'a, M: 'static + MinBspFeatures> 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> 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> Renderer<'a, M> { #[system] #[read_component(Transform)] #[read_component(CameraSettings)] -pub fn do_render>( - #[resource] renderer: &mut Renderer<'static, T>, +pub fn do_render( + #[resource] renderer: &mut Renderer, #[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>( - &mut self, - renderer: &Renderer, - ) { + pub fn populate_initial_state(&mut self, renderer: &Renderer) { 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, ->( - #[resource] renderer: &mut Renderer<'static, M>, +pub fn _process_window_events( + #[resource] renderer: &mut Renderer, #[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, ->() -> impl Runnable { +pub fn process_window_events_system( +) -> impl Runnable { _process_window_events_system::(Vec::with_capacity(4)) } -- cgit v1.2.3