From dfb74318dae09b3430acf533374dc7004df65c65 Mon Sep 17 00:00:00 2001 From: tcmal Date: Sun, 25 Aug 2024 17:44:22 +0100 Subject: feat(render): 2D drawing on top of 3D scene --- stockton-render/src/draw/context.rs | 160 +++++--------- stockton-render/src/draw/draw_buffers.rs | 11 +- stockton-render/src/draw/mod.rs | 4 +- stockton-render/src/draw/pipeline.rs | 39 +--- stockton-render/src/draw/render.rs | 117 ++++++++++ stockton-render/src/draw/target.rs | 91 +++++++- stockton-render/src/draw/ui/data/stockton.frag | 8 + stockton-render/src/draw/ui/data/stockton.vert | 11 + stockton-render/src/draw/ui/mod.rs | 22 ++ stockton-render/src/draw/ui/pipeline.rs | 295 +++++++++++++++++++++++++ stockton-render/src/draw/ui/render.rs | 28 +++ 11 files changed, 638 insertions(+), 148 deletions(-) create mode 100644 stockton-render/src/draw/render.rs create mode 100644 stockton-render/src/draw/ui/data/stockton.frag create mode 100644 stockton-render/src/draw/ui/data/stockton.vert create mode 100644 stockton-render/src/draw/ui/mod.rs create mode 100644 stockton-render/src/draw/ui/pipeline.rs create mode 100644 stockton-render/src/draw/ui/render.rs (limited to 'stockton-render') diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index b17bbe9..94f1f0c 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -19,7 +19,7 @@ //! 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. -use std::{convert::TryInto, mem::ManuallyDrop, ops::Deref}; +use std::{mem::ManuallyDrop, ops::Deref}; use arrayvec::ArrayVec; use hal::{pool::CommandPoolCreateFlags, prelude::*}; @@ -27,22 +27,17 @@ use log::debug; use na::Mat4; use winit::window::Window; -use stockton_levels::prelude::*; -use stockton_levels::traits::faces::FaceType; -use stockton_types::{Vector2, Vector3}; - use super::{ buffer::ModifiableBuffer, - draw_buffers::{DrawBuffers, INITIAL_INDEX_SIZE, INITIAL_VERT_SIZE}, + draw_buffers::DrawBuffers, pipeline::CompletePipeline, + render::do_render, target::{SwapchainProperties, TargetChain}, texture::TextureStore, + ui::{do_render as do_render_ui, UIPipeline}, }; use crate::{error, types::*}; - -/// Represents a point of a triangle, including UV and texture information. -#[derive(Debug, Clone, Copy)] -pub struct UVPoint(pub Vector3, pub i32, pub Vector2); +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. @@ -68,6 +63,9 @@ pub struct RenderingContext<'a> { /// 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, @@ -81,6 +79,9 @@ pub struct RenderingContext<'a> { /// Buffers used for drawing draw_buffers: ManuallyDrop>, + /// Buffers used for drawing the UI + ui_draw_buffers: ManuallyDrop>, + /// View projection matrix pub(crate) vp_matrix: Mat4, } @@ -143,6 +144,9 @@ impl<'a> RenderingContext<'a> { // Vertex and index buffers let draw_buffers = DrawBuffers::new(&mut device, &adapter)?; + // UI Vertex and index buffers + let ui_draw_buffers = DrawBuffers::new(&mut device, &adapter)?; + // Texture store let texture_store = TextureStore::new( &mut device, @@ -163,12 +167,21 @@ impl<'a> RenderingContext<'a> { descriptor_set_layouts, )?; + // UI pipeline + let ui_pipeline = UIPipeline::new( + &mut device, + swapchain_properties.extent, + &swapchain_properties, + &[], + )?; + // Swapchain and associated resources let target_chain = TargetChain::new( &mut device, &adapter, &mut surface, &pipeline, + &ui_pipeline, &mut cmd_pool, swapchain_properties, None, @@ -187,10 +200,12 @@ impl<'a> RenderingContext<'a> { cmd_pool: ManuallyDrop::new(cmd_pool), pipeline: ManuallyDrop::new(pipeline), + ui_pipeline: ManuallyDrop::new(ui_pipeline), texture_store: ManuallyDrop::new(texture_store), draw_buffers: ManuallyDrop::new(draw_buffers), + ui_draw_buffers: ManuallyDrop::new(ui_draw_buffers), vp_matrix: Mat4::identity(), }) @@ -222,6 +237,13 @@ impl<'a> RenderingContext<'a> { )? }); + // 2D Graphics pipeline + // TODO: Recycle + ManuallyDrop::into_inner(read(&self.ui_pipeline)).deactivate(&mut self.device); + self.ui_pipeline = ManuallyDrop::new({ + UIPipeline::new(&mut self.device, properties.extent, &properties, &[])? + }); + let old_swapchain = ManuallyDrop::into_inner(read(&self.target_chain)) .deactivate_with_recyling(&mut self.device, &mut self.cmd_pool); self.target_chain = ManuallyDrop::new( @@ -230,6 +252,7 @@ impl<'a> RenderingContext<'a> { &self.adapter, &mut self.surface, &self.pipeline, + &self.ui_pipeline, &mut self.cmd_pool, properties, Some(old_swapchain), @@ -246,102 +269,27 @@ impl<'a> RenderingContext<'a> { file: &M, faces: &[u32], ) -> Result<(), &'static str> { - // Prepare command buffer + // 3D Pass let cmd_buffer = self.target_chain.prep_next_target( &mut self.device, &mut self.draw_buffers, &self.pipeline, &self.vp_matrix, )?; + do_render( + cmd_buffer, + &mut self.draw_buffers, + &self.texture_store, + &self.pipeline.pipeline_layout, + file, + faces, + ); - // 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. - let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); - descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk)); - unsafe { - cmd_buffer.bind_graphics_descriptor_sets( - &self.pipeline.pipeline_layout, - 0, - descriptor_sets, - &[], - ); - cmd_buffer.draw_indexed( - chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, - 0, - 0..1, - ); - } - - // 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().unwrap(); - - 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().unwrap(), uv); - self.draw_buffers.vertex_buffer[curr_vert_idx] = uvp; - - curr_vert_idx += 1; - } - - self.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().unwrap() - || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() - { - println!("out of vertex buffer space!"); - break; - } - } - } else { - // TODO: Other types of faces - } - - if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() - || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() - { - println!("out of vertex buffer space!"); - break; - } - } - - // Draw the final group of chunks - let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); - descriptor_sets.push(self.texture_store.get_chunk_descriptor_set(current_chunk)); - unsafe { - cmd_buffer.bind_graphics_descriptor_sets( - &self.pipeline.pipeline_layout, - 0, - descriptor_sets, - &[], - ); - cmd_buffer.draw_indexed( - chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, - 0, - 0..1, - ); - } + // 2D Pass + let cmd_buffer = self + .target_chain + .target_2d_pass(&mut self.ui_draw_buffers, &self.ui_pipeline)?; + do_render_ui(cmd_buffer, &mut self.ui_draw_buffers); // Update our buffers before we actually start drawing self.draw_buffers.vertex_buffer.commit( @@ -356,6 +304,18 @@ impl<'a> RenderingContext<'a> { &mut self.cmd_pool, ); + self.ui_draw_buffers.vertex_buffer.commit( + &self.device, + &mut self.queue_group.queues[0], + &mut self.cmd_pool, + ); + + self.ui_draw_buffers.index_buffer.commit( + &self.device, + &mut self.queue_group.queues[0], + &mut self.cmd_pool, + ); + // Send commands off to GPU self.target_chain .finish_and_submit_target(&mut self.queue_group.queues[0])?; @@ -372,6 +332,7 @@ impl<'a> core::ops::Drop for RenderingContext<'a> { use core::ptr::read; ManuallyDrop::into_inner(read(&self.draw_buffers)).deactivate(&mut self.device); + ManuallyDrop::into_inner(read(&self.ui_draw_buffers)).deactivate(&mut self.device); ManuallyDrop::into_inner(read(&self.texture_store)).deactivate(&mut self.device); ManuallyDrop::into_inner(read(&self.target_chain)) @@ -381,6 +342,7 @@ impl<'a> core::ops::Drop for RenderingContext<'a> { .destroy_command_pool(ManuallyDrop::into_inner(read(&self.cmd_pool))); ManuallyDrop::into_inner(read(&self.pipeline)).deactivate(&mut self.device); + ManuallyDrop::into_inner(read(&self.ui_pipeline)).deactivate(&mut self.device); self.instance .destroy_surface(ManuallyDrop::into_inner(read(&self.surface))); diff --git a/stockton-render/src/draw/draw_buffers.rs b/stockton-render/src/draw/draw_buffers.rs index faf9c4e..a6e0996 100644 --- a/stockton-render/src/draw/draw_buffers.rs +++ b/stockton-render/src/draw/draw_buffers.rs @@ -15,13 +15,14 @@ * with this program. If not, see . */ -use crate::{ - draw::{buffer::StagedBuffer, UVPoint}, - error::CreationError, - types::*, -}; +use crate::{draw::buffer::StagedBuffer, error::CreationError, types::*}; use hal::buffer::Usage; use std::mem::ManuallyDrop; +use stockton_types::{Vector2, Vector3}; + +/// Represents a point of a triangle, including UV and texture information. +#[derive(Debug, Clone, Copy)] +pub struct UVPoint(pub Vector3, pub i32, pub Vector2); /// Initial size of vertex buffer. TODO: Way of overriding this pub const INITIAL_VERT_SIZE: u64 = 3 * 3000; diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs index 482d62c..c96fc10 100644 --- a/stockton-render/src/draw/mod.rs +++ b/stockton-render/src/draw/mod.rs @@ -26,8 +26,10 @@ mod camera; mod context; mod draw_buffers; mod pipeline; +mod render; mod texture; +mod ui; pub use self::camera::calc_vp_matrix_system; pub use self::context::RenderingContext; -pub use self::context::UVPoint; +pub use self::draw_buffers::UVPoint; diff --git a/stockton-render/src/draw/pipeline.rs b/stockton-render/src/draw/pipeline.rs index 8ad167d..2e45b61 100644 --- a/stockton-render/src/draw/pipeline.rs +++ b/stockton-render/src/draw/pipeline.rs @@ -71,11 +71,7 @@ impl CompletePipeline { // Renderpass let renderpass = { - use hal::{ - image::{Access, Layout}, - memory::Dependencies, - pass::*, - }; + use hal::{image::Layout, pass::*}; let img_attachment = Attachment { format: Some(swapchain_properties.format), @@ -85,7 +81,7 @@ impl CompletePipeline { AttachmentLoadOp::Clear, AttachmentStoreOp::DontCare, ), - layouts: Layout::Undefined..Layout::Present, + layouts: Layout::Undefined..Layout::ColorAttachmentOptimal, }; let depth_attachment = Attachment { @@ -107,37 +103,8 @@ impl CompletePipeline { preserves: &[], }; - let in_dependency = SubpassDependency { - flags: Dependencies::empty(), - passes: None..Some(0), - stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT - ..(PipelineStage::COLOR_ATTACHMENT_OUTPUT - | PipelineStage::EARLY_FRAGMENT_TESTS), - accesses: Access::empty() - ..(Access::COLOR_ATTACHMENT_READ - | Access::COLOR_ATTACHMENT_WRITE - | Access::DEPTH_STENCIL_ATTACHMENT_READ - | Access::DEPTH_STENCIL_ATTACHMENT_WRITE), - }; - - let out_dependency = SubpassDependency { - flags: Dependencies::empty(), - passes: Some(0)..None, - stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT | PipelineStage::EARLY_FRAGMENT_TESTS - ..PipelineStage::COLOR_ATTACHMENT_OUTPUT, - accesses: (Access::COLOR_ATTACHMENT_READ - | Access::COLOR_ATTACHMENT_WRITE - | Access::DEPTH_STENCIL_ATTACHMENT_READ - | Access::DEPTH_STENCIL_ATTACHMENT_WRITE) - ..Access::empty(), - }; - unsafe { - device.create_render_pass( - &[img_attachment, depth_attachment], - &[subpass], - &[in_dependency, out_dependency], - ) + device.create_render_pass(&[img_attachment, depth_attachment], &[subpass], &[]) } .map_err(|_| error::CreationError::OutOfMemoryError)? }; diff --git a/stockton-render/src/draw/render.rs b/stockton-render/src/draw/render.rs new file mode 100644 index 0000000..e73b569 --- /dev/null +++ b/stockton-render/src/draw/render.rs @@ -0,0 +1,117 @@ +/* + * Copyright (C) Oscar Shrimpton 2020 + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +use crate::draw::draw_buffers::INITIAL_INDEX_SIZE; +use crate::draw::draw_buffers::INITIAL_VERT_SIZE; +use crate::draw::texture::TextureStore; +use crate::draw::UVPoint; +use arrayvec::ArrayVec; +use faces::FaceType; +use hal::prelude::*; +use std::convert::TryInto; +use stockton_levels::prelude::*; +use stockton_types::Vector2; + +use crate::draw::draw_buffers::DrawBuffers; +use crate::types::*; + +pub fn do_render>( + cmd_buffer: &mut CommandBuffer, + draw_buffers: &mut DrawBuffers, + texture_store: &TextureStore, + pipeline_layout: &PipelineLayout, + file: &M, + faces: &[u32], +) { + // 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. + let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); + descriptor_sets.push(texture_store.get_chunk_descriptor_set(current_chunk)); + unsafe { + cmd_buffer.bind_graphics_descriptor_sets(pipeline_layout, 0, descriptor_sets, &[]); + cmd_buffer.draw_indexed( + chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, + 0, + 0..1, + ); + } + + // 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().unwrap(); + + 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().unwrap(), 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().unwrap() + || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() + { + println!("out of vertex buffer space!"); + break; + } + } + } else { + // TODO: Other types of faces + } + + if curr_vert_idx >= INITIAL_VERT_SIZE.try_into().unwrap() + || curr_idx_idx >= INITIAL_INDEX_SIZE.try_into().unwrap() + { + println!("out of vertex buffer space!"); + break; + } + } + + // Draw the final group of chunks + let mut descriptor_sets: ArrayVec<[_; 1]> = ArrayVec::new(); + descriptor_sets.push(texture_store.get_chunk_descriptor_set(current_chunk)); + unsafe { + cmd_buffer.bind_graphics_descriptor_sets(&pipeline_layout, 0, descriptor_sets, &[]); + cmd_buffer.draw_indexed( + chunk_start as u32 * 3..(curr_idx_idx as u32 * 3) + 1, + 0, + 0..1, + ); + } +} diff --git a/stockton-render/src/draw/target.rs b/stockton-render/src/draw/target.rs index 12a01ea..b47700f 100644 --- a/stockton-render/src/draw/target.rs +++ b/stockton-render/src/draw/target.rs @@ -32,7 +32,7 @@ use na::Mat4; use super::{ buffer::ModifiableBuffer, draw_buffers::DrawBuffers, pipeline::CompletePipeline, - texture::image::LoadedImage, + texture::image::LoadedImage, ui::UIPipeline, }; use crate::types::*; @@ -151,11 +151,13 @@ pub struct TargetChain { } impl TargetChain { + #[allow(clippy::too_many_arguments)] pub fn new( device: &mut Device, adapter: &Adapter, surface: &mut Surface, pipeline: &CompletePipeline, + ui_pipeline: &UIPipeline, cmd_pool: &mut CommandPool, properties: SwapchainProperties, old_swapchain: Option, @@ -218,10 +220,10 @@ impl TargetChain { device, cmd_pool, &pipeline.renderpass, + &ui_pipeline.renderpass, image, &(*depth_buffer.image_view), - properties.extent, - properties.format, + &properties, ) .map_err(|_| TargetChainCreationError::Todo)?, ); @@ -369,6 +371,69 @@ impl TargetChain { Ok(&mut target.cmd_buffer) } + pub fn target_2d_pass<'a>( + &'a mut self, + draw_buffers: &mut DrawBuffers, + pipeline: &UIPipeline, + ) -> Result<&'a mut CommandBuffer, &'static str> { + 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(), + &[], + ); + } + + // Record commands + unsafe { + use hal::buffer::{IndexBufferView, SubRange}; + use hal::command::{ClearColor, ClearValue, SubpassContents}; + + // Colour to clear window to + let clear_values = [ClearValue { + color: ClearColor { + float32: [1.0, 0.0, 0.0, 1.5], + }, + }]; + + // Get references to our buffers + let (vbufs, ibuf) = { + let vbufref: &::Buffer = + draw_buffers.vertex_buffer.get_buffer(); + + let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].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, + clear_values.iter(), + SubpassContents::Inline, + ); + target.cmd_buffer.bind_graphics_pipeline(&pipeline.pipeline); + + // Bind buffers + target.cmd_buffer.bind_vertex_buffers(0, vbufs); + target.cmd_buffer.bind_index_buffer(IndexBufferView { + buffer: ibuf, + range: SubRange::WHOLE, + index_type: hal::IndexType::U16, + }); + }; + + Ok(&mut target.cmd_buffer) + } + pub fn finish_and_submit_target( &mut self, command_queue: &mut CommandQueue, @@ -427,6 +492,9 @@ pub struct TargetResources { /// Framebuffer for this frame pub framebuffer: ManuallyDrop, + + /// Framebuffer for this frame when drawing in 2D + pub framebuffer_2d: ManuallyDrop, } impl TargetResources { @@ -434,10 +502,10 @@ impl TargetResources { device: &mut Device, cmd_pool: &mut CommandPool, renderpass: &RenderPass, + renderpass_2d: &RenderPass, image: Image, depth_pass: &ImageView, - extent: Extent, - format: Format, + properties: &SwapchainProperties, ) -> Result { // Command Buffer let cmd_buffer = unsafe { cmd_pool.allocate_one(hal::command::Level::Primary) }; @@ -448,7 +516,7 @@ impl TargetResources { .create_image_view( &image, ViewKind::D2, - format, + properties.format, Swizzle::NO, COLOR_RANGE.clone(), ) @@ -461,16 +529,24 @@ impl TargetResources { .create_framebuffer( &renderpass, once(&imageview).chain(once(depth_pass)), - extent, + properties.extent, ) .map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? }; + // 2D framebuffer just needs the imageview, not the depth pass + let framebuffer_2d = unsafe { + device + .create_framebuffer(&renderpass_2d, once(&imageview), properties.extent) + .map_err(|_| TargetResourcesCreationError::FrameBufferNoMemory)? + }; + Ok(TargetResources { cmd_buffer: ManuallyDrop::new(cmd_buffer), image: ManuallyDrop::new(image), imageview: ManuallyDrop::new(imageview), framebuffer: ManuallyDrop::new(framebuffer), + framebuffer_2d: ManuallyDrop::new(framebuffer_2d), }) } @@ -480,6 +556,7 @@ impl TargetResources { cmd_pool.free(once(ManuallyDrop::into_inner(read(&self.cmd_buffer)))); device.destroy_framebuffer(ManuallyDrop::into_inner(read(&self.framebuffer))); + device.destroy_framebuffer(ManuallyDrop::into_inner(read(&self.framebuffer_2d))); device.destroy_image_view(ManuallyDrop::into_inner(read(&self.imageview))); } } diff --git a/stockton-render/src/draw/ui/data/stockton.frag b/stockton-render/src/draw/ui/data/stockton.frag new file mode 100644 index 0000000..1a85ab6 --- /dev/null +++ b/stockton-render/src/draw/ui/data/stockton.frag @@ -0,0 +1,8 @@ +#version 450 +#extension GL_ARB_separate_shader_objects : enable + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(1.0, 0.0, 0.0, 1.0); +} \ No newline at end of file diff --git a/stockton-render/src/draw/ui/data/stockton.vert b/stockton-render/src/draw/ui/data/stockton.vert new file mode 100644 index 0000000..1dd9477 --- /dev/null +++ b/stockton-render/src/draw/ui/data/stockton.vert @@ -0,0 +1,11 @@ +#version 450 + +vec2 positions[3] = vec2[]( + vec2(0.0, -0.5), + vec2(0.5, 0.5), + vec2(-0.5, 0.5) +); + +void main() { + gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); +} diff --git a/stockton-render/src/draw/ui/mod.rs b/stockton-render/src/draw/ui/mod.rs new file mode 100644 index 0000000..cd19362 --- /dev/null +++ b/stockton-render/src/draw/ui/mod.rs @@ -0,0 +1,22 @@ +/* + * Copyright (C) Oscar Shrimpton 2020 + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +pub mod pipeline; +pub mod render; + +pub use pipeline::UIPipeline; +pub use render::do_render; diff --git a/stockton-render/src/draw/ui/pipeline.rs b/stockton-render/src/draw/ui/pipeline.rs new file mode 100644 index 0000000..378c558 --- /dev/null +++ b/stockton-render/src/draw/ui/pipeline.rs @@ -0,0 +1,295 @@ +/* + * Copyright (C) Oscar Shrimpton 2020 + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ +//! 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::{ + borrow::Borrow, + mem::{size_of, ManuallyDrop}, +}; + +use hal::prelude::*; + +use crate::draw::target::SwapchainProperties; +use crate::error; +use crate::types::*; + +/// A complete 2D graphics pipeline and associated resources +pub struct UIPipeline { + /// 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 UIPipeline { + pub fn new( + device: &mut Device, + extent: hal::image::Extent, + swapchain_properties: &SwapchainProperties, + set_layouts: T, + ) -> Result + where + T: IntoIterator, + T::Item: Borrow, + { + use hal::format::Format; + use hal::pso::*; + + // Renderpass + let renderpass = { + use hal::{ + image::{Access, Layout}, + memory::Dependencies, + pass::*, + }; + + let img_attachment = Attachment { + format: Some(swapchain_properties.format), + samples: 1, + ops: AttachmentOps::new(AttachmentLoadOp::Load, AttachmentStoreOp::Store), + stencil_ops: AttachmentOps::new( + AttachmentLoadOp::DontCare, + AttachmentStoreOp::DontCare, + ), + layouts: Layout::ColorAttachmentOptimal..Layout::Present, + }; + + let subpass = SubpassDesc { + colors: &[(0, Layout::ColorAttachmentOptimal)], + depth_stencil: None, + inputs: &[], + resolves: &[], + preserves: &[], + }; + + let external_dependency = SubpassDependency { + flags: Dependencies::empty(), + passes: None..Some(0), + stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT + ..(PipelineStage::COLOR_ATTACHMENT_OUTPUT + | PipelineStage::EARLY_FRAGMENT_TESTS), + accesses: Access::empty() + ..(Access::COLOR_ATTACHMENT_READ | Access::COLOR_ATTACHMENT_WRITE), + }; + + unsafe { + device.create_render_pass(&[img_attachment], &[subpass], &[external_dependency]) + } + .map_err(|_| error::CreationError::OutOfMemoryError)? + }; + + // 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(error::CreationError::NoShaderC)?; + + let vertex_compile_artifact = compiler + .compile_into_spirv( + VERTEX_SOURCE, + shaderc::ShaderKind::Vertex, + "vertex.vert", + ENTRY_NAME, + None, + ) + .map_err(error::CreationError::ShaderCError)?; + + let fragment_compile_artifact = compiler + .compile_into_spirv( + FRAGMENT_SOURCE, + shaderc::ShaderKind::Fragment, + "fragment.frag", + ENTRY_NAME, + None, + ) + .map_err(error::CreationError::ShaderCError)?; + + // Make into shader module + unsafe { + ( + device + .create_shader_module(vertex_compile_artifact.as_binary()) + .map_err(error::CreationError::ShaderModuleFailed)?, + device + .create_shader_module(fragment_compile_artifact.as_binary()) + .map_err(error::CreationError::ShaderModuleFailed)?, + ) + } + }; + + // 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(), + }, + ); + + // Shader set + let shaders = GraphicsShaderSet { + vertex: vs_entry, + fragment: Some(fs_entry), + hull: None, + domain: None, + geometry: None, + }; + + // Vertex buffers + let vertex_buffers: Vec = vec![VertexBufferDesc { + binding: 0, + stride: (size_of::() * 5) as u32, + rate: VertexInputRate::Vertex, + }]; + + let attributes: Vec = pipeline_vb_attributes!(0, + size_of::() * 2; Rg32Sfloat, + size_of::() * 3; Rgb32Sfloat + ); + + // Rasterizer + let rasterizer = Rasterizer { + polygon_mode: PolygonMode::Fill, + cull_face: Face::NONE, + front_face: FrontFace::CounterClockwise, + depth_clamping: false, + depth_bias: None, + conservative: true, + line_width: hal::pso::State::Static(1.0), + }; + + // Depth stencil + let depth_stencil = DepthStencilDesc { + depth: None, + depth_bounds: false, + stencil: None, + }; + + // Pipeline layout + let layout = unsafe { device.create_pipeline_layout(set_layouts, &[]) } + .map_err(|_| error::CreationError::OutOfMemoryError)?; + + // Colour blending + let blender = { + let blend_state = BlendState { + color: BlendOp::Add { + src: Factor::SrcAlpha, + dst: Factor::OneMinusSrcAlpha, + }, + alpha: BlendOp::Add { + src: Factor::OneMinusSrcAlpha, + dst: Factor::Zero, + }, + }; + + BlendDesc { + logic_op: None, + 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_color: None, + depth_bounds: None, + }; + + // Input assembler + let input_assembler = InputAssemblerDesc::new(Primitive::TriangleList); + + // Pipeline description + let pipeline_desc = GraphicsPipelineDesc { + shaders, + rasterizer, + vertex_buffers, + blender, + depth_stencil, + multisampling: None, + baked_states, + layout: &layout, + subpass, + flags: PipelineCreationFlags::empty(), + parent: BasePipeline::None, + input_assembler, + attributes, + }; + + // Pipeline + let pipeline = unsafe { device.create_graphics_pipeline(&pipeline_desc, None) } + .map_err(error::CreationError::PipelineError)?; + + Ok(UIPipeline { + 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 Device) { + 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/ui/render.rs b/stockton-render/src/draw/ui/render.rs new file mode 100644 index 0000000..b965a80 --- /dev/null +++ b/stockton-render/src/draw/ui/render.rs @@ -0,0 +1,28 @@ +/* + * Copyright (C) Oscar Shrimpton 2020 + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +use hal::prelude::*; + +use crate::draw::draw_buffers::DrawBuffers; +use crate::types::*; + +pub fn do_render(cmd_buffer: &mut CommandBuffer, _draw_buffers: &mut DrawBuffers) { + // TODO: Actual UI Rendering + unsafe { + cmd_buffer.draw(0..3, 0..1); + } +} -- cgit v1.2.3