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 | 47a0c0317cc774c19b78582bec9b5b09d56f569a (patch) | |
tree | d03471ea4e084ace9b95a2c5b7febb780b45bb63 /stockton-render/src | |
parent | fb996488aa651cb2e7f46abc083c4318b47e77cd (diff) |
feat(render): draw passes
Diffstat (limited to 'stockton-render/src')
-rw-r--r-- | stockton-render/src/draw/buffers/mod.rs | 15 | ||||
-rw-r--r-- | stockton-render/src/draw/buffers/staged.rs | 76 | ||||
-rw-r--r-- | stockton-render/src/draw/builders/mod.rs | 3 | ||||
-rw-r--r-- | stockton-render/src/draw/builders/pipeline.rs | 276 | ||||
-rw-r--r-- | stockton-render/src/draw/builders/renderpass.rs | 75 | ||||
-rw-r--r-- | stockton-render/src/draw/builders/shader.rs | 35 | ||||
-rw-r--r-- | stockton-render/src/draw/camera.rs | 76 | ||||
-rw-r--r-- | stockton-render/src/draw/context.rs | 41 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/cons.rs | 45 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/level.rs | 812 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/mod.rs | 27 | ||||
-rw-r--r-- | stockton-render/src/draw/mod.rs | 3 | ||||
-rw-r--r-- | stockton-render/src/draw/queue_negotiator.rs | 19 | ||||
-rw-r--r-- | stockton-render/src/draw/target.rs | 85 | ||||
-rw-r--r-- | stockton-render/src/draw/texture/resolver.rs | 6 | ||||
-rw-r--r-- | stockton-render/src/lib.rs | 27 | ||||
-rw-r--r-- | stockton-render/src/systems.rs | 1 | ||||
-rw-r--r-- | stockton-render/src/types.rs | 1 | ||||
-rw-r--r-- | stockton-render/src/window.rs | 6 |
19 files changed, 1003 insertions, 626 deletions
diff --git a/stockton-render/src/draw/buffers/mod.rs b/stockton-render/src/draw/buffers/mod.rs index 5093872..dc7df65 100644 --- a/stockton-render/src/draw/buffers/mod.rs +++ b/stockton-render/src/draw/buffers/mod.rs @@ -11,9 +11,9 @@ use hal::{ MemoryTypeId, }; -mod dedicated_image; -mod draw_buffers; -mod staged; +pub mod dedicated_image; +pub mod draw_buffers; +pub mod staged; pub use dedicated_image::DedicatedLoadedImage; pub use draw_buffers::DrawBuffers; @@ -58,11 +58,6 @@ pub trait ModifiableBuffer: IndexMut<usize> { /// Get a handle to the underlying GPU buffer fn get_buffer(&mut self) -> &BufferT; - /// Commit all changes to GPU memory, returning a handle to the GPU buffer - fn commit<'a>( - &'a mut self, - device: &DeviceT, - command_queue: &mut QueueT, - command_pool: &mut CommandPoolT, - ) -> Result<&'a BufferT>; + /// Record the command(s) required to commit changes to this buffer to the given command buffer. + fn record_commit_cmds<'a>(&'a mut self, cmd_buffer: &mut CommandBufferT) -> Result<()>; } diff --git a/stockton-render/src/draw/buffers/staged.rs b/stockton-render/src/draw/buffers/staged.rs index f92c41d..71b5204 100644 --- a/stockton-render/src/draw/buffers/staged.rs +++ b/stockton-render/src/draw/buffers/staged.rs @@ -1,20 +1,19 @@ //! A buffer that can be written to by the CPU using staging memory use super::{create_buffer, ModifiableBuffer}; -use crate::{error::EnvironmentError, types::*}; +use crate::types::*; use core::mem::{size_of, ManuallyDrop}; use std::{ convert::TryInto, - iter::{empty, once}, ops::{Index, IndexMut}, }; use anyhow::{Context, Result}; use hal::{ buffer::Usage, - memory::{Properties, Segment, SparseFlags}, - MemoryTypeId, + command::BufferCopy, + memory::{Properties, Segment}, }; /// A GPU buffer that is written to using a staging buffer @@ -34,9 +33,6 @@ pub struct StagedBuffer<'a, T: Sized> { /// Where staged buffer is mapped in CPU memory staged_mapped_memory: &'a mut [T], - /// If staged memory has been changed since last `commit` - staged_is_dirty: bool, - /// The highest index in the buffer that's been written to. pub highest_used: usize, } @@ -88,7 +84,6 @@ impl<'a, T: Sized> StagedBuffer<'a, T> { buffer: ManuallyDrop::new(buffer), memory: ManuallyDrop::new(memory), staged_mapped_memory, - staged_is_dirty: false, highest_used: 0, }) } @@ -112,60 +107,20 @@ impl<'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> { &self.buffer } - fn commit<'b>( - &'b mut self, - device: &DeviceT, - command_queue: &mut QueueT, - command_pool: &mut CommandPoolT, - ) -> Result<&'b BufferT> { - // Only commit if there's changes to commit. - if self.staged_is_dirty { - // Copy from staged to buffer - let buf = unsafe { - use hal::command::{BufferCopy, CommandBufferFlags}; - // Get a command buffer - let mut buf = command_pool.allocate_one(hal::command::Level::Primary); - - // Put in our copy command - buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); - buf.copy_buffer( - &self.staged_buffer, - &self.buffer, - std::iter::once(BufferCopy { - src: 0, - dst: 0, - size: ((self.highest_used + 1) * size_of::<T>()) as u64, - }), - ); - buf.finish(); - - buf - }; - - // Submit it and wait for completion - // TODO: Proper management of transfer operations - unsafe { - let mut copy_finished = device.create_fence(false)?; - command_queue.submit( - once(&buf), - empty::<(&SemaphoreT, hal::pso::PipelineStage)>(), - empty::<&SemaphoreT>(), - Some(&mut copy_finished), - ); - - device - .wait_for_fence(©_finished, core::u64::MAX) - .context("Error waiting for fence")?; - - // Destroy temporary resources - device.destroy_fence(copy_finished); - command_pool.free(once(buf)); - } - - self.staged_is_dirty = false; + fn record_commit_cmds(&mut self, buf: &mut CommandBufferT) -> Result<()> { + unsafe { + buf.copy_buffer( + &self.staged_buffer, + &self.buffer, + std::iter::once(BufferCopy { + src: 0, + dst: 0, + size: ((self.highest_used + 1) * size_of::<T>()) as u64, + }), + ); } - Ok(&self.buffer) + Ok(()) } } @@ -179,7 +134,6 @@ impl<'a, T: Sized> Index<usize> for StagedBuffer<'a, T> { impl<'a, T: Sized> IndexMut<usize> for StagedBuffer<'a, T> { fn index_mut(&mut self, index: usize) -> &mut Self::Output { - self.staged_is_dirty = true; if index > self.highest_used { self.highest_used = index; } diff --git a/stockton-render/src/draw/builders/mod.rs b/stockton-render/src/draw/builders/mod.rs new file mode 100644 index 0000000..002c09f --- /dev/null +++ b/stockton-render/src/draw/builders/mod.rs @@ -0,0 +1,3 @@ +pub mod pipeline; +pub mod renderpass; +pub mod shader; diff --git a/stockton-render/src/draw/builders/pipeline.rs b/stockton-render/src/draw/builders/pipeline.rs new file mode 100644 index 0000000..cdd9736 --- /dev/null +++ b/stockton-render/src/draw/builders/pipeline.rs @@ -0,0 +1,276 @@ +use super::{renderpass::RenderpassSpec, shader::ShaderDesc}; +use crate::{draw::target::SwapchainProperties, error::EnvironmentError, types::*}; + +use std::{mem::ManuallyDrop, ops::Range}; + +use anyhow::{Context, Result}; +use hal::{ + format::Format, + pso::{ + AttributeDesc, BakedStates, BasePipeline, BlendDesc, BufferIndex, DepthStencilDesc, + ElemStride, Element, GraphicsPipelineDesc, InputAssemblerDesc, PipelineCreationFlags, + PrimitiveAssemblerDesc, Rasterizer, Rect, ShaderStageFlags, VertexBufferDesc, + VertexInputRate, Viewport, + }, +}; +use shaderc::Compiler; + +pub struct VertexBufferSpec { + pub attributes: Vec<Format>, + pub rate: VertexInputRate, +} + +impl VertexBufferSpec { + pub fn as_attribute_desc(&self, binding: BufferIndex) -> Vec<AttributeDesc> { + let mut v = Vec::with_capacity(self.attributes.len()); + let mut offset = 0; + for (idx, format) in self.attributes.iter().enumerate() { + v.push(AttributeDesc { + location: idx as u32, + binding, + element: Element { + offset, + format: *format, + }, + }); + offset += get_size(*format); + } + + v + } + pub fn stride(&self) -> ElemStride { + self.attributes.iter().fold(0, |x, f| x + get_size(*f)) + } +} + +fn get_size(f: Format) -> u32 { + match f { + Format::Rgb32Sfloat => 4 * 3, + Format::R32Sint => 4, + Format::Rg32Sfloat => 4 * 2, + _ => unimplemented!("dont know size of format {:?}", f), + } +} + +#[derive(Debug, Clone)] +pub struct VertexPrimitiveAssemblerSpec { + buffers: Vec<VertexBufferDesc>, + attributes: Vec<AttributeDesc>, + input_assembler: InputAssemblerDesc, +} + +impl VertexPrimitiveAssemblerSpec { + pub fn with_buffer(&mut self, bd: VertexBufferSpec) -> &mut Self { + let idx = self.buffers.len() as u32; + self.buffers.push(VertexBufferDesc { + binding: idx, + stride: bd.stride(), + rate: bd.rate, + }); + + self.attributes.extend(bd.as_attribute_desc(idx)); + + self + } + + pub fn with_buffers(iad: InputAssemblerDesc, mut bds: Vec<VertexBufferSpec>) -> Self { + let mut this = VertexPrimitiveAssemblerSpec { + buffers: vec![], + attributes: vec![], + input_assembler: iad, + }; + + for bd in bds.drain(..) { + this.with_buffer(bd); + } + + this + } +} + +#[derive(Builder, Debug)] +#[builder(public)] +pub struct PipelineSpec { + rasterizer: Rasterizer, + depth_stencil: DepthStencilDesc, + blender: BlendDesc, + primitive_assembler: VertexPrimitiveAssemblerSpec, + + shader_vertex: ShaderDesc, + #[builder(setter(strip_option))] + shader_fragment: Option<ShaderDesc>, + #[builder(setter(strip_option), default)] + shader_geom: Option<ShaderDesc>, + #[builder(setter(strip_option), default)] + shader_tesselation: Option<(ShaderDesc, ShaderDesc)>, + + push_constants: Vec<(ShaderStageFlags, Range<u32>)>, + + renderpass: RenderpassSpec, +} + +impl PipelineSpec { + pub fn build<'b, T: Iterator<Item = &'b DescriptorSetLayoutT> + std::fmt::Debug>( + self, + device: &mut DeviceT, + extent: hal::image::Extent, + _swapchain_properties: &SwapchainProperties, + set_layouts: T, + ) -> Result<CompletePipeline> { + // Renderpass + let renderpass = self.renderpass.build_renderpass(device)?; + + // Subpass + let subpass = hal::pass::Subpass { + index: 0, + main_pass: &renderpass, + }; + + let mut compiler = Compiler::new().ok_or(EnvironmentError::NoShaderC)?; + let (vs_module, fs_module, gm_module, ts_module) = { + ( + self.shader_vertex.compile(&mut compiler, device)?, + self.shader_fragment + .as_ref() + .map(|x| x.compile(&mut compiler, device)) + .transpose()?, + self.shader_geom + .as_ref() + .map(|x| x.compile(&mut compiler, device)) + .transpose()?, + self.shader_tesselation + .as_ref() + .map::<Result<_>, _>(|(a, b)| { + Ok(( + a.compile(&mut compiler, device)?, + b.compile(&mut compiler, device)?, + )) + }) + .transpose()?, + ) + }; + + // Safety: *_module is always populated when shader_* is, so this is safe + let (vs_entry, fs_entry, gm_entry, ts_entry) = ( + self.shader_vertex.as_entry(&vs_module), + self.shader_fragment + .as_ref() + .map(|x| x.as_entry(fs_module.as_ref().unwrap())), + self.shader_geom + .as_ref() + .map(|x| x.as_entry(gm_module.as_ref().unwrap())), + self.shader_tesselation.as_ref().map(|(a, b)| { + ( + a.as_entry(&ts_module.as_ref().unwrap().0), + b.as_entry(&ts_module.as_ref().unwrap().1), + ) + }), + ); + + // Pipeline layout + let layout = unsafe { + device.create_pipeline_layout(set_layouts.into_iter(), self.push_constants.into_iter()) + } + .context("Error creating pipeline layout")?; + + // 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: self.primitive_assembler.buffers.as_slice(), + attributes: self.primitive_assembler.attributes.as_slice(), + input_assembler: self.primitive_assembler.input_assembler, + vertex: vs_entry, + tessellation: ts_entry, + geometry: gm_entry, + }; + + // Pipeline description + let pipeline_desc = GraphicsPipelineDesc { + label: Some("stockton"), + rasterizer: self.rasterizer, + fragment: fs_entry, + blender: self.blender, + depth_stencil: self.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, + gm_module, + ts_module, + render_area: extent.rect(), + }) + } +} + +pub struct CompletePipeline { + /// Our main render pass + pub(crate) renderpass: ManuallyDrop<RenderPassT>, + + /// The layout of our main graphics pipeline + pub(crate) pipeline_layout: ManuallyDrop<PipelineLayoutT>, + + /// Our main graphics pipeline + pub(crate) pipeline: ManuallyDrop<GraphicsPipelineT>, + + /// The vertex shader module + pub(crate) vs_module: ManuallyDrop<ShaderModuleT>, + + /// The fragment shader module + pub(crate) fs_module: Option<ShaderModuleT>, + pub(crate) gm_module: Option<ShaderModuleT>, + pub(crate) ts_module: Option<(ShaderModuleT, ShaderModuleT)>, + + pub(crate) render_area: Rect, +} + +impl CompletePipeline { + /// Deactivate vulkan resources. Use before dropping + pub fn deactivate(mut 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))); + if let Some(x) = self.fs_module.take() { + device.destroy_shader_module(x) + } + if let Some(x) = self.gm_module.take() { + device.destroy_shader_module(x) + } + self.ts_module.take().map(|(a, b)| { + device.destroy_shader_module(a); + device.destroy_shader_module(b); + }); + + 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/builders/renderpass.rs b/stockton-render/src/draw/builders/renderpass.rs new file mode 100644 index 0000000..43f0eb2 --- /dev/null +++ b/stockton-render/src/draw/builders/renderpass.rs @@ -0,0 +1,75 @@ +use crate::types::*; + +use std::iter::{empty, once}; + +use anyhow::Result; +use hal::pass::{Attachment, AttachmentRef, SubpassDesc}; + +#[derive(Debug, Clone)] +pub struct RenderpassSpec { + pub colors: Vec<Attachment>, + pub depth: Option<Attachment>, + pub inputs: Vec<Attachment>, + pub resolves: Vec<Attachment>, + pub preserves: Vec<Attachment>, +} + +impl RenderpassSpec { + pub fn build_renderpass(self, device: &mut DeviceT) -> Result<RenderPassT> { + let mut next_offset = 0; + + let colors: Vec<AttachmentRef> = self + .colors + .iter() + .enumerate() + .map(|(i, a)| (next_offset + i, a.layouts.end)) + .collect(); + next_offset = colors.len(); + + let depth_stencil = self.depth.as_ref().map(|x| (next_offset, x.layouts.end)); + if depth_stencil.is_some() { + next_offset += 1; + } + + let inputs: Vec<AttachmentRef> = self + .inputs + .iter() + .enumerate() + .map(|(i, a)| (next_offset + i, a.layouts.end)) + .collect(); + next_offset += inputs.len(); + + let resolves: Vec<AttachmentRef> = self + .resolves + .iter() + .enumerate() + .map(|(i, a)| (next_offset + i, a.layouts.end)) + .collect(); + next_offset += resolves.len(); + + let preserves: Vec<usize> = self + .preserves + .iter() + .enumerate() + .map(|(i, _a)| next_offset + i) + .collect(); + + let sp_desc = SubpassDesc { + colors: colors.as_slice(), + depth_stencil: depth_stencil.as_ref(), + inputs: inputs.as_slice(), + resolves: resolves.as_slice(), + preserves: preserves.as_slice(), + }; + + let all_attachments = self + .colors + .into_iter() + .chain(self.depth.into_iter()) + .chain(self.inputs.into_iter()) + .chain(self.resolves.into_iter()) + .chain(self.preserves.into_iter()); + + Ok(unsafe { device.create_render_pass(all_attachments, once(sp_desc), empty())? }) + } +} diff --git a/stockton-render/src/draw/builders/shader.rs b/stockton-render/src/draw/builders/shader.rs new file mode 100644 index 0000000..fde185d --- /dev/null +++ b/stockton-render/src/draw/builders/shader.rs @@ -0,0 +1,35 @@ +use crate::types::*; + +use anyhow::{Context, Result}; +use hal::pso::Specialization; +use shaderc::{Compiler, ShaderKind}; + +#[derive(Debug, Clone)] +pub struct ShaderDesc { + pub source: String, + pub entry: String, + pub kind: ShaderKind, +} + +impl ShaderDesc { + pub fn compile(&self, compiler: &mut Compiler, device: &mut DeviceT) -> Result<ShaderModuleT> { + let artifact = compiler + .compile_into_spirv(&self.source, self.kind, "shader", &self.entry, None) + .context("Shader compilation failed")?; + + // Make into shader module + Ok(unsafe { + device + .create_shader_module(artifact.as_binary()) + .context("Shader module creation failed")? + }) + } + + pub fn as_entry<'a>(&'a self, module: &'a ShaderModuleT) -> EntryPoint<'a> { + EntryPoint { + entry: &self.entry, + module, + specialization: Specialization::default(), + } + } +} diff --git a/stockton-render/src/draw/camera.rs b/stockton-render/src/draw/camera.rs index 421b661..495e6a2 100644 --- a/stockton-render/src/draw/camera.rs +++ b/stockton-render/src/draw/camera.rs @@ -1,14 +1,15 @@ //! Things related to converting 3D world space to 2D screen space use legion::maybe_changed; +use na::{look_at_lh, perspective_lh_zo}; +use stockton_types::{ + components::{CameraSettings, CameraVPMatrix, Transform}, + Vector3, +}; + + -use nalgebra_glm::look_at_lh; -use nalgebra_glm::perspective_lh_zo; -use stockton_levels::prelude::MinRenderFeatures; -use crate::Renderer; -use stockton_types::components::{CameraSettings, Transform}; -use stockton_types::Vector3; fn euler_to_direction(euler: &Vector3) -> Vector3 { let pitch = euler.x; @@ -22,35 +23,34 @@ fn euler_to_direction(euler: &Vector3) -> Vector3 { ) } -// #[system(for_each)] -// #[filter(maybe_changed::<Transform>() | maybe_changed::<CameraSettings>())] -// pub fn calc_vp_matrix<M: 'static + MinRenderFeatures>( -// transform: &Transform, -// settings: &CameraSettings, -// #[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; -// // Get look direction from euler angles -// let direction = euler_to_direction(&transform.rotation); - -// // Converts world space to camera space -// let view_matrix = look_at_lh( -// &transform.position, -// &(transform.position + direction), -// &Vector3::new(0.0, 1.0, 0.0), -// ); - -// // Converts camera space to screen space -// let projection_matrix = { -// let mut temp = perspective_lh_zo(ratio, settings.fov, settings.near, settings.far); - -// // Vulkan's co-ord system is different from OpenGLs -// temp[(1, 1)] *= -1.0; - -// temp -// }; - -// // Chain them together into a single matrix -// renderer.context.vp_matrix = projection_matrix * view_matrix -// } +#[system(for_each)] +#[filter(maybe_changed::<Transform>() | maybe_changed::<CameraSettings>())] +pub fn calc_vp_matrix( + transform: &Transform, + settings: &CameraSettings, + matrix: &mut CameraVPMatrix, + #[state] ratio: &mut f32, +) { + // Get look direction from euler angles + let direction = euler_to_direction(&transform.rotation); + + // Converts world space to camera space + let view_matrix = look_at_lh( + &transform.position, + &(transform.position + direction), + &Vector3::new(0.0, 1.0, 0.0), + ); + + // Converts camera space to screen space + let projection_matrix = { + let mut temp = perspective_lh_zo(*ratio, settings.fov, settings.near, settings.far); + + // Vulkan's co-ord system is different from OpenGLs + temp[(1, 1)] *= -1.0; + + temp + }; + + // Chain them together into a single matrix + matrix.vp_matrix = projection_matrix * view_matrix; +} diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index 1a690fe..65dcff6 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -10,20 +10,19 @@ use std::{ use anyhow::{Context, Result}; use hal::pool::CommandPoolCreateFlags; use log::debug; -use na::Mat4; + use winit::window::Window; use super::{ - draw_passes::{DrawPass, IntoDrawPass, LevelDrawPass}, + draw_passes::{DrawPass, IntoDrawPass}, queue_negotiator::{DrawQueue, QueueNegotiator}, target::{SwapchainProperties, TargetChain}, }; use crate::{ error::{EnvironmentError, LockPoisoned}, types::*, - window::UiState, }; -use stockton_levels::prelude::*; + use stockton_types::Session; /// Contains all the hal related stuff. @@ -53,17 +52,15 @@ pub struct RenderingContext<DP> { /// Deals with drawing logic, and holds any data required for drawing. draw_pass: ManuallyDrop<DP>, - /// View projection matrix - pub(crate) vp_matrix: Mat4, - pub(crate) pixels_per_point: f32, } impl<DP: DrawPass> RenderingContext<DP> { /// Create a new RenderingContext for the given window. - pub fn new<ILDP: IntoDrawPass<DP>>( + pub fn new<IDP: IntoDrawPass<DP>>( window: &Window, - idp: ILDP, + session: &Session, + idp: IDP, ) -> Result<Self> { // Create surface let (instance, surface, mut adapters) = unsafe { @@ -97,7 +94,7 @@ impl<DP: DrawPass> RenderingContext<DP> { // Auxiliary queues for DP queue_families_specs.extend( - DP::find_aux_queues(&adapter, &mut qn) + IDP::find_aux_queues(&adapter, &mut qn) .context("Level pass couldn't populate queue negotiator")?, ); @@ -105,7 +102,7 @@ impl<DP: DrawPass> RenderingContext<DP> { }; // Device & Queue groups - let (device_lock, mut queue_groups) = { + let (device_lock, queue_groups) = { // 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() @@ -122,16 +119,22 @@ impl<DP: DrawPass> RenderingContext<DP> { (Arc::new(RwLock::new(gpu.device)), gpu.queue_groups) }; + queue_negotiator.set_queue_groups(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, - )?; + let dp = idp + .init( + session, + &adapter, + device_lock.clone(), + &mut queue_negotiator, + &swapchain_properties, + ) + .context("Error initialising draw pass")?; // Lock device let mut device = device_lock @@ -172,7 +175,7 @@ impl<DP: DrawPass> RenderingContext<DP> { adapter, queue: queue_negotiator - .get_queue::<DrawQueue>(&mut queue_groups) + .get_queue::<DrawQueue>() .ok_or(EnvironmentError::NoQueues) .context("Error getting draw queue")?, @@ -180,8 +183,6 @@ impl<DP: DrawPass> RenderingContext<DP> { target_chain: ManuallyDrop::new(target_chain), cmd_pool: ManuallyDrop::new(cmd_pool), - vp_matrix: Mat4::identity(), - // pixels_per_point: window.scale_factor() as f32, pixels_per_point: window.scale_factor() as f32, }) @@ -237,7 +238,7 @@ impl<DP: DrawPass> RenderingContext<DP> { // Level draw pass self.target_chain - .do_draw_with(&mut device, &mut queue, &*self.draw_pass, session) + .do_draw_with(&mut device, &mut queue, &mut *self.draw_pass, session) .context("Error preparing next target")?; Ok(()) diff --git a/stockton-render/src/draw/draw_passes/cons.rs b/stockton-render/src/draw/draw_passes/cons.rs index 76e2f32..8f912ec 100644 --- a/stockton-render/src/draw/draw_passes/cons.rs +++ b/stockton-render/src/draw/draw_passes/cons.rs @@ -1,8 +1,10 @@ //! 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 std::sync::{Arc, RwLock}; + use super::DrawPass; -use crate::{draw::queue_negotiator::QueueNegotiator, types::*}; +use crate::types::*; use stockton_types::Session; use anyhow::Result; @@ -14,23 +16,21 @@ pub struct ConsDrawPass<A: DrawPass, B: DrawPass> { } impl<A: DrawPass, B: DrawPass> DrawPass for ConsDrawPass<A, B> { - fn queue_draw(&self, session: &Session, cmd_buffer: &mut CommandBufferT) -> Result<()> { - self.a.queue_draw(&session, cmd_buffer)?; - self.b.queue_draw(&session, cmd_buffer)?; + fn queue_draw( + &mut self, + session: &Session, + img_view: &ImageViewT, + cmd_buffer: &mut CommandBufferT, + ) -> Result<()> { + self.a.queue_draw(session, img_view, cmd_buffer)?; + self.b.queue_draw(session, img_view, 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) + fn deactivate(self, device: &mut Arc<RwLock<DeviceT>>) -> Result<()> { + self.a.deactivate(device)?; + self.b.deactivate(device) } } @@ -38,15 +38,16 @@ impl<A: DrawPass, B: DrawPass> DrawPass for ConsDrawPass<A, B> { pub struct NilDrawPass; impl DrawPass for NilDrawPass { - - fn queue_draw(&self, _input: &Session, _cmd_buffer: &mut CommandBufferT) -> Result<()> { + fn queue_draw( + &mut self, + _input: &Session, + _img_view: &ImageViewT, + _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![]) + fn deactivate(self, _device: &mut Arc<RwLock<DeviceT>>) -> Result<()> { + Ok(()) } -}
\ No newline at end of file +} diff --git a/stockton-render/src/draw/draw_passes/level.rs b/stockton-render/src/draw/draw_passes/level.rs index b968a1a..3a4e748 100644 --- a/stockton-render/src/draw/draw_passes/level.rs +++ b/stockton-render/src/draw/draw_passes/level.rs @@ -2,18 +2,57 @@ use super::{DrawPass, IntoDrawPass}; use crate::{ - draw::{queue_negotiator::QueueNegotiator, target::SwapchainProperties, texture::TextureRepo}, - error::EnvironmentError, + draw::{ + buffers::{ + draw_buffers::{DrawBuffers, INITIAL_INDEX_SIZE, INITIAL_VERT_SIZE}, + DedicatedLoadedImage, ModifiableBuffer, + }, + builders::{ + pipeline::{ + CompletePipeline, PipelineSpecBuilder, VertexBufferSpec, + VertexPrimitiveAssemblerSpec, + }, + renderpass::RenderpassSpec, + shader::ShaderDesc, + }, + queue_negotiator::QueueNegotiator, + target::SwapchainProperties, + texture::{resolver::FsResolver, TexLoadQueue, TextureLoadConfig, TextureRepo}, + }, + error::{EnvironmentError, LevelError, LockPoisoned}, types::*, }; -use stockton_levels::features::MinRenderFeatures; -use stockton_types::*; +use hal::{ + buffer::SubRange, + command::{ClearColor, ClearDepthStencil, ClearValue, RenderAttachmentInfo, SubpassContents}, + format::{Aspects, Format}, + image::{ + Filter, FramebufferAttachment, Layout, SubresourceRange, Usage, ViewCapabilities, WrapMode, + }, + pass::{Attachment, AttachmentLoadOp, AttachmentOps, AttachmentStoreOp}, + pso::{ + BlendDesc, BlendOp, BlendState, ColorBlendDesc, ColorMask, Comparison, DepthStencilDesc, + DepthTest, Face, Factor, FrontFace, InputAssemblerDesc, LogicOp, PolygonMode, Primitive, + Rasterizer, ShaderStageFlags, State, VertexInputRate, + }, +}; +use legion::{Entity, IntoQuery}; +use shaderc::ShaderKind; +use stockton_levels::{ + features::MinRenderFeatures, + parts::{data::Geometry, IsFace}, +}; +use stockton_types::{ + components::{CameraSettings, CameraVPMatrix, Transform}, + *, +}; use std::{ array::IntoIter, + convert::TryInto, iter::{empty, once}, marker::PhantomData, - mem::{size_of, ManuallyDrop}, + path::Path, sync::{Arc, RwLock}, }; @@ -24,435 +63,398 @@ use anyhow::{Context, Result}; struct UvPoint(pub Vector3, pub i32, pub Vector2); /// Draw a level -pub struct LevelDrawPass<M: MinRenderFeatures> { +pub struct LevelDrawPass<'a, M> { pipeline: CompletePipeline, repo: TextureRepo, + active_camera: Entity, + draw_buffers: DrawBuffers<'a, UvPoint>, + + framebuffers: Vec<FramebufferT>, + depth_buffers: Vec<DedicatedLoadedImage>, + next_resources: usize, + _d: PhantomData<M>, } -impl<M: MinRenderFeatures> DrawPass for LevelDrawPass<M> { +impl<'a, M> DrawPass for LevelDrawPass<'a, M> +where + M: for<'b> MinRenderFeatures<'b> + 'static, +{ fn queue_draw( - &self, - _input: &Session, - _cmd_buffer: &mut crate::types::CommandBufferT, + &mut self, + session: &Session, + img_view: &ImageViewT, + 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"); + // We might have loaded more textures + self.repo.process_responses(); + + // Make sure we update the vertex buffers after they're written to, but before they're read from. + self.draw_buffers + .vertex_buffer + .record_commit_cmds(cmd_buffer)?; + self.draw_buffers + .index_buffer + .record_commit_cmds(cmd_buffer)?; + + // Get level & camera + let mut query = <(&Transform, &CameraSettings, &CameraVPMatrix)>::query(); + let (camera_transform, camera_settings, camera_vp) = query + .get(&session.world, self.active_camera) + .context("Couldn't find camera components")?; + let map_lock: Arc<RwLock<M>> = session.resources.get::<Arc<RwLock<M>>>().unwrap().clone(); + let map = map_lock.read().map_err(|_| LockPoisoned::Map)?; + + // Get framebuffer and depth buffer + let fb = &self.framebuffers[self.next_resources]; + let db = &self.depth_buffers[self.next_resources]; + self.next_resources = (self.next_resources + 1) % self.framebuffers.len(); -/// 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<RenderPassT>, - - /// The layout of our main graphics pipeline - pub(crate) pipeline_layout: ManuallyDrop<PipelineLayoutT>, - - /// Our main graphics pipeline - pub(crate) pipeline: ManuallyDrop<GraphicsPipelineT>, - - /// The vertex shader module - pub(crate) vs_module: ManuallyDrop<ShaderModuleT>, + unsafe { + cmd_buffer.begin_render_pass( + &self.pipeline.renderpass, + fb, + self.pipeline.render_area, + vec![ + RenderAttachmentInfo { + image_view: img_view, + clear_value: ClearValue { + color: ClearColor { + float32: [0.0, 0.0, 0.0, 1.0], + }, + }, + }, + RenderAttachmentInfo { + image_view: &*db.image_view, + clear_value: ClearValue { + depth_stencil: ClearDepthStencil { + depth: 1.0, + stencil: 0, + }, + }, + }, + ] + .into_iter(), + SubpassContents::Inline, + ); + cmd_buffer.bind_graphics_pipeline(&self.pipeline.pipeline); + + // VP Matrix + let vp = &*(camera_vp.vp_matrix.data.as_slice() as *const [f32] as *const [u32]); + + cmd_buffer.push_graphics_constants( + &self.pipeline.pipeline_layout, + ShaderStageFlags::VERTEX, + 0, + vp, + ); + + // Bind buffers + cmd_buffer.bind_vertex_buffers( + 0, + once(( + self.draw_buffers.vertex_buffer.get_buffer(), + SubRange { + offset: 0, + size: None, + }, + )), + ); + cmd_buffer.bind_index_buffer( + self.draw_buffers.index_buffer.get_buffer(), + SubRange { + offset: 0, + size: None, + }, + hal::IndexType::U16, + ); + } - /// The fragment shader module - pub(crate) fs_module: ManuallyDrop<ShaderModuleT>, -} + // Get visible faces + let mut faces = map.get_visible(camera_transform, camera_settings); + + // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once. + let face = faces.next(); + if let Some(face) = face { + let mut face = map.get_face(face).ok_or(LevelError::BadReference)?; + let mut current_chunk = face.texture_idx(&map) as usize / 8; + let mut chunk_start = 0; + + let mut curr_vert_idx: usize = 0; + let mut curr_idx_idx: usize = 0; + loop { + if current_chunk != face.texture_idx(&map) as usize / 8 { + // Last index was last of group, so draw it all if textures are loaded. + draw_or_queue( + current_chunk, + &mut self.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(&map) as usize / 8; + } + + match face.geometry(&map) { + Geometry::Vertices(v1, v2, v3) => { + for v in [v1, v2, v3] { + let uvp = + UvPoint(v.position, face.texture_idx(&map).try_into()?, v.tex); + + self.draw_buffers.vertex_buffer[curr_vert_idx] = uvp; + curr_vert_idx += 1; + } + self.draw_buffers.index_buffer[curr_idx_idx] = ( + curr_vert_idx as u16 - 3, + curr_vert_idx as u16 - 2, + curr_vert_idx as u16 - 1, + ); + 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; + } + + match faces.next() { + Some(x) => face = map.get_face(x).ok_or(LevelError::BadReference)?, + None => break, + }; + } -impl CompletePipeline { - pub fn new<'a, T: Iterator<Item = &'a DescriptorSetLayoutT> + std::fmt::Debug>( - device: &mut DeviceT, - extent: hal::image::Extent, - swapchain_properties: &SwapchainProperties, - set_layouts: T, - ) -> Result<Self> { - 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, - }; + // Draw the final group of chunks + draw_or_queue( + current_chunk, + &mut self.repo, + cmd_buffer, + &*self.pipeline.pipeline_layout, + chunk_start as u32, + curr_idx_idx as u32, + )?; + } - 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, - }; + unsafe { + cmd_buffer.end_render_pass(); + } - let subpass = SubpassDesc { - colors: &[(0, Layout::ColorAttachmentOptimal)], - depth_stencil: Some(&(1, Layout::DepthStencilAttachmentOptimal)), - inputs: &[], - resolves: &[], - preserves: &[], - }; + Ok(()) + } - unsafe { - device.create_render_pass( - IntoIter::new([img_attachment, depth_attachment]), - once(subpass), - empty(), - ) + fn deactivate(self, device_lock: &mut Arc<RwLock<DeviceT>>) -> Result<()> { + unsafe { + let mut device = device_lock.write().map_err(|_| LockPoisoned::Device)?; + self.pipeline.deactivate(&mut device); + self.draw_buffers.deactivate(&mut device); + for fb in self.framebuffers.into_iter() { + device.destroy_framebuffer(fb); } - .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")?, - ) + for db in self.depth_buffers.into_iter() { + db.deactivate(&mut device); } - }; - - // Shader entry points (ShaderStage) - let (vs_entry, fs_entry) = ( - EntryPoint::<back::Backend> { - entry: ENTRY_NAME, - module: &vs_module, - specialization: Specialization::default(), - }, - EntryPoint::<back::Backend> { - 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, - }, - }; + self.repo.deactivate(device_lock); + + Ok(()) + } +} - BlendDesc { +impl<'a, M> IntoDrawPass<LevelDrawPass<'a, M>> for Entity +where + M: for<'b> MinRenderFeatures<'b> + 'static, +{ + fn init( + self, + session: &Session, + adapter: &Adapter, + device_lock: Arc<RwLock<DeviceT>>, + queue_negotiator: &mut QueueNegotiator, + swapchain_properties: &SwapchainProperties, + ) -> Result<LevelDrawPass<'a, M>> { + let spec = PipelineSpecBuilder::default() + .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(DepthStencilDesc { + depth: Some(DepthTest { + fun: Comparison::Less, + write: true, + }), + depth_bounds: false, + stencil: None, + }) + .blender(BlendDesc { logic_op: Some(LogicOp::Copy), targets: vec![ColorBlendDesc { mask: ColorMask::ALL, - blend: Some(blend_state), + blend: Some(BlendState { + color: BlendOp::Add { + src: Factor::One, + dst: Factor::Zero, + }, + alpha: BlendOp::Add { + src: Factor::One, + dst: Factor::Zero, + }, + }), + }], + }) + .primitive_assembler(VertexPrimitiveAssemblerSpec::with_buffers( + InputAssemblerDesc::new(Primitive::TriangleList), + vec![VertexBufferSpec { + attributes: vec![Format::Rgb32Sfloat, Format::R32Sint, Format::Rg32Sfloat], + rate: VertexInputRate::Vertex, + }], + )) + .shader_vertex(ShaderDesc { + source: include_str!("../data/stockton.vert").to_string(), + entry: "main".to_string(), + kind: ShaderKind::Vertex, + }) + .shader_fragment(ShaderDesc { + source: include_str!("../data/stockton.frag").to_string(), + entry: "main".to_string(), + kind: ShaderKind::Fragment, + }) + .push_constants(vec![(ShaderStageFlags::VERTEX, 0..64)]) + .renderpass(RenderpassSpec { + colors: vec![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::ColorAttachmentOptimal..Layout::ColorAttachmentOptimal, }], + depth: Some(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, + }), + inputs: vec![], + resolves: vec![], + preserves: vec![], + }) + .build() + .context("Error building pipeline")?; + + let map_lock: Arc<RwLock<M>> = session.resources.get::<Arc<RwLock<M>>>().unwrap().clone(); + let repo = TextureRepo::new( + device_lock.clone(), + queue_negotiator + .family::<TexLoadQueue>() + .ok_or(EnvironmentError::NoSuitableFamilies) + .context("Error finding texture queue")?, + queue_negotiator + .get_queue::<TexLoadQueue>() + .ok_or(EnvironmentError::NoQueues) + .context("Error finding texture queue")?, + adapter, + TextureLoadConfig { + resolver: FsResolver::new(Path::new("textures"), map_lock), + filter: Filter::Linear, + wrap_mode: WrapMode::Tile, + }, + ) + .context("Error creating texture repo")?; + + let (draw_buffers, pipeline, framebuffers, depth_buffers) = { + let mut device = device_lock.write().map_err(|_| LockPoisoned::Device)?; + let draw_buffers = + DrawBuffers::new(&mut device, adapter).context("Error creating draw buffers")?; + let pipeline = spec + .build( + &mut device, + swapchain_properties.extent, + swapchain_properties, + once(&*repo.get_ds_layout()?), + ) + .context("Error building pipeline")?; + + let mut framebuffers = Vec::with_capacity(swapchain_properties.image_count as usize); + let mut depth_buffers = Vec::with_capacity(swapchain_properties.image_count as usize); + let fat = FramebufferAttachment { + usage: Usage::COLOR_ATTACHMENT, + format: swapchain_properties.format, + view_caps: ViewCapabilities::empty(), + }; + let dat = FramebufferAttachment { + usage: Usage::DEPTH_STENCIL_ATTACHMENT, + format: swapchain_properties.depth_format, + view_caps: ViewCapabilities::empty(), + }; + for _i in 0..swapchain_properties.image_count { + unsafe { + framebuffers.push(device.create_framebuffer( + &pipeline.renderpass, + IntoIter::new([fat.clone(), dat.clone()]), + swapchain_properties.extent, + )?); + depth_buffers.push( + DedicatedLoadedImage::new( + &mut device, + adapter, + swapchain_properties.depth_format, + Usage::DEPTH_STENCIL_ATTACHMENT, + SubresourceRange { + aspects: Aspects::DEPTH, + level_start: 0, + level_count: Some(1), + layer_start: 0, + layer_count: Some(1), + }, + swapchain_properties.extent.width as usize, + swapchain_properties.extent.height as usize, + ) + .context("Error creating depth buffer")?, + ) + } } - }; - - // 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::<f32>() * 6) as u32, - rate: VertexInputRate::Vertex, - }], - attributes: &[ - AttributeDesc { - location: 0, - binding: 0, - element: Element { - format: Format::Rgb32Sfloat, - offset: 0, - }, - }, - AttributeDesc { - location: 1, - binding: 0, - element: Element { - format: Format::R32Sint, - offset: (size_of::<f32>() * 3) as u32, - }, - }, - AttributeDesc { - location: 2, - binding: 0, - element: Element { - format: Format::Rg32Sfloat, - offset: (size_of::<f32>() * 4) as u32, - }, - }, - ], - input_assembler: InputAssemblerDesc::new(Primitive::TriangleList), - vertex: vs_entry, - tessellation: None, - geometry: None, - }; - // Pipeline description - let pipeline_desc = GraphicsPipelineDesc { - 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, + (draw_buffers, pipeline, framebuffers, depth_buffers) }; - // 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), + Ok(LevelDrawPass { + pipeline, + repo, + draw_buffers, + active_camera: self, + _d: PhantomData, + framebuffers, + depth_buffers, + next_resources: 0, }) } - /// 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))); + fn find_aux_queues<'c>( + adapter: &'c Adapter, + queue_negotiator: &mut QueueNegotiator, + ) -> Result<Vec<(&'c QueueFamilyT, Vec<f32>)>> { + queue_negotiator.find(adapter, &TexLoadQueue)?; - device.destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); - - device.destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); - } + Ok(vec![queue_negotiator + .family_spec::<TexLoadQueue>(&adapter.queue_families, 1) + .ok_or(EnvironmentError::NoSuitableFamilies)?]) } } diff --git a/stockton-render/src/draw/draw_passes/mod.rs b/stockton-render/src/draw/draw_passes/mod.rs index 566a64b..70f1786 100644 --- a/stockton-render/src/draw/draw_passes/mod.rs +++ b/stockton-render/src/draw/draw_passes/mod.rs @@ -10,22 +10,21 @@ use anyhow::Result; mod cons; mod level; -pub use level::LevelDrawPass; pub use cons::{ConsDrawPass, NilDrawPass}; +pub use level::LevelDrawPass; /// One of several 'passes' that draw on each frame. pub trait DrawPass { /// 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, session: &Session, cmd_buffer: &mut CommandBufferT) -> Result<()>; + fn queue_draw( + &mut self, + session: &Session, + img_view: &ImageViewT, + 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>)>>; + fn deactivate(self, device: &mut Arc<RwLock<DeviceT>>) -> Result<()>; } /// A type that can be made into a specific draw pass type. @@ -33,8 +32,18 @@ pub trait DrawPass { pub trait IntoDrawPass<O: DrawPass> { fn init( self, + session: &Session, + adapter: &Adapter, device: Arc<RwLock<DeviceT>>, queue_negotiator: &mut QueueNegotiator, swapchain_properties: &SwapchainProperties, ) -> Result<O>; + + /// 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>)>>; } diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs index 411e835..b570b12 100644 --- a/stockton-render/src/draw/mod.rs +++ b/stockton-render/src/draw/mod.rs @@ -3,7 +3,8 @@ pub mod target; mod buffers; -mod camera; +mod builders; +pub mod camera; mod context; pub mod draw_passes; mod queue_negotiator; diff --git a/stockton-render/src/draw/queue_negotiator.rs b/stockton-render/src/draw/queue_negotiator.rs index b39678f..65c7aa4 100644 --- a/stockton-render/src/draw/queue_negotiator.rs +++ b/stockton-render/src/draw/queue_negotiator.rs @@ -12,6 +12,7 @@ use std::{ pub struct QueueNegotiator { family_ids: HashMap<TypeId, QueueFamilyId>, already_allocated: HashMap<TypeId, (Vec<Arc<RwLock<QueueT>>>, usize)>, + all: Vec<QueueGroup>, } /// Can be used to select a specific queue family @@ -25,6 +26,7 @@ impl QueueNegotiator { QueueNegotiator { family_ids: HashMap::new(), already_allocated: HashMap::new(), + all: vec![], } } @@ -57,20 +59,23 @@ impl QueueNegotiator { Ok(()) } - pub fn get_queue<T: QueueFamilySelector>( - &mut self, - groups: &mut Vec<QueueGroup>, - ) -> Option<Arc<RwLock<QueueT>>> { + pub fn set_queue_groups(&mut self, queue_groups: Vec<QueueGroup>) { + self.all = queue_groups + } + + pub fn get_queue<T: QueueFamilySelector>(&mut self) -> Option<Arc<RwLock<QueueT>>> { let tid = TypeId::of::<T>(); let family_id = self.family_ids.get(&tid)?; - - match groups + log::debug!("{:?}", self.all); + log::debug!("{:?}", self.already_allocated); + match self + .all .iter() .position(|x| !x.queues.is_empty() && x.family == *family_id) { Some(idx) => { // At least one remaining queue - let queue = groups[idx].queues.pop().unwrap(); + let queue = self.all[idx].queues.pop().unwrap(); let queue = Arc::new(RwLock::new(queue)); self.add_to_allocated::<T>(queue.clone()); diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs index c46feb5..de803aa 100644 --- a/stockton-render/src/draw/target.rs +++ b/stockton-render/src/draw/target.rs @@ -1,19 +1,21 @@ //! Resources needed for drawing on the screen, including sync objects use std::{ + borrow::Borrow, iter::{empty, once}, mem::ManuallyDrop, }; use hal::{ command::CommandBufferFlags, - format::{ChannelType, Format, ImageFeature}, - image::{Extent, Usage as ImgUsage}, - pso::Viewport, + format::{Aspects, ChannelType, Format, ImageFeature}, + image::{Access, Extent, Layout, SubresourceRange, Usage as ImgUsage}, + memory::{Barrier, Dependencies}, + pso::{PipelineStage, Viewport}, window::{CompositeAlphaMode, Extent2D, PresentMode, SwapchainConfig}, }; -use super::{buffers::DedicatedLoadedImage, draw_passes::DrawPass}; +use super::{draw_passes::DrawPass}; use crate::{error::EnvironmentError, types::*}; use anyhow::{Context, Result}; use stockton_types::Session; @@ -26,6 +28,7 @@ pub struct SwapchainProperties { pub composite_alpha_mode: CompositeAlphaMode, pub viewport: Viewport, pub extent: Extent, + pub image_count: u32, } impl SwapchainProperties { @@ -97,6 +100,11 @@ impl SwapchainProperties { composite_alpha_mode, extent, viewport, + image_count: if present_mode == PresentMode::MAILBOX { + ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(3)) + } else { + ((*caps.image_count.end()) - 1).min((*caps.image_count.start()).max(2)) + }, }) } } @@ -104,12 +112,8 @@ impl SwapchainProperties { pub struct TargetChain { /// Surface we're targeting pub surface: ManuallyDrop<SurfaceT>, - pub properties: SwapchainProperties, - /// The depth buffer/image used for drawing - pub depth_buffer: ManuallyDrop<DedicatedLoadedImage>, - /// Resources tied to each target frame in the swapchain pub targets: Box<[TargetResources]>, @@ -155,28 +159,6 @@ impl TargetChain { image_usage: ImgUsage::COLOR_ATTACHMENT, }; - let depth_buffer = { - use hal::format::Aspects; - use hal::image::SubresourceRange; - - DedicatedLoadedImage::new( - device, - adapter, - properties.depth_format, - ImgUsage::DEPTH_STENCIL_ATTACHMENT, - SubresourceRange { - aspects: Aspects::DEPTH, - level_start: 0, - level_count: Some(1), - layer_start: 0, - layer_count: Some(1), - }, - properties.extent.width as usize, - properties.extent.height as usize, - ) - .context("Error creating depth buffer")? - }; - let _fat = swap_config.framebuffer_attachment(); let mut targets: Vec<TargetResources> = Vec::with_capacity(swap_config.image_count as usize); @@ -203,7 +185,6 @@ impl TargetChain { surface: ManuallyDrop::new(surface), targets: targets.into_boxed_slice(), sync_objects: sync_objects.into_boxed_slice(), - depth_buffer: ManuallyDrop::new(depth_buffer), properties, last_syncs: (image_count - 1) as usize, // This means the next one to be used is index 0 last_image: 0, @@ -230,8 +211,6 @@ impl TargetChain { ) -> SurfaceT { use core::ptr::read; unsafe { - ManuallyDrop::into_inner(read(&self.depth_buffer)).deactivate(device); - for i in 0..self.targets.len() { read(&self.targets[i]).deactivate(device, cmd_pool); } @@ -250,7 +229,7 @@ impl TargetChain { &'a mut self, device: &mut DeviceT, command_queue: &mut QueueT, - dp: &DP, + dp: &mut DP, session: &Session, ) -> Result<()> { self.last_syncs = (self.last_syncs + 1) % self.sync_objects.len(); @@ -280,9 +259,45 @@ impl TargetChain { unsafe { target.cmd_buffer.begin_primary(CommandBufferFlags::empty()); - dp.queue_draw(session, &mut target.cmd_buffer) + target.cmd_buffer.pipeline_barrier( + PipelineStage::TOP_OF_PIPE..PipelineStage::TOP_OF_PIPE, + Dependencies::empty(), + once(Barrier::Image { + states: (Access::empty(), Layout::Undefined) + ..(Access::empty(), Layout::ColorAttachmentOptimal), + target: img.borrow(), + range: SubresourceRange { + aspects: Aspects::COLOR, + level_start: 0, + level_count: Some(1), + layer_start: 0, + layer_count: Some(1), + }, + families: None, + }), + ); + + dp.queue_draw(session, img.borrow(), &mut target.cmd_buffer) .context("Error in draw pass")?; + target.cmd_buffer.pipeline_barrier( + PipelineStage::BOTTOM_OF_PIPE..PipelineStage::BOTTOM_OF_PIPE, + Dependencies::empty(), + once(Barrier::Image { + states: (Access::empty(), Layout::ColorAttachmentOptimal) + ..(Access::empty(), Layout::Present), + target: img.borrow(), + range: SubresourceRange { + aspects: Aspects::COLOR, + level_start: 0, + level_count: Some(1), + layer_start: 0, + layer_count: Some(1), + }, + families: None, + }), + ); + target.cmd_buffer.finish(); } diff --git a/stockton-render/src/draw/texture/resolver.rs b/stockton-render/src/draw/texture/resolver.rs index 1dbc62d..4b61c41 100644 --- a/stockton-render/src/draw/texture/resolver.rs +++ b/stockton-render/src/draw/texture/resolver.rs @@ -4,7 +4,6 @@ use crate::draw::texture::image::LoadableImage; use stockton_levels::{parts::IsTexture, prelude::HasTextures}; use std::{ - mem::drop, path::Path, sync::{Arc, RwLock}, }; @@ -39,8 +38,8 @@ impl<'a, T: HasTextures> TextureResolver for FsResolver<'a, T> { let tex = map.get_texture(tex)?; let path = self.path.join(&tex.name()); - drop(tex); - drop(map); + // drop(tex); + // drop(map); if let Ok(file) = Reader::open(path) { if let Ok(guessed) = file.with_guessed_format() { @@ -50,6 +49,7 @@ impl<'a, T: HasTextures> TextureResolver for FsResolver<'a, T> { } } + log::warn!("Couldn't resolve texture {:?}", tex.name()); None } } diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs index 850d3eb..17dbf03 100644 --- a/stockton-render/src/lib.rs +++ b/stockton-render/src/lib.rs @@ -6,6 +6,9 @@ extern crate gfx_hal as hal; extern crate nalgebra_glm as na; #[macro_use] +extern crate derive_builder; + +#[macro_use] extern crate legion; pub mod draw; @@ -15,22 +18,18 @@ mod types; pub mod window; use draw::{ + draw_passes::{DrawPass, IntoDrawPass}, RenderingContext, - draw_passes::{DrawPass, IntoDrawPass} }; -use error::full_error_display; -use legion::world::SubWorld; -use legion::IntoQuery; + use std::sync::mpsc::{Receiver, Sender}; use std::sync::Arc; use std::sync::RwLock; pub use window::{UiState, WindowEvent}; use anyhow::Result; -use log::error; -use stockton_levels::prelude::*; -use stockton_types::components::{CameraSettings, Transform}; -use stockton_types::{Vector3, Session}; + +use stockton_types::Session; use winit::event_loop::ControlFlow; use winit::window::Window; @@ -51,13 +50,17 @@ pub struct Renderer<DP> { impl<DP: DrawPass> Renderer<DP> { /// Create a new Renderer. - pub fn new<IDP: IntoDrawPass<DP>>(window: &Window, ui: &mut UiState, idp: IDP) -> Result<(Self, Sender<WindowEvent>)> { + pub fn new<IDP: IntoDrawPass<DP>>( + window: &Window, + session: &Session, + idp: IDP, + ) -> Result<(Self, Sender<WindowEvent>)> { let (tx, rx) = channel(); let update_control_flow = Arc::new(RwLock::new(ControlFlow::Poll)); Ok(( Renderer { - context: RenderingContext::new(window, idp)?, + context: RenderingContext::new(window, session, idp)?, window_events: rx, update_control_flow, }, @@ -66,7 +69,7 @@ impl<DP: DrawPass> Renderer<DP> { } /// Render a single frame of the given session. - fn render(&mut self, session: &Session) -> Result<()> { + pub fn render(&mut self, session: &Session) -> Result<()> { // Try to draw if self.context.draw_next_frame(session).is_err() { // Probably the surface changed @@ -82,4 +85,4 @@ impl<DP: DrawPass> Renderer<DP> { fn resize(&mut self) -> Result<()> { unsafe { self.context.handle_surface_change() } } -}
\ No newline at end of file +} diff --git a/stockton-render/src/systems.rs b/stockton-render/src/systems.rs index 67cea84..5f86c29 100644 --- a/stockton-render/src/systems.rs +++ b/stockton-render/src/systems.rs @@ -1 +1,2 @@ +pub use crate::draw::camera::calc_vp_matrix_system; pub use crate::window::process_window_events_system; diff --git a/stockton-render/src/types.rs b/stockton-render/src/types.rs index 797ced9..03c6e37 100644 --- a/stockton-render/src/types.rs +++ b/stockton-render/src/types.rs @@ -25,6 +25,7 @@ pub type FramebufferT = <back::Backend as hal::Backend>::Framebuffer; pub type RenderPassT = <back::Backend as hal::Backend>::RenderPass; pub type Adapter = hal::adapter::Adapter<back::Backend>; +pub type EntryPoint<'a> = hal::pso::EntryPoint<'a, back::Backend>; pub type QueueGroup = hal::queue::QueueGroup<back::Backend>; pub type DescriptorAllocator = rendy_descriptor::DescriptorAllocator<back::Backend>; diff --git a/stockton-render/src/window.rs b/stockton-render/src/window.rs index 6e6a0e1..62e43d3 100644 --- a/stockton-render/src/window.rs +++ b/stockton-render/src/window.rs @@ -1,8 +1,8 @@ -use crate::{error::full_error_display, Renderer, DrawPass}; +use crate::{error::full_error_display, DrawPass, Renderer}; use egui::{Modifiers, Rect, Vec2}; use legion::systems::Runnable; use log::debug; -use stockton_levels::prelude::MinRenderFeatures; + use egui::{CtxRef, Event, Output, Pos2, RawInput}; use epaint::ClippedShape; @@ -93,7 +93,7 @@ impl UiState { } } - pub fn populate_initial_state<T: MinRenderFeatures>(&mut self, renderer: &Renderer<T>) { + pub fn populate_initial_state<'a, T: DrawPass>(&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)); |