From cd94fb83fb51d37a84cbb80c8135430e26c74566 Mon Sep 17 00:00:00 2001 From: tcmal Date: Sun, 25 Aug 2024 17:44:19 +0100 Subject: feat(render): add draw triangles function --- stockton-render/src/draw/context.rs | 1200 ++++++++++++--------------- stockton-render/src/draw/data/stockton.frag | 8 + stockton-render/src/draw/data/stockton.vert | 12 + stockton-render/src/draw/frame.rs | 51 -- stockton-render/src/draw/mod.rs | 1 - 5 files changed, 569 insertions(+), 703 deletions(-) create mode 100644 stockton-render/src/draw/data/stockton.frag create mode 100644 stockton-render/src/draw/data/stockton.vert delete mode 100644 stockton-render/src/draw/frame.rs (limited to 'stockton-render/src/draw') diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index ffac0ec..6e51f46 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -14,52 +14,38 @@ // with this program. If not, see . //! Deals with all the Vulkan/HAL details. +use crate::error as error; +use crate::error::{CreationError, FrameError}; + use core::mem::{ManuallyDrop, size_of}; -use std::borrow::Cow; +use std::convert::TryInto; -use crate::error::{CreationError, FrameError}; -use super::frame::FrameCell; +use winit::{EventsLoop, WindowBuilder}; use arrayvec::ArrayVec; -use winit::Window; +use hal::*; +use hal::format::{AsFormat, Rgba8Srgb as ColorFormat, Format, ChannelType}; -// Trait imports -use hal::{Surface as SurfaceTrait, Instance as InstanceTrait, QueueFamily as QFTrait, PhysicalDevice as PDTrait, Device as DeviceTrait, Swapchain as SwapchainTrait}; +use back::Backend; -use hal::{Graphics, Gpu, Features, SwapchainConfig, Submission}; -use hal::pso::*; -use hal::pass::{Subpass, SubpassDesc, AttachmentOps, Attachment, AttachmentStoreOp, AttachmentLoadOp}; -use hal::image::{Usage, Layout, SubresourceRange, ViewKind, Extent}; -use hal::command::{ClearValue, ClearColor, CommandBuffer}; -use hal::format::{ChannelType, Format, Swizzle, Aspects}; -use hal::pool::{CommandPoolCreateFlags, CommandPool}; -use hal::window::{PresentMode, Extent2D}; -use hal::queue::family::QueueGroup; -use hal::adapter::{Adapter, MemoryTypeId}; - -use back::{Instance}; -use back::{Backend}; +#[cfg(feature = "gl")] +use back::glutin as glutin; use stockton_types::Vector2; -const VERTEX_SOURCE: &str = "#version 450 -layout (location = 0) in vec2 position; -out gl_PerVertex { - vec4 gl_Position; +const ENTRY_NAME: &str = "main"; +const COLOR_RANGE: image::SubresourceRange = image::SubresourceRange { + aspects: format::Aspects::COLOR, + levels: 0..1, + layers: 0..1, }; -void main() -{ - gl_Position = vec4(position, 0.0, 1.0); -}"; -const FRAGMENT_SOURCE: &str = "#version 450 -layout(location = 0) out vec4 color; -void main() -{ - color = vec4(1.0); -}"; + +const VERTEX_SOURCE: &str = include_str!("./data/stockton.vert"); +const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag"); /// Represents a triangle in 2D (screen) space. +#[derive(Debug, Clone, Copy)] pub struct Tri2 (pub [Vector2; 3]); /// Easy conversion to proper format. @@ -71,397 +57,339 @@ impl From for [f32; 6] { } } -/// Size of Tri2 -const F32_XY_TRIANGLE: u64 = (size_of::() * 2 * 3) as u64; - /// Contains all the hal related stuff. /// In the end, this takes some 3D points and puts it on the screen. -pub struct RenderingContext<'a> { - /// The window to render to - window: &'a Window, - - /// The instance of vulkan (or another backend) being used. - /// For vulkan, the name is always "stockton" & version is always 1 - instance: ManuallyDrop, - - /// The surface being drawn to. - /// Mostly an abstraction over Window. +// TODO: Settings for clear colour, buffer sizes, etc +pub struct RenderingContext { + pub events_loop: winit::EventsLoop, surface: ::Surface, - - /// Points to something that's used for rendering & presentation. - adapter: Adapter, - /// Lets us actually render & present device: ManuallyDrop<::Device>, - - /// A collection of images that we create then present. - swapchain: ManuallyDrop<::Swapchain>, - /// The size of where we're rendering to. - render_area: Rect, - - /// Describes the render pipeline. - render_pass: ManuallyDrop<::RenderPass>, - - /// The format of the images. - format: Format, - - /// What part(s) of the device will do our work - queue_group: ManuallyDrop>, - - /// A parent for all our command buffers. - command_pool: ManuallyDrop>, - - /// Each FrameCell draws a frame to its own image & command buffer. - cells: Vec, - - /// Images from the swapchain to render to. - backbuffer: Vec<::Image>, - - /// The images we're rendering to with some additional metadata. - image_views: Vec<::ImageView>, + swapchain: ManuallyDrop<::Swapchain>, + + viewport: pso::Viewport, - /// What the command buffers will actually target. + imageviews: Vec<::ImageView>, framebuffers: Vec<::Framebuffer>, - /// The maximum number of frames we'll have ready for presentation at any time. - frames_in_flight: usize, + renderpass: ManuallyDrop<::RenderPass>, - /// Track which framecell is up next. current_frame: usize, + // TODO: Collect these together + get_image: Vec<::Semaphore>, + render_complete: Vec<::Semaphore>, + present_complete: Vec<::Fence>, + + frames_in_flight: usize, + cmd_pools: Vec>>, + cmd_buffers: Vec>, + queue_group: QueueGroup, - descriptor_set_layouts: Vec<::DescriptorSetLayout>, + buffer: ManuallyDrop<::Buffer>, + memory: ManuallyDrop<::Memory>, + requirements: memory::Requirements, + descriptor_set_layouts: ::DescriptorSetLayout, pipeline_layout: ManuallyDrop<::PipelineLayout>, - pipeline: ManuallyDrop<::GraphicsPipeline>, - buffer: ManuallyDrop<::Buffer>, - memory: ManuallyDrop<::Memory>, - requirements: hal::memory::Requirements } -impl<'a> RenderingContext<'a> { +impl RenderingContext { /// Create a new RenderingContext for the given window. - pub fn new(window: &'a Window) -> Result { - let instance = Instance::create("stockton", 1); - let mut surface = instance.create_surface(&window); - - // find a suitable adapter - // one that can render graphics & present to our surface - let adapter = instance - .enumerate_adapters() - .into_iter() - .find(|a| { - a.queue_families - .iter() - .any(|qf| qf.supports_graphics() && surface.supports_queue_family(qf)) - }) - .ok_or(CreationError::NoAdapter)?; - - // from that adapter, get the device & queue group - let (mut device, queue_group) = { - let queue_family = adapter - .queue_families - .iter() - .find(|qf| qf.supports_graphics() && surface.supports_queue_family(qf)) - .ok_or(CreationError::NoQueueFamily)?; - - let Gpu { device, mut queues } = unsafe { - adapter - .physical_device - .open(&[(&queue_family, &[1.0; 1])], Features::empty()) - .map_err(|_| CreationError::NoPhysicalDevice)? - }; - - let queue_group = queues - .take::(queue_family.id()) - .ok_or(CreationError::NoQueueGroup)?; - - if queue_group.queues.is_empty() { - return Err(CreationError::NoCommandQueues) - }; - - (device, queue_group) - }; - - // Create the swapchain - let (swapchain, extent, backbuffer, format, frames_in_flight) = { - let (caps, formats, modes) = surface.compatibility(&adapter.physical_device); - - let present_mode = { - use hal::window::PresentMode::*; - [Mailbox, Fifo, Relaxed, Immediate] - .iter() - .cloned() - .find(|pm| modes.contains(pm)) - .ok_or(CreationError::NoPresentModes)? - }; - - let composite_alpha = { - use hal::window::CompositeAlpha; - [CompositeAlpha::OPAQUE, CompositeAlpha::INHERIT, CompositeAlpha::PREMULTIPLIED, CompositeAlpha::POSTMULTIPLIED] - .iter() - .cloned() - .find(|ca| caps.composite_alpha.contains(*ca)) - .ok_or(CreationError::NoCompositeAlphas)? - }; - - let format = match formats { - None => Format::Rgba8Srgb, - Some(formats) => match formats - .iter() - .find(|format| format.base_format().1 == ChannelType::Srgb) - .cloned() { - Some(srgb_format) => srgb_format, - None => formats.get(0).cloned().ok_or(CreationError::NoImageFormats)?, - }, - }; - - let extent = { - let window_client_area = window - .get_inner_size() - .ok_or(CreationError::NoWindow)? - .to_physical(window.get_hidpi_factor()); - Extent2D { - width: caps.extents.end.width.min(window_client_area.width as u32), - height: caps.extents.end.height.min(window_client_area.height as u32) - } - }; - let image_count = if present_mode == PresentMode::Mailbox { - (caps.image_count.end - 1).min(3) - } else { - (caps.image_count.end - 1).min(2) + pub fn new() -> Result { + let events_loop = EventsLoop::new(); + let wb = WindowBuilder::new(); + + // Create surface + // TODO: Vulkan version + #[cfg(feature = "gl")] + let (mut surface, mut adapters) = { + let window = unsafe { + let builder = back::config_context(glutin::ContextBuilder::new(), ColorFormat::SELF, None); + + builder.build_windowed(wb, &events_loop) + .map_err(|_| CreationError::WindowError)? + .make_current() + }.map_err(|_| CreationError::WindowError)?; + + let surface = back::Surface::from_window(window); + let adapters = surface.enumerate_adapters(); + + (surface, adapters) + }; + + // TODO: Properly figure out which adapter to use + let mut adapter = adapters.remove(0); + + // Device & Queue group + let (mut device, queue_group) = adapter + .open_with::<_, Graphics>(1, |family| surface.supports_queue_family(family)) + .map_err(|e| CreationError::DeviceError (e))?; + + // Swapchain stuff + let (format, viewport, extent, swapchain, backbuffer) = { + let (caps, formats, present_modes) = surface.compatibility(&mut adapter.physical_device); + + let format = formats.map_or(Format::Rgba8Srgb, |formats| { + formats.iter() + .find(|format| format.base_format().1 == ChannelType::Srgb) + .map(|format| *format) + .unwrap_or(formats[0]) + }); + + let present_mode = { + use window::PresentMode::*; + [Mailbox, Fifo, Relaxed, Immediate] + .iter() + .cloned() + .find(|pm| present_modes.contains(pm)) + .ok_or(CreationError::BadSurface)? + }; + let composite_alpha = { + [CompositeAlpha::OPAQUE, CompositeAlpha::INHERIT, CompositeAlpha::PREMULTIPLIED, CompositeAlpha::POSTMULTIPLIED] + .iter() + .cloned() + .find(|ca| caps.composite_alpha.contains(*ca)) + .ok_or(CreationError::BadSurface)? + }; + + let extent = caps.extents.end; + let image_count = if present_mode == PresentMode::Mailbox { + (caps.image_count.end - 1).min(caps.image_count.start.max(3)) + } else { + (caps.image_count.end - 1).min(caps.image_count.start.max(2)) + }; + + let image_layers = 1; + let image_usage = if caps.usage.contains(image::Usage::COLOR_ATTACHMENT) { + image::Usage::COLOR_ATTACHMENT + } else { + Err(CreationError::BadSurface)? }; - let image_layers = 1; - let image_usage = if caps.usage.contains(Usage::COLOR_ATTACHMENT) { - Usage::COLOR_ATTACHMENT - } else { - Err(CreationError::NoColor)? + // Swap config + let swap_config = SwapchainConfig { + present_mode, + composite_alpha, + format, + extent, + image_count, + image_layers, + image_usage, }; - - let swapchain_config = SwapchainConfig { - present_mode, - composite_alpha, - format, - extent, - image_count, - image_layers, - image_usage, - }; - let (swapchain, backbuffer) = unsafe { - device - .create_swapchain(&mut surface, swapchain_config, None) - .map_err(|e| CreationError::SwapchainFailed { 0: e})? - }; - - (swapchain, extent, backbuffer, format, image_count as usize) - }; - - let render_area = Rect { - x: 0, y: 0, - w: extent.width as i16, - h: extent.height as i16 - }; - - // Create render pass - let render_pass = { - let color_attachment = Attachment { + // Viewport + let extent = extent.to_extent(); + let viewport = pso::Viewport { + rect: extent.rect(), + depth: 0.0..1.0 + }; + + // Swapchain + let (swapchain, backbuffer) = unsafe { + device.create_swapchain(&mut surface, swap_config, None) + .map_err(|e| CreationError::SwapchainError (e))? + }; + + (format, viewport, extent, swapchain, backbuffer) + }; + + // Renderpass + let renderpass = { + use pass::*; + use pso::PipelineStage; + use image::{Access, Layout}; + + let attachment = Attachment { format: Some(format), samples: 1, - ops: AttachmentOps { - load: AttachmentLoadOp::Clear, - store: AttachmentStoreOp::Store, - }, - stencil_ops: AttachmentOps::DONT_CARE, - layouts: Layout::Undefined..Layout::Present, + ops: AttachmentOps::new(AttachmentLoadOp::Clear, AttachmentStoreOp::Store), + stencil_ops: AttachmentOps::new(AttachmentLoadOp::DontCare, AttachmentStoreOp::DontCare), + layouts: Layout::Undefined..Layout::Present }; + let subpass = SubpassDesc { colors: &[(0, Layout::ColorAttachmentOptimal)], depth_stencil: None, inputs: &[], resolves: &[], - preserves: &[], + preserves: &[] }; - unsafe { - device - .create_render_pass(&[color_attachment], &[subpass], &[]) - .map_err(|e| CreationError::RenderPassFailed { 0: e })? - } - }; - - // Graphics pipeline - let (descriptor_set_layouts, pipeline_layout, pipeline) = RenderingContext::create_pipeline(&mut device, extent, &render_pass)?; - - // Vertex Buffer - let (mut buffer, memory, requirements) = unsafe { - let buffer = device - .create_buffer(F32_XY_TRIANGLE, hal::buffer::Usage::VERTEX) - .map_err(|e| CreationError::BufferFailed (e))?; - - let requirements = device.get_buffer_requirements(&buffer); - let memory_type_id = adapter.physical_device - .memory_properties().memory_types - .iter().enumerate() - .find(|&(id, memory_type)| { - requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(hal::memory::Properties::CPU_VISIBLE) - }) - .map(|(id, _)| MemoryTypeId(id)) - .ok_or(CreationError::NoMemory)?; - - let memory = device - .allocate_memory(memory_type_id, requirements.size) - .map_err(|e| CreationError::AllocationFailed (e))?; - - (buffer, memory, requirements) - }; + let dependency = SubpassDependency { + passes: SubpassRef::External..SubpassRef::Pass(0), + stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT..PipelineStage::COLOR_ATTACHMENT_OUTPUT, + accesses: Access::empty() + ..(Access::COLOR_ATTACHMENT_READ | Access::COLOR_ATTACHMENT_WRITE) + }; - // Make the command pool - let mut command_pool = unsafe { - device - .create_command_pool_typed(&queue_group, CommandPoolCreateFlags::RESET_INDIVIDUAL) - .map_err(|e| CreationError::CommandPoolFailed { 0: e })? + unsafe { device.create_render_pass(&[attachment], &[subpass], &[dependency]) } + .map_err(|_| CreationError::OutOfMemoryError)? }; - // Create framecells - let cells = (0..frames_in_flight) - .map(|_| { - let image_available = device.create_semaphore().map_err(|e| CreationError::SemaphoreFailed { 0: e })?; - let render_finished = device.create_semaphore().map_err(|e| CreationError::SemaphoreFailed { 0: e })?; - let frame_presented = device.create_fence(true).map_err(|e| CreationError::FenceFailed { 0: e })?; - - - let command_buffer = command_pool.acquire_command_buffer(); - - Ok(FrameCell { - command_buffer, - image_available, - render_finished, - frame_presented - }) - }) - .collect::, CreationError>>()?; - - // Create image views and framebuffers - let image_views = backbuffer.iter().map( - |image| unsafe { - device.create_image_view( - &image, - ViewKind::D2, - format, - Swizzle::NO, - SubresourceRange { - aspects: Aspects::COLOR, - levels: 0..1, - layers: 0..1, - }, - ).map_err(|e| CreationError::ImageViewFailed { 0: e }) - } - ).collect::::ImageView>, CreationError>>()?; - - let framebuffers = image_views.iter().map( - |image_view| unsafe { - device.create_framebuffer( - &render_pass, - vec![image_view], - Extent { - width: extent.width as u32, - height: extent.height as u32, - depth: 1 - } - ).map_err(|e| CreationError::FramebufferFailed { 0: e }) - } - ).collect::::Framebuffer>, CreationError>>()?; - - - Ok(RenderingContext { - instance: ManuallyDrop::new(instance), - - window, surface, - - device: ManuallyDrop::new(device), - adapter, - queue_group: ManuallyDrop::new(queue_group), - - swapchain: ManuallyDrop::new(swapchain), - render_area, format, frames_in_flight, - - framebuffers, image_views, backbuffer, - - render_pass: ManuallyDrop::new(render_pass), - cells, - - command_pool: ManuallyDrop::new(command_pool), - - current_frame: 0, - - descriptor_set_layouts, - pipeline_layout: ManuallyDrop::new(pipeline_layout), - pipeline: ManuallyDrop::new(pipeline), + // Subpass + let subpass = pass::Subpass { + index: 0, + main_pass: &renderpass + }; - buffer: ManuallyDrop::new(buffer), - memory: ManuallyDrop::new(memory), - requirements - }) + // Vertex buffer + // TODO: Proper sizing / resizing + let mut buffer = unsafe { device + .create_buffer((size_of::() * 3).try_into().unwrap(), buffer::Usage::VERTEX) } + .map_err(|e| CreationError::BufferError (e))?; + + let requirements = unsafe { device.get_buffer_requirements(&buffer) }; + let memory_type_id = adapter.physical_device + .memory_properties().memory_types + .iter().enumerate() + .find(|&(id, memory_type)| { + requirements.type_mask & (1 << id) != 0 && memory_type.properties.contains(memory::Properties::CPU_VISIBLE) + }) + .map(|(id, _)| MemoryTypeId(id)) + .ok_or(CreationError::BufferNoMemory)?; + + let memory = unsafe {device + .allocate_memory(memory_type_id, requirements.size) } + .map_err(|_| CreationError::OutOfMemoryError)?; + + unsafe { device + .bind_buffer_memory(&memory, 0, &mut buffer) } + .map_err(|_| CreationError::BufferNoMemory)?; + + // Command Pools, Buffers, imageviews, framebuffers & Sync objects + let frames_in_flight = backbuffer.len(); + let (cmd_pools, cmd_buffers, get_image, render_complete, present_complete, imageviews, framebuffers) = { + let mut cmd_pools = Vec::with_capacity(frames_in_flight); + let mut cmd_buffers = Vec::with_capacity(frames_in_flight); + let mut get_image = Vec::with_capacity(frames_in_flight); + let mut render_complete = Vec::with_capacity(frames_in_flight); + let mut present_complete = Vec::with_capacity(frames_in_flight); + let mut imageviews = Vec::with_capacity(frames_in_flight); + let mut framebuffers = Vec::with_capacity(frames_in_flight); + + for i in 0..frames_in_flight { + cmd_pools.push(ManuallyDrop::new(unsafe { + device.create_command_pool_typed(&queue_group, pool::CommandPoolCreateFlags::empty()) + }.map_err(|_| CreationError::OutOfMemoryError)?)); + + cmd_buffers.push(cmd_pools[i].acquire_command_buffer::()); + get_image.push(device.create_semaphore().map_err(|_| CreationError::SyncObjectError)?); + render_complete.push(device.create_semaphore().map_err(|_| CreationError::SyncObjectError)?); + present_complete.push(device.create_fence(true).map_err(|_| CreationError::SyncObjectError)?); + + unsafe { + imageviews.push(device.create_image_view( + &backbuffer[i], + image::ViewKind::D2, + format, + format::Swizzle::NO, + COLOR_RANGE.clone(), + ).map_err(|e| CreationError::ImageViewError (e))?); + framebuffers.push(device.create_framebuffer( + &renderpass, + Some(imageviews[i]), + extent + ).map_err(|_| CreationError::OutOfMemoryError)?); + } + } + + (cmd_pools, cmd_buffers, get_image, render_complete, present_complete, imageviews, framebuffers) + }; + + // Graphics pipeline + let (descriptor_set_layouts, pipeline_layout, pipeline) = Self::create_pipeline(&mut device, extent, &subpass)?; + + Ok(RenderingContext { + events_loop, + surface, + + device: ManuallyDrop::new(device), + queue_group, + swapchain: ManuallyDrop::new(swapchain), + viewport, + + imageviews, + framebuffers, + + renderpass: ManuallyDrop::new(renderpass), + current_frame: 0, + + get_image, + render_complete, + present_complete, + frames_in_flight, + cmd_pools, + cmd_buffers, + + descriptor_set_layouts: descriptor_set_layouts, + pipeline_layout: ManuallyDrop::new(pipeline_layout), + pipeline: ManuallyDrop::new(pipeline), + + buffer: ManuallyDrop::new(buffer), + memory: ManuallyDrop::new(memory), + requirements + }) } #[allow(clippy::type_complexity)] - fn create_pipeline(device: &mut back::Device, extent: Extent2D, render_pass: &::RenderPass) - -> Result<( - Vec<::DescriptorSetLayout>, - ::PipelineLayout, - ::GraphicsPipeline, - ), CreationError> { - - // Compile shaders - let mut compiler = shaderc::Compiler::new().ok_or(CreationError::NoShaderC)?; - - let vertex_compile_artifact = compiler - .compile_into_spirv(VERTEX_SOURCE, shaderc::ShaderKind::Vertex, "vertex.vert", "main", None) - .map_err(|e| CreationError::ShaderCError (e))?; - - let fragment_compile_artifact = compiler - .compile_into_spirv(FRAGMENT_SOURCE, shaderc::ShaderKind::Fragment, "fragment.frag", "main", None) - .map_err(|e| CreationError::ShaderCError (e))?; - - // Make into shader module - let vertex_shader_module = unsafe { - device - .create_shader_module(vertex_compile_artifact.as_binary_u8()) - .map_err(|e| CreationError::ShaderModuleFailed (e))? - }; - let fragment_shader_module = unsafe { - device - .create_shader_module(fragment_compile_artifact.as_binary_u8()) - .map_err(|e| CreationError::ShaderModuleFailed (e))? - }; - - // Specify entrypoints for each shader. - let vs_entry: EntryPoint = EntryPoint { - entry: "main", - module: &vertex_shader_module, - specialization: Specialization { - constants: Cow::Borrowed (&[]), - data: Cow::Borrowed (&[]), - } - }; - - let fs_entry: EntryPoint = EntryPoint { - entry: "main", - module: &fragment_shader_module, - specialization: Specialization { - constants: Cow::Borrowed (&[]), - data: Cow::Borrowed (&[]), + pub fn create_pipeline(device: &mut back::Device, extent: image::Extent, subpass: &pass::Subpass) -> Result< + ( + ::DescriptorSetLayout, + ::PipelineLayout, + ::GraphicsPipeline, + ), error::CreationError> { + use pso::*; + + // 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", "main", None) + .map_err(|e| error::CreationError::ShaderCError (e))?; + + let fragment_compile_artifact = compiler + .compile_into_spirv(FRAGMENT_SOURCE, shaderc::ShaderKind::Fragment, "fragment.frag", "main", None) + .map_err(|e| error::CreationError::ShaderCError (e))?; + + // Make into shader module + unsafe { + (device + .create_shader_module(vertex_compile_artifact.as_binary()) + .map_err(|e| error::CreationError::ShaderModuleFailed (e))?, + device + .create_shader_module(fragment_compile_artifact.as_binary()) + .map_err(|e| error::CreationError::ShaderModuleFailed (e))?) } - }; - - // Specify input format - let input_assembler = InputAssemblerDesc::new(hal::Primitive::TriangleList); - - // Vertex Shader I/O + }; + + // 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::() * 2) as u32, @@ -472,40 +400,42 @@ impl<'a> RenderingContext<'a> { location: 0, binding: 0, element: Element { - format: Format::Rgb32Sfloat, + format: Format::Rg32Sfloat, offset: 0, }, }]; - // Make shader set - let shaders = GraphicsShaderSet { - vertex: vs_entry, - hull: None, - domain: None, - geometry: None, - fragment: Some(fs_entry), - }; - - - // Rasterisation options - let rasterizer = Rasterizer { - depth_clamping: false, - polygon_mode: PolygonMode::Fill, - cull_face: Face::NONE, - front_face: FrontFace::Clockwise, - depth_bias: None, - conservative: false, - }; - - // Depth testing options + // Rasterizer + let rasterizer = Rasterizer { + polygon_mode: PolygonMode::Fill, + cull_face: Face::NONE, + front_face: FrontFace::Clockwise, + depth_clamping: false, + depth_bias: None, + conservative: true + }; + + // Depth stencil let depth_stencil = DepthStencilDesc { depth: DepthTest::Off, depth_bounds: false, stencil: StencilTest::Off, }; - // Colour blending options - // Only takes the source value + // Descriptor set layout + let set_layout = unsafe { + device.create_descriptor_set_layout( + &[], + &[], + ) + }.map_err(|_| error::CreationError::OutOfMemoryError)?; + + // Pipeline layout + let layout = unsafe { + device.create_pipeline_layout(std::iter::once(&set_layout), &[]) + }.map_err(|_| error::CreationError::OutOfMemoryError)?; + + // Colour blending let blender = { let blend_state = BlendState::On { color: BlendOp::Add { @@ -517,284 +447,252 @@ impl<'a> RenderingContext<'a> { dst: Factor::Zero, }, }; + BlendDesc { logic_op: Some(LogicOp::Copy), targets: vec![ColorBlendDesc(ColorMask::ALL, blend_state)], } }; - // Viewport, scissor, options + // Baked states let baked_states = BakedStates { viewport: Some(Viewport { - rect: extent.to_extent().rect(), - depth: (0.0..1.0), + rect: extent.rect(), + depth: (0.0..1.0) }), - scissor: Some(extent.to_extent().rect()), + scissor: Some(extent.rect()), blend_color: None, depth_bounds: None, }; - // Non-Buffer data sources (none right now) - let bindings = Vec::::new(); - let immutable_samplers = Vec::<::Sampler>::new(); - let descriptor_set_layouts: Vec<::DescriptorSetLayout> = vec![unsafe { - device - .create_descriptor_set_layout(bindings, immutable_samplers) - .map_err(|e| CreationError::DescriptorSetLayoutFailed (e))? - }]; - let push_constants = Vec::<(ShaderStageFlags, core::ops::Range)>::new(); - let layout = unsafe { - device - .create_pipeline_layout(&descriptor_set_layouts, push_constants) - .map_err(|e| CreationError::PipelineLayoutFailed (e))? - }; - - // Create the actual pipeline - let gfx_pipeline = { - let desc = GraphicsPipelineDesc { - shaders, - rasterizer, - vertex_buffers, - attributes, - input_assembler, - blender, - depth_stencil, - multisampling: None, - baked_states, - layout: &layout, - subpass: Subpass { - index: 0, - main_pass: render_pass, - }, - flags: PipelineCreationFlags::empty(), - parent: BasePipeline::None, - }; - - unsafe { - device.create_graphics_pipeline(&desc, None) - .map_err(|e| CreationError::PipelineFailed (e))? - } - }; - - // TODO: Destroy shader modules - unsafe { - device.destroy_shader_module(vertex_shader_module); - device.destroy_shader_module(fragment_shader_module); - } - - Ok((descriptor_set_layouts, layout, gfx_pipeline)) - } - - /// Internal function. Gets the index of the next image from the swapchain to use & resets the frame_presented fence. - fn get_image(swapchain: &mut ::Swapchain, device: &mut ::Device, cell: &FrameCell) -> Result { - // Get the image of the swapchain to present to. - let (i, _) = unsafe { - swapchain - .acquire_image(core::u64::MAX, Some(&cell.image_available), None) - .map_err(|e| FrameError::AcquisitionError { 0: e })? - }; - let i = i as usize; - - // Make sure frame has been presented since whenever it was last drawn. - unsafe { - device - .wait_for_fence(&cell.frame_presented, core::u64::MAX) - .map_err(|e| FrameError::FenceWaitError { 0: e })?; - device - .reset_fence(&cell.frame_presented) - .map_err(|e| FrameError::FenceResetError { 0: e })?; - } - - Ok(i) + // 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: *subpass, + flags: pso::PipelineCreationFlags::empty(), + parent: pso::BasePipeline::None, + input_assembler, + attributes + }; + + // Pipeline + let pipeline = unsafe { + device.create_graphics_pipeline(&pipeline_desc, None) + }.map_err(|e| error::CreationError::PipelineError (e))?; + + Ok((set_layout, layout, pipeline)) } - /// Internal function. Prepares a submission for a frame. - fn prep_submission(cell: &FrameCell) - -> Submission; 1]>, - ArrayVec<[(&::Semaphore, hal::pso::PipelineStage); 1]>, - ArrayVec<[&::Semaphore; 1]>> { - let command_buffers: ArrayVec<[_; 1]> = [&cell.command_buffer].into(); - - let wait_semaphores: ArrayVec<[_; 1]> = [(&cell.image_available, PipelineStage::COLOR_ATTACHMENT_OUTPUT)].into(); - let signal_semaphores: ArrayVec<[_; 1]> = [&cell.render_finished].into(); - - Submission { - command_buffers, - wait_semaphores, - signal_semaphores, - } - } - - /// Draw a frame of color - pub fn draw_clear(&mut self, color: [f32; 4]) -> Result<(), FrameError> { - // Advance the frame before early outs to prevent fuckery. - self.current_frame = (self.current_frame + 1) % self.frames_in_flight; - - let i = RenderingContext::get_image(&mut self.swapchain, &mut self.device, &self.cells[self.current_frame])?; - - let cell = &mut self.cells[self.current_frame]; - - // Record commands. - unsafe { - cell.command_buffer.begin(); - - let clear_values = [ClearValue::Color(ClearColor::Float(color))]; - cell.command_buffer.begin_render_pass_inline( - &self.render_pass, - &self.framebuffers[i], - self.render_area, - clear_values.iter(), - ); - cell.command_buffer.finish(); - }; - - - // Prepare submission - let submission = RenderingContext::prep_submission(&cell); - - // Submit it for rendering and presentation. - let command_queue = &mut self.queue_group.queues[0]; - - let present_wait_semaphores: ArrayVec<[_; 1]> = [&cell.render_finished].into(); - - unsafe { - command_queue.submit(submission, Some(&cell.frame_presented)); - self.swapchain - .present(command_queue, i as u32, present_wait_semaphores) - .map_err(|e| FrameError::PresentError { 0: e })? - }; - - Ok(()) + /// Draw a frame that's just cleared to the color specified. + pub fn draw_clear(&mut self, color: [f32; 4]) -> Result<(), FrameError> { + let get_image = &self.get_image[self.current_frame]; + let render_complete = &self.render_complete[self.current_frame]; + + // Advance the frame _before_ we start using the `?` operator + self.current_frame = (self.current_frame + 1) % self.frames_in_flight; + + // Get the image + let (image_index, _) = unsafe { + self + .swapchain + .acquire_image(core::u64::MAX, Some(get_image), None) + .map_err(|e| FrameError::AcquireError (e))? + }; + let image_index = image_index as usize; + + // Make sure whatever was last using this has finished + let present_complete = &self.present_complete[image_index]; + unsafe { + self.device + .wait_for_fence(present_complete, core::u64::MAX) + .map_err(|_| FrameError::SyncObjectError)?; + self.device + .reset_fence(present_complete) + .map_err(|_| FrameError::SyncObjectError)?; + }; + + // Record commands + unsafe { + let buffer = &mut self.cmd_buffers[image_index]; + let clear_values = [command::ClearValue::Color(command::ClearColor::Sfloat(color))]; + + buffer.begin(false); + buffer.begin_render_pass_inline( + &self.renderpass, + &self.framebuffers[image_index], + self.viewport.rect, + clear_values.iter(), + ); + buffer.finish(); + }; + + // Make submission object + let command_buffers = &self.cmd_buffers[image_index..=image_index]; + let wait_semaphores: ArrayVec<[_; 1]> = [(get_image, pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT)].into(); + let signal_semaphores: ArrayVec<[_; 1]> = [render_complete].into(); + + let present_wait_semaphores: ArrayVec<[_; 1]> = [render_complete].into(); + + let submission = Submission { + command_buffers, + wait_semaphores, + signal_semaphores, + }; + + // Submit it + let command_queue = &mut self.queue_group.queues[0]; + unsafe { + command_queue.submit(submission, Some(present_complete)); + self.swapchain + .present(command_queue, image_index as u32, present_wait_semaphores) + .map_err(|_| FrameError::PresentError)? + }; + + Ok(()) } - /// Draw a single triangle as a frame. - pub fn draw_triangle(&mut self, triangle: Tri2) -> Result<(), FrameError> { - // Advance the frame before early outs to prevent fuckery. - self.current_frame = (self.current_frame + 1) % self.frames_in_flight; - - // Get the image - let i = RenderingContext::get_image(&mut self.swapchain, &mut self.device, &self.cells[self.current_frame])?; - - let cell = &mut self.cells[self.current_frame]; - - // Write the vertex data to the buffer. - unsafe { + pub fn draw_triangle(&mut self, tris: [Tri2; 3]) -> Result<(), &'static str> { + let get_image = &self.get_image[self.current_frame]; + let render_complete = &self.render_complete[self.current_frame]; + + // Advance the frame _before_ we start using the `?` operator + self.current_frame = (self.current_frame + 1) % self.frames_in_flight; + + // Get the image + let (image_index, _) = unsafe { + self + .swapchain + .acquire_image(core::u64::MAX, Some(get_image), None) + .map_err(|_| "FrameError::AcquireError")? + }; + let image_index = image_index as usize; + + // Make sure whatever was last using this has finished + let present_complete = &self.present_complete[image_index]; + unsafe { + self.device + .wait_for_fence(present_complete, core::u64::MAX) + .map_err(|_| "FrameError::SyncObjectError")?; + self.device + .reset_fence(present_complete) + .map_err(|_| "FrameError::SyncObjectError")?; + }; + + // Write triangle to the vertex buffer + unsafe { let mut data_target = self.device .acquire_mapping_writer(&self.memory, 0..self.requirements.size) - .map_err(|e| FrameError::BufferError (e))?; - - let points: [f32; 6] = triangle.into(); - data_target[..6].copy_from_slice(&points); - + .map_err(|_| "Failed to acquire a memory writer!")?; + + for (i,tri) in tris.into_iter().enumerate() { + let points: [f32; 6] = (*tri).into(); + data_target[i * 6..(i * 6) + 6].copy_from_slice(&points); + } + self .device .release_mapping_writer(data_target) - .map_err(|e| FrameError::BufferError (hal::mapping::Error::OutOfMemory(e)))?; - } - - // Record commands. - unsafe { - - const TRIANGLE_CLEAR: [ClearValue; 1] = [ClearValue::Color(ClearColor::Float([0.1, 0.2, 0.3, 1.0]))]; - - cell.command_buffer.begin(); - - { - let mut encoder = cell.command_buffer.begin_render_pass_inline( - &self.render_pass, - &self.framebuffers[i], - self.render_area, - TRIANGLE_CLEAR.iter(), + .map_err(|_| "Couldn't release the mapping writer!")?; + } + + // Record commands + unsafe { + let buffer = &mut self.cmd_buffers[image_index]; + let clear_values = [command::ClearValue::Color(command::ClearColor::Sfloat([0.0, 0.0, 0.0, 1.0]))]; + + buffer.begin(false); + { + let mut encoder = buffer.begin_render_pass_inline( + &self.renderpass, + &self.framebuffers[image_index], + self.viewport.rect, + clear_values.iter(), ); encoder.bind_graphics_pipeline(&self.pipeline); + // Here we must force the Deref impl of ManuallyDrop to play nice. let buffer_ref: &::Buffer = &self.buffer; let buffers: ArrayVec<[_; 1]> = [(buffer_ref, 0)].into(); - + encoder.bind_vertex_buffers(0, buffers); - encoder.draw(0..3, 0..1); + encoder.draw(0..18, 0..3); } - cell.command_buffer.finish(); - } - - - // Prepare submission - let submission = RenderingContext::prep_submission(&cell); - - // Submit it for rendering and presentation. - let command_queue = &mut self.queue_group.queues[0]; - - let present_wait_semaphores: ArrayVec<[_; 1]> = [&cell.render_finished].into(); - - unsafe { - command_queue.submit(submission, Some(&cell.frame_presented)); - self.swapchain - .present(command_queue, i as u32, present_wait_semaphores) - .map_err(|e| FrameError::PresentError { 0: e })?; - println!("presented"); - }; - Ok(()) + buffer.finish(); + }; + + // Make submission object + let command_buffers = &self.cmd_buffers[image_index..=image_index]; + let wait_semaphores: ArrayVec<[_; 1]> = [(get_image, pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT)].into(); + let signal_semaphores: ArrayVec<[_; 1]> = [render_complete].into(); + + let present_wait_semaphores: ArrayVec<[_; 1]> = [render_complete].into(); + + let submission = Submission { + command_buffers, + wait_semaphores, + signal_semaphores, + }; + + // Submit it + let command_queue = &mut self.queue_group.queues[0]; + unsafe { + command_queue.submit(submission, Some(present_complete)); + self.swapchain + .present(command_queue, image_index as u32, present_wait_semaphores) + .map_err(|_| "FrameError::PresentError")? + }; + + Ok(()) } } -/// Properly destroys all the vulkan objects we have. -impl<'a> std::ops::Drop for RenderingContext<'a> { - fn drop(&mut self) { - use core::ptr::read; - let _ = self.device.wait_idle(); - - unsafe { - // cells (semaphores, fences & command buffers) - for cell in self.cells.drain(..) { - cell.destroy(&self.device, &mut self.command_pool); - } - - // images - for image in self.backbuffer.drain(..) { - self.device.destroy_image(image); - } - - // image views - for image_view in self.image_views.drain(..) { - self.device.destroy_image_view(image_view); - } - - // framebuffers - for framebuffer in self.framebuffers.drain(..) { - self.device.destroy_framebuffer(framebuffer); - } +#[cfg(feature = "gl")] +pub struct WindowWrapper<'a> (&'a back::glutin::Window); - for descriptor_set in self.descriptor_set_layouts.drain(..) { - self.device.destroy_descriptor_set_layout(descriptor_set); - } - - // buffer - self.device.destroy_buffer(ManuallyDrop::into_inner(read(&self.buffer))); - self.device.free_memory(ManuallyDrop::into_inner(read(&self.memory))); - - // graphics pipeline - self.device - .destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); - - self.device - .destroy_graphics_pipeline(ManuallyDrop::into_inner(read(&self.pipeline))); - - - // command pool - self.device - .destroy_command_pool(ManuallyDrop::into_inner(read(&self.command_pool)).into_raw()); - - // render pass - self.device - .destroy_render_pass(ManuallyDrop::into_inner(read(&self.render_pass))); - - // swapchain - self.device - .destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain))); +impl core::ops::Drop for RenderingContext { + fn drop(&mut self) { + // TODO: Probably missing some destroy stuff + self.device.wait_idle().unwrap(); - ManuallyDrop::drop(&mut self.queue_group); - ManuallyDrop::drop(&mut self.device); - ManuallyDrop::drop(&mut self.instance); + unsafe { + for fence in self.present_complete.drain(..) { + self.device.destroy_fence(fence) + } + for semaphore in self.render_complete.drain(..) { + self.device.destroy_semaphore(semaphore) + } + for semaphore in self.get_image.drain(..) { + self.device.destroy_semaphore(semaphore) + } + for framebuffer in self.framebuffers.drain(..) { + self.device.destroy_framebuffer(framebuffer); + } + for image_view in self.imageviews.drain(..) { + self.device.destroy_image_view(image_view); + } + + use core::ptr::read; + for cmd_pool in self.cmd_pools.drain(..) { + self.device.destroy_command_pool( + ManuallyDrop::into_inner(cmd_pool).into_raw(), + ); + } + self.device + .destroy_render_pass(ManuallyDrop::into_inner(read(&self.renderpass))); + self.device + .destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain))); + + ManuallyDrop::drop(&mut self.device); } } } \ No newline at end of file diff --git a/stockton-render/src/draw/data/stockton.frag b/stockton-render/src/draw/data/stockton.frag new file mode 100644 index 0000000..032ff67 --- /dev/null +++ b/stockton-render/src/draw/data/stockton.frag @@ -0,0 +1,8 @@ +#version 450 + +layout(location = 0) out vec4 color; + +void main() +{ + color = vec4(1.0); +} \ No newline at end of file diff --git a/stockton-render/src/draw/data/stockton.vert b/stockton-render/src/draw/data/stockton.vert new file mode 100644 index 0000000..e0aea99 --- /dev/null +++ b/stockton-render/src/draw/data/stockton.vert @@ -0,0 +1,12 @@ +#version 450 + +layout (location = 0) in vec2 position; + +out gl_PerVertex { + vec4 gl_Position; +}; + +void main() +{ + gl_Position = vec4(position, 0.0, 1.0); +} \ No newline at end of file diff --git a/stockton-render/src/draw/frame.rs b/stockton-render/src/draw/frame.rs deleted file mode 100644 index d1e617c..0000000 --- a/stockton-render/src/draw/frame.rs +++ /dev/null @@ -1,51 +0,0 @@ -// 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 . - -//! Helper struct. Keeps the data for each frame 'in flight' seperate instead of linked lists. -use hal::{Device as DeviceTrait}; -use hal::command::CommandBuffer; -use hal::Graphics; -use back::{Backend}; -use hal::pool::{CommandPool}; - - -/// Helper struct for a frame that can be in flight -pub struct FrameCell { - /// How we ask the GPU to do work for us. - pub command_buffer: CommandBuffer, - - /// Signalled once an image is acquired to draw on. - pub image_available: ::Semaphore, - - /// Signalled once the frame is done being drawn. - pub render_finished: ::Semaphore, - - /// Signalled once the frame is presented. - pub frame_presented: ::Fence -} - -impl FrameCell { - /// Safely deinitialises all the objects in this struct. - /// Use this instead of drop. - pub unsafe fn destroy(self, device: &::Device, command_pool: &mut CommandPool) { - // fences & semaphores - device.destroy_semaphore(self.image_available); - device.destroy_semaphore(self.render_finished); - device.destroy_fence(self.frame_presented); - - // command buffer - command_pool.free(vec![self.command_buffer]); - } -} \ No newline at end of file diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs index 16bfd29..bdfd3c8 100644 --- a/stockton-render/src/draw/mod.rs +++ b/stockton-render/src/draw/mod.rs @@ -16,7 +16,6 @@ //! Given 3D points and some camera information, renders to the screen. mod context; -mod frame; pub use context::RenderingContext; pub use context::Tri2; \ No newline at end of file -- cgit v1.2.3