aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:22 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:22 +0100
commitdfb74318dae09b3430acf533374dc7004df65c65 (patch)
tree4e18f70dd7b34d29039d8f93bd5d1578712f5a88
parent2a0d97911052cf3631523cb4573aae69a3816e85 (diff)
feat(render): 2D drawing on top of 3D scene
-rw-r--r--stockton-render/src/draw/context.rs160
-rw-r--r--stockton-render/src/draw/draw_buffers.rs11
-rw-r--r--stockton-render/src/draw/mod.rs4
-rw-r--r--stockton-render/src/draw/pipeline.rs39
-rw-r--r--stockton-render/src/draw/render.rs117
-rw-r--r--stockton-render/src/draw/target.rs91
-rw-r--r--stockton-render/src/draw/ui/data/stockton.frag8
-rw-r--r--stockton-render/src/draw/ui/data/stockton.vert11
-rw-r--r--stockton-render/src/draw/ui/mod.rs22
-rw-r--r--stockton-render/src/draw/ui/pipeline.rs295
-rw-r--r--stockton-render/src/draw/ui/render.rs28
11 files changed, 638 insertions, 148 deletions
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<CompletePipeline>,
+ /// 2D Graphics pipeline and associated objects
+ ui_pipeline: ManuallyDrop<UIPipeline>,
+
// Command pool and buffers
/// The command pool used for our buffers
cmd_pool: ManuallyDrop<CommandPool>,
@@ -81,6 +79,9 @@ pub struct RenderingContext<'a> {
/// Buffers used for drawing
draw_buffers: ManuallyDrop<DrawBuffers<'a>>,
+ /// Buffers used for drawing the UI
+ ui_draw_buffers: ManuallyDrop<DrawBuffers<'a>>,
+
/// 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 <http://www.gnu.org/licenses/>.
*/
-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 <http://www.gnu.org/licenses/>.
+ */
+
+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<M: MinBSPFeatures<VulkanSystem>>(
+ 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<Swapchain>,
@@ -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: &<back::Backend as hal::Backend>::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>,
+
+ /// Framebuffer for this frame when drawing in 2D
+ pub framebuffer_2d: ManuallyDrop<Framebuffer>,
}
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<TargetResources, TargetResourcesCreationError> {
// 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 <http://www.gnu.org/licenses/>.
+ */
+
+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 <http://www.gnu.org/licenses/>.
+ */
+//! 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<RenderPass>,
+
+ /// The layout of our main graphics pipeline
+ pub(crate) pipeline_layout: ManuallyDrop<PipelineLayout>,
+
+ /// Our main graphics pipeline
+ pub(crate) pipeline: ManuallyDrop<GraphicsPipeline>,
+
+ /// The vertex shader module
+ pub(crate) vs_module: ManuallyDrop<ShaderModule>,
+
+ /// The fragment shader module
+ pub(crate) fs_module: ManuallyDrop<ShaderModule>,
+}
+
+impl UIPipeline {
+ pub fn new<T>(
+ device: &mut Device,
+ extent: hal::image::Extent,
+ swapchain_properties: &SwapchainProperties,
+ set_layouts: T,
+ ) -> Result<Self, error::CreationError>
+ where
+ T: IntoIterator,
+ T::Item: Borrow<DescriptorSetLayout>,
+ {
+ 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::<back::Backend> {
+ entry: ENTRY_NAME,
+ module: &vs_module,
+ specialization: Specialization::default(),
+ },
+ EntryPoint::<back::Backend> {
+ 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<VertexBufferDesc> = vec![VertexBufferDesc {
+ binding: 0,
+ stride: (size_of::<f32>() * 5) as u32,
+ rate: VertexInputRate::Vertex,
+ }];
+
+ let attributes: Vec<AttributeDesc> = pipeline_vb_attributes!(0,
+ size_of::<f32>() * 2; Rg32Sfloat,
+ size_of::<f32>() * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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);
+ }
+}