From f6c2f402e245c620f8f03dcf3aa6265fca6e8dcf Mon Sep 17 00:00:00 2001 From: tcmal Date: Sun, 25 Aug 2024 17:44:20 +0100 Subject: feat(render): 3D projection --- stockton-render/src/draw/buffer.rs | 2 +- stockton-render/src/draw/camera.rs | 215 ++++++++++++++++++++++++++++ stockton-render/src/draw/context.rs | 68 +++++---- stockton-render/src/draw/data/stockton.frag | 17 +-- stockton-render/src/draw/data/stockton.vert | 22 +-- stockton-render/src/draw/mod.rs | 1 + 6 files changed, 278 insertions(+), 47 deletions(-) create mode 100644 stockton-render/src/draw/camera.rs (limited to 'stockton-render/src/draw') diff --git a/stockton-render/src/draw/buffer.rs b/stockton-render/src/draw/buffer.rs index 7bbd7e3..f59431e 100644 --- a/stockton-render/src/draw/buffer.rs +++ b/stockton-render/src/draw/buffer.rs @@ -86,7 +86,7 @@ impl<'a, T: Sized> StagedBuffer<'a, T> { // Map it somewhere and get a slice to that memory let staged_mapped_memory = unsafe { - let ptr = device.map_memory(&staged_memory, Segment::ALL).unwrap(); + let ptr = device.map_memory(&staged_memory, Segment::ALL).unwrap(); // TODO std::slice::from_raw_parts_mut(ptr as *mut T, size.try_into().unwrap()) }; diff --git a/stockton-render/src/draw/camera.rs b/stockton-render/src/draw/camera.rs new file mode 100644 index 0000000..2cba57e --- /dev/null +++ b/stockton-render/src/draw/camera.rs @@ -0,0 +1,215 @@ +// Copyright (C) 2019 Oscar Shrimpton + +// 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 . + +//! Things related to converting 3D world space to 2D screen space + +use std::iter::once; +use hal::prelude::*; +use hal::buffer::Usage; +use na::{look_at_lh, perspective_lh_zo}; + +use core::mem::ManuallyDrop; + +use crate::error; +use crate::types::*; +use super::buffer::{StagedBuffer, ModifiableBuffer}; +use stockton_types::{Vector3, Matrix4}; + +/// Holds settings related to the projection of world space to screen space +/// Also holds maths for generating important matrices +pub struct Camera<'a> { + position: Vector3, + looking_at: Vector3, + up: Vector3, + + /// Aspect ratio as a fraction + aspect_ratio: f32, + + /// FOV in radians + fov: f32, + + /// Near clipping plane (world units) + near: f32, + + /// Far clipping plane (world units) + far: f32, + + /// Layout of the descriptor set to pass to the shader + pub descriptor_set_layout: ManuallyDrop, + + /// Buffer of memory used for passing data to shaders + // TODO: Does this need to be staged? + buffer: ManuallyDrop>, + + // TODO: Share descriptor pool with textures? + descriptor_pool: ManuallyDrop, + descriptor_set: DescriptorSet, + + /// If true, buffer needs updated + is_dirty: bool +} + +impl<'a> Camera<'a> { + /// Return a camera with default settings + // TODO + pub fn defaults(aspect_ratio: f32, device: &mut Device, adapter: &Adapter, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool) -> Result, error::CreationError> { + + let descriptor_type = { + use hal::pso::{DescriptorType, BufferDescriptorType, BufferDescriptorFormat}; + + DescriptorType::Buffer { + ty: BufferDescriptorType::Uniform, + format: BufferDescriptorFormat::Structured { + dynamic_offset: false + } + } + }; + + // Create set layout + let descriptor_set_layout = unsafe { + use hal::pso::{DescriptorSetLayoutBinding, ShaderStageFlags}; + + device.create_descriptor_set_layout( + &[ + DescriptorSetLayoutBinding { + binding: 0, + ty: descriptor_type, + count: 1, + stage_flags: ShaderStageFlags::VERTEX, + immutable_samplers: false + } + ], + &[], + ) + }.map_err(|_| error::CreationError::OutOfMemoryError)?; + + // Create pool and allocate set + let (descriptor_pool, descriptor_set) = unsafe { + use hal::pso::{DescriptorRangeDesc, DescriptorPoolCreateFlags}; + + let mut pool = device.create_descriptor_pool( + 1, + &[ + DescriptorRangeDesc { + ty: descriptor_type, + count: 1 + } + ], + DescriptorPoolCreateFlags::empty() + ).map_err(|_| error::CreationError::OutOfMemoryError)?; + + let set = pool.allocate_set(&descriptor_set_layout).map_err(|_| error::CreationError::OutOfMemoryError)?; + + (pool, set) + }; + + // Create buffer for descriptor + let mut buffer = StagedBuffer::new(device, adapter, Usage::UNIFORM, 1)?; + + // Bind our buffer to our descriptor set + unsafe { + use hal::pso::{Descriptor, DescriptorSetWrite}; + use hal::buffer::SubRange; + + device.write_descriptor_sets(once( + DescriptorSetWrite { + set: &descriptor_set, + binding: 0, + array_offset: 0, + descriptors: once( + Descriptor::Buffer(buffer.commit(device, command_queue, command_pool), SubRange::WHOLE) + ) + } + )); + } + + Ok(Camera { + position: Vector3::new(-0.5, 1.5, -1.0), + looking_at: Vector3::new(0.5, 0.5, 0.5), + up: Vector3::new(0.0, 1.0, 0.0), + + aspect_ratio, + fov: f32::to_radians(90.0), + near: 0.1, + far: 100.0, + + descriptor_set_layout: ManuallyDrop::new(descriptor_set_layout), + buffer: ManuallyDrop::new(buffer), + + descriptor_pool: ManuallyDrop::new(descriptor_pool), + descriptor_set: descriptor_set, + + is_dirty: true + }) + } + + /// Returns a matrix that transforms from world space to screen space + pub fn vp_matrix(&self) -> Matrix4 { + // Converts world space to camera space + let view_matrix = look_at_lh( + &self.position, + &self.looking_at, + &self.up + ); + + // Converts camera space to screen space + let projection_matrix = { + let mut temp = perspective_lh_zo( + self.aspect_ratio, + self.fov, + self.near, + self.far + ); + + // Vulkan's co-ord system is different from openGLs + temp[(1, 1)] *= -1.0; + + temp + }; + + // Chain them together into a single matrix + projection_matrix * view_matrix + } + + pub fn commit<'b>(&'b mut self, device: &Device, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool) -> &'b DescriptorSet { + // Update buffer if needed + if self.is_dirty { + self.buffer[0] = self.vp_matrix(); + self.buffer.commit(device, command_queue, command_pool); + + self.is_dirty = false; + } + + // Return the descriptor set for matrices + &self.descriptor_set + } + + /// This should be called before dropping + pub fn deactivate(mut self, device: &mut Device) -> () { + unsafe { + use core::ptr::read; + + ManuallyDrop::into_inner(read(&self.buffer)).deactivate(device); + + self.descriptor_pool.reset(); + device.destroy_descriptor_pool(ManuallyDrop::into_inner(read(&self.descriptor_pool))); + device.destroy_descriptor_set_layout(ManuallyDrop::into_inner(read(&self.descriptor_set_layout))); + } + } +} \ No newline at end of file diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index c179f9d..061d22c 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -19,8 +19,8 @@ use std::{ mem::{ManuallyDrop, size_of}, - iter::once, - ops::Deref + ops::Deref, + borrow::Borrow }; use winit::window::Window; use arrayvec::ArrayVec; @@ -35,6 +35,7 @@ use stockton_types::{Vector2, Vector3}; use crate::types::*; use crate::error; +use super::camera::Camera; use super::texture::TextureStore; use super::buffer::{StagedBuffer, ModifiableBuffer}; @@ -63,9 +64,9 @@ const VERTEX_SOURCE: &str = include_str!("./data/stockton.vert"); /// Source for fragment shader. TODO const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag"); -/// Represents a point of a vertices, including RGB, UV and texture information. +/// Represents a point of a triangle, including UV and texture information. #[derive(Debug, Clone, Copy)] -pub struct UVPoint (pub Vector2, pub Vector3, pub Vector2, pub i32); +pub struct UVPoint (pub Vector3, pub Vector2, pub i32); /// Contains all the hal related stuff. /// In the end, this takes some 3D points and puts it on the screen. @@ -109,6 +110,8 @@ pub struct RenderingContext<'a> { // These are both staged pub vert_buffer: ManuallyDrop>, pub index_buffer: ManuallyDrop>, + + camera: ManuallyDrop> } impl<'a> RenderingContext<'a> { @@ -129,7 +132,7 @@ impl<'a> RenderingContext<'a> { let adapter = adapters.remove(0); // Device & Queue group - let (mut device, queue_group) = { + let (mut device, mut queue_group) = { let family = adapter .queue_families .iter() @@ -283,12 +286,12 @@ impl<'a> RenderingContext<'a> { // Command Pool, Buffers, imageviews, framebuffers & Sync objects let frames_in_flight = backbuffer.len(); - let (cmd_pool, cmd_buffers, get_image, render_complete, present_complete, imageviews, framebuffers) = { + let (mut cmd_pool, cmd_buffers, get_image, render_complete, present_complete, imageviews, framebuffers) = { use hal::pool::CommandPoolCreateFlags; use hal::command::Level; let mut cmd_pool = ManuallyDrop::new(unsafe { - device.create_command_pool(queue_group.family, CommandPoolCreateFlags::empty()) + device.create_command_pool(queue_group.family, CommandPoolCreateFlags::RESET_INDIVIDUAL) }.map_err(|_| error::CreationError::OutOfMemoryError)?); let mut cmd_buffers = Vec::with_capacity(frames_in_flight); @@ -332,8 +335,17 @@ impl<'a> RenderingContext<'a> { // Texture store let texture_store = TextureStore::new(&mut device, INITIAL_TEX_SIZE)?; + // Camera + // TODO: Settings + let ratio = extent.width as f32 / extent.height as f32; + let camera = Camera::defaults(ratio, &mut device, &adapter, &mut queue_group.queues[0], &mut cmd_pool)?; + + let mut descriptor_set_layouts: ArrayVec<[_; 2]> = ArrayVec::new(); + descriptor_set_layouts.push(camera.descriptor_set_layout.deref()); + descriptor_set_layouts.push(texture_store.descriptor_set_layout.deref()); + // Graphics pipeline - let (pipeline_layout, pipeline) = Self::create_pipeline(&mut device, extent, &subpass, &texture_store.descriptor_set_layout)?; + let (pipeline_layout, pipeline) = Self::create_pipeline(&mut device, extent, &subpass, descriptor_set_layouts)?; Ok(RenderingContext { instance: ManuallyDrop::new(instance), @@ -365,6 +377,8 @@ impl<'a> RenderingContext<'a> { vert_buffer: ManuallyDrop::new(vert_buffer), index_buffer: ManuallyDrop::new(index_buffer), + + camera: ManuallyDrop::new(camera) }) } @@ -378,11 +392,11 @@ impl<'a> RenderingContext<'a> { } #[allow(clippy::type_complexity)] - pub fn create_pipeline(device: &mut Device, extent: hal::image::Extent, subpass: &hal::pass::Subpass, set_layout: &DescriptorSetLayout) -> Result< + pub fn create_pipeline(device: &mut Device, extent: hal::image::Extent, subpass: &hal::pass::Subpass, set_layouts: T) -> Result< ( PipelineLayout, GraphicsPipeline, - ), error::CreationError> { + ), error::CreationError> where T: IntoIterator, T::Item: Borrow { use hal::pso::*; use hal::format::Format; @@ -435,44 +449,37 @@ impl<'a> RenderingContext<'a> { // Vertex buffers let vertex_buffers: Vec = vec![VertexBufferDesc { binding: 0, - stride: (size_of::() * 8) as u32, + stride: (size_of::() * 6) as u32, rate: VertexInputRate::Vertex, }]; - let attributes: Vec = vec![AttributeDesc { // XY Attribute + let attributes: Vec = vec![AttributeDesc { // XYZ Attribute location: 0, binding: 0, element: Element { - format: Format::Rg32Sfloat, + format: Format::Rgb32Sfloat, offset: 0, }, - }, AttributeDesc { // RGB Attribute - location: 1, - binding: 0, - element: Element { - format: Format::Rgb32Sfloat, - offset: (size_of::() * 2) as ElemOffset, - } }, AttributeDesc { // UV Attribute - location: 2, + location: 1, binding: 0, element: Element { format: Format::Rg32Sfloat, - offset: (size_of::() * 5) as ElemOffset, + offset: (size_of::() * 3) as ElemOffset, } }, AttributeDesc { // Tex Attribute - location: 3, + location: 2, binding: 0, element: Element { format: Format::R32Sint, - offset: (size_of::() * 7) as ElemOffset + offset: (size_of::() * 5) as ElemOffset } }]; // Rasterizer let rasterizer = Rasterizer { polygon_mode: PolygonMode::Fill, - cull_face: Face::NONE, + cull_face: Face::BACK, front_face: FrontFace::Clockwise, depth_clamping: false, depth_bias: None, @@ -489,7 +496,7 @@ impl<'a> RenderingContext<'a> { // Pipeline layout let layout = unsafe { - device.create_pipeline_layout(once(set_layout), &[]) + device.create_pipeline_layout(set_layouts, &[]) }.map_err(|_| error::CreationError::OutOfMemoryError)?; // Colour blending @@ -694,10 +701,16 @@ impl<'a> RenderingContext<'a> { ); buffer.bind_graphics_pipeline(&self.pipeline); + let mut descriptor_sets: ArrayVec<[_; 2]> = ArrayVec::new(); + descriptor_sets.push(self.camera.commit(&self.device, + &mut self.queue_group.queues[0], + &mut self.cmd_pool)); + descriptor_sets.push(&self.texture_store.descriptor_set); + buffer.bind_graphics_descriptor_sets( &self.pipeline_layout, 0, - once(self.texture_store.descriptor_set.deref()), + descriptor_sets, &[] ); @@ -765,6 +778,7 @@ impl<'a> core::ops::Drop for RenderingContext<'a> { ManuallyDrop::into_inner(read(&self.vert_buffer)).deactivate(&mut self.device); ManuallyDrop::into_inner(read(&self.index_buffer)).deactivate(&mut self.device); ManuallyDrop::into_inner(read(&self.texture_store)).deactivate(&mut self.device); + ManuallyDrop::into_inner(read(&self.camera)).deactivate(&mut self.device); self.device.destroy_command_pool( ManuallyDrop::into_inner(read(&self.cmd_pool)), diff --git a/stockton-render/src/draw/data/stockton.frag b/stockton-render/src/draw/data/stockton.frag index 09ff8a7..b558335 100644 --- a/stockton-render/src/draw/data/stockton.frag +++ b/stockton-render/src/draw/data/stockton.frag @@ -1,19 +1,16 @@ #version 450 -layout(set = 0, binding = 0) uniform texture2D tex[2]; -layout(set = 0, binding = 1) uniform sampler samp[2]; +// DescriptorSet 0 = Matrices +// DescriptorSet 1 = Textures +layout(set = 1, binding = 0) uniform texture2D tex[2]; +layout(set = 1, binding = 1) uniform sampler samp[2]; -layout (location = 1) in vec3 frag_color; -layout (location = 2) in vec2 frag_uv; -layout (location = 3) in flat int frag_tex; +layout (location = 1) in vec2 frag_uv; +layout (location = 2) in flat int frag_tex; layout (location = 0) out vec4 color; void main() { - if(frag_tex == -1) { - color = vec4(frag_color, 1.0); - } else { - color = texture(sampler2D(tex[frag_tex], samp[frag_tex]), frag_uv); - } + color = texture(sampler2D(tex[frag_tex], samp[frag_tex]), frag_uv); } \ No newline at end of file diff --git a/stockton-render/src/draw/data/stockton.vert b/stockton-render/src/draw/data/stockton.vert index f40d0b8..d3f4438 100644 --- a/stockton-render/src/draw/data/stockton.vert +++ b/stockton-render/src/draw/data/stockton.vert @@ -1,21 +1,25 @@ #version 450 -layout (location = 0) in vec2 position; -layout (location = 1) in vec3 colour; -layout (location = 2) in vec2 uv; -layout (location = 3) in int tex; +// DescriptorSet 0 = Matrices +layout (binding = 0) uniform UniformBufferObject { + mat4 vp; +} matrices; + +// DescriptorSet 1 = Textures/Samplers + +layout (location = 0) in vec3 position; +layout (location = 1) in vec2 uv; +layout (location = 2) in int tex; out gl_PerVertex { vec4 gl_Position; }; -layout (location = 1) out vec3 frag_colour; -layout (location = 2) out vec2 frag_uv; -layout (location = 3) out flat int frag_tex; +layout (location = 1) out vec2 frag_uv; +layout (location = 2) out flat int frag_tex; void main() { - gl_Position = vec4(position, 0.0, 1.0); - frag_colour = colour; + gl_Position = matrices.vp * vec4(position, 1.0); frag_uv = uv; frag_tex = tex; } \ No newline at end of file diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs index a1a5780..00b5bb6 100644 --- a/stockton-render/src/draw/mod.rs +++ b/stockton-render/src/draw/mod.rs @@ -18,6 +18,7 @@ mod context; mod buffer; mod texture; +mod camera; pub use self::context::RenderingContext; pub use self::context::UVPoint; -- cgit v1.2.3