diff options
Diffstat (limited to 'stockton-render/src/draw/draw_passes')
-rw-r--r-- | stockton-render/src/draw/draw_passes/cons.rs | 66 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/level.rs | 483 | ||||
-rw-r--r-- | stockton-render/src/draw/draw_passes/mod.rs | 43 |
3 files changed, 592 insertions, 0 deletions
diff --git a/stockton-render/src/draw/draw_passes/cons.rs b/stockton-render/src/draw/draw_passes/cons.rs new file mode 100644 index 0000000..274bd76 --- /dev/null +++ b/stockton-render/src/draw/draw_passes/cons.rs @@ -0,0 +1,66 @@ +//! Code for using multiple draw passes in place of just one +//! Note that this can be extended to an arbitrary amount of draw passes. + +use super::{DrawPass, DrawPassInput}; +use crate::{draw::queue_negotiator::QueueNegotiator, types::*}; +use anyhow::Result; + +/// One draw pass, then another. +struct ConsDrawPass<A: DrawPass, B: DrawPass> { + a: A, + b: B, +} + +impl<A: DrawPass, B: DrawPass> DrawPass for ConsDrawPass<A, B> { + type Input = ConsDrawPassInput<A::Input, B::Input>; + + fn queue_draw(&self, input: &Self::Input, cmd_buffer: &mut CommandBufferT) -> Result<()> { + self.a.queue_draw(&input.a, cmd_buffer)?; + self.b.queue_draw(&input.b, cmd_buffer)?; + + Ok(()) + } + + fn find_aux_queues<'a>( + adapter: &'a Adapter, + queue_negotiator: &mut QueueNegotiator, + ) -> Result<Vec<(&'a QueueFamilyT, Vec<f32>)>> { + let mut vec = Vec::new(); + + vec.extend(A::find_aux_queues(adapter, queue_negotiator)?); + vec.extend(B::find_aux_queues(adapter, queue_negotiator)?); + + Ok(vec) + } +} + +/// Input for a ConsDrawPass. +struct ConsDrawPassInput<A, B> { + pub a: A, + pub b: B, +} + +impl<A: DrawPassInput, B: DrawPassInput> DrawPassInput for ConsDrawPassInput<A, B> {} + +/// A draw pass that does nothing. Can be used at the end of sequences if there's an odd number of draw passes. +struct NilDrawPass; + +impl DrawPass for NilDrawPass { + type Input = NilDrawPassInput; + + fn queue_draw(&self, _input: &Self::Input, _cmd_buffer: &mut CommandBufferT) -> Result<()> { + Ok(()) + } + + fn find_aux_queues<'a>( + _adapter: &'a Adapter, + _queue_negotiator: &mut QueueNegotiator, + ) -> Result<Vec<(&'a QueueFamilyT, Vec<f32>)>> { + Ok(vec![]) + } +} + +/// Input for a NilDrawPass. +struct NilDrawPassInput; + +impl DrawPassInput for NilDrawPassInput {} diff --git a/stockton-render/src/draw/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<M: MinRenderFeatures> { + pipeline: CompletePipeline, + repo: TextureRepo, + _d: PhantomData<M>, +} + +/// Any map can be used as draw pass input. +/// TODO: Restrict this based on the type of the renderer. +impl<T: MinRenderFeatures> DrawPassInput for T {} + +impl<M: MinRenderFeatures> DrawPass for LevelDrawPass<M> { + type Input = M; + + fn queue_draw( + &self, + _file: &Self::Input, + _cmd_buffer: &mut crate::types::CommandBufferT, + ) -> anyhow::Result<()> { + todo!() + // // Get visible faces + // // let faces = get_visible_faces( + // // pos, + // // &*self + // // .context + // // .map + // // .read() + // // .map_err(|_| LockPoisoned::Map) + // // .context("Error getting read lock on map")?, + // // ); + // let faces: Vec<u32> = { + // let map = &*self + // .context + // .map + // .read() + // .map_err(|_| LockPoisoned::Map) + // .context("Error getting read lock on map")?; + + // map.iter_faces().map(|x| x.index(map)).collect() + // }; + + // // Iterate over faces, copying them in and drawing groups that use the same texture chunk all at once. + // let mut current_chunk = file + // .get_face(0) + // .ok_or(LevelError::BadReference)? + // .texture_idx(file) as usize + // / 8; + // let mut chunk_start = 0; + + // let mut curr_vert_idx: usize = 0; + // let mut curr_idx_idx: usize = 0; + + // for face in faces.iter().map(|idx| file.get_face(*idx)) { + // if let Some(face) = face { + // if current_chunk != face.texture_idx(file) as usize / 8 { + // // Last index was last of group, so draw it all if textures are loaded. + // draw_or_queue( + // current_chunk, + // self.tex_repo, + // cmd_buffer, + // self.pipeline.pipeline_layout, + // chunk_start as u32, + // curr_idx_idx as u32, + // )?; + + // // Next group of same-chunked faces starts here. + // chunk_start = curr_idx_idx; + // current_chunk = face.texture_idx(file) as usize / 8; + // } + + // match face.geometry(file) { + // Geometry::Vertices(v1, v2, v3) => { + // for v in [v1, v2, v3] { + // let uvp = + // UvPoint(v.position, face.texture_idx(file).try_into()?, v.tex); + + // draw_buffers.vertex_buffer[curr_vert_idx] = uvp; + // curr_vert_idx += 1; + // } + // draw_buffers.index_buffer[curr_idx_idx] = ( + // curr_vert_idx as u16 - 2, + // curr_vert_idx as u16 - 1, + // curr_vert_idx as u16, + // ); + // curr_idx_idx += 1; + // } + // } + + // if curr_vert_idx >= INITIAL_VERT_SIZE.try_into()? + // || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into()? + // { + // println!("out of vertex buffer space!"); + // break; + // } + // } else { + // anyhow::bail!(LevelError::BadReference); + // } + // } + + // // Draw the final group of chunks + // draw_or_queue( + // current_chunk, + // self.tex_repo, + // cmd_buffer, + // self.pipeline.pipeline_layout, + // chunk_start as u32, + // curr_idx_idx as u32, + // )?; + + // Ok(()) + } + + fn find_aux_queues<'a>( + _adapter: &'a Adapter, + _queue_negotiator: &mut QueueNegotiator, + ) -> Result<Vec<(&'a QueueFamilyT, Vec<f32>)>> { + todo!() + // queue_negotiator.find(TexLoadQueue) + } +} + +impl<M: MinRenderFeatures> IntoDrawPass<LevelDrawPass<M>> for () { + fn init( + self, + _device_lock: Arc<RwLock<DeviceT>>, + _queue_negotiator: &mut QueueNegotiator, + _swapchain_properties: &SwapchainProperties, + ) -> Result<LevelDrawPass<M>> { + todo!() + // let repo = TextureRepo::new( + // device_lock.clone(), + // queue_negotiator + // .family() + // .ok_or(EnvironmentError::NoQueues)?, + // ); + // let pipeline = { + // let device = device_lock.write().or(Err(LockPoisoned::Device))?; + // CompletePipeline::new( + // device, + // swapchain_properties.extent, + // swapchain_properties, + // std::iter::empty(), + // )? + // }; + // Ok(LevelDrawPass { + // pipeline, + // repo, + // _d: PhantomData, + // }) + } +} + +/// Entry point name for shaders +const ENTRY_NAME: &str = "main"; + +/// Source for vertex shader. TODO +const VERTEX_SOURCE: &str = include_str!("../data/stockton.vert"); + +/// Source for fragment shader. TODO +const FRAGMENT_SOURCE: &str = include_str!("../data/stockton.frag"); + +/// A complete graphics pipeline and associated resources +pub struct CompletePipeline { + /// Our main render pass + 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: ManuallyDrop<ShaderModuleT>, +} + +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, + }; + + 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::<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, + }, + }; + + 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::<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, + }; + + // 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<Vec<(&'a QueueFamilyT, Vec<f32>)>>; +} + +/// A type that can be made into a specific draw pass type. +/// This allows extra data to be used in initialisation without the Renderer needing to worry about it. +pub trait IntoDrawPass<O: DrawPass> { + fn init( + self, + device: Arc<RwLock<DeviceT>>, + queue_negotiator: &mut QueueNegotiator, + swapchain_properties: &SwapchainProperties, + ) -> Result<O>; +} |