From b31035bf03e0843fb516bee065f9a010424ba546 Mon Sep 17 00:00:00 2001 From: tcmal Date: Sun, 25 Aug 2024 17:44:20 +0100 Subject: refactor(render): update gfx-hal and use staging and index buffers. also some minor changes to types because of deprecation and a bunch of readability improvements --- Cargo.toml | 2 +- README.md | 20 +- examples/render-quad/Cargo.toml | 14 + examples/render-quad/README.md | 3 + examples/render-quad/src/main.rs | 87 +++ examples/render-triangles/Cargo.toml | 14 - examples/render-triangles/README.md | 3 - examples/render-triangles/src/main.rs | 80 --- stockton-render/Cargo.toml | 3 +- stockton-render/src/draw/buffer.rs | 158 +++-- stockton-render/src/draw/context.rs | 1092 +++++++++++++++++---------------- stockton-render/src/draw/mod.rs | 2 +- stockton-render/src/error.rs | 1 - stockton-render/src/lib.rs | 42 +- stockton-render/src/types.rs | 36 ++ stockton-types/src/entity_store.rs | 12 +- stockton-types/src/world.rs | 4 +- 17 files changed, 828 insertions(+), 745 deletions(-) create mode 100644 examples/render-quad/Cargo.toml create mode 100644 examples/render-quad/README.md create mode 100644 examples/render-quad/src/main.rs delete mode 100644 examples/render-triangles/Cargo.toml delete mode 100644 examples/render-triangles/README.md delete mode 100644 examples/render-triangles/src/main.rs create mode 100644 stockton-render/src/types.rs diff --git a/Cargo.toml b/Cargo.toml index 10bd505..75e03a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,5 @@ members = [ "stockton-types", "stockton-render", - "examples/render-triangles" + "examples/render-quad" ] \ No newline at end of file diff --git a/README.md b/README.md index 1b64e05..47c6725 100644 --- a/README.md +++ b/README.md @@ -2,25 +2,7 @@ [![Build Status](https://travis-ci.org/tcmal/stockton.svg?branch=master)](https://travis-ci.org/tcmal/stockton) -A 3D engine inspired by quake. - -Most of what is described below isn't fully done, or even started. - -## Developing games - -Maps currently use the regular Q3 `.bsp` format, with each type of entity needing to be defined as a type implementing the `Entity` trait, through which it recieves events. You'll also need some sort of a `TextureStore` which finds the textures needed and converts them into a usable format. A lot of this is helped by `stockton-glue` - -## Internal Structure - -`bsp` is a library for parsing `.bsp` files to nice data structures. It can be found [here](https://github.com/tcmal/rust-bsp) - -`stockton-types` contains shared types & macros used by all the other crates, for example the world, entities, and other important things. - -`stockton-simulate` makes the world living, including collision detection, propagating events to entities and game state. - -`stockton-render` renders the world to a given surface, using `gfx` and `nalgebra`. - -`stockton-glue` helps you glue these together into an actual executable game. +A 3D engine. ## License diff --git a/examples/render-quad/Cargo.toml b/examples/render-quad/Cargo.toml new file mode 100644 index 0000000..6f75543 --- /dev/null +++ b/examples/render-quad/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "render-triangles" +version = "0.1.0" +authors = ["Oscar "] + +[dependencies] +stockton-render = { path = "../../stockton-render", features = ["vulkan"] } +stockton-types = { path = "../../stockton-types" } +stockton-bsp = "2.0.0" +winit = "^0.21" +log = "0.4.0" +simple_logger = "1.0" +rand = "0.7" + diff --git a/examples/render-quad/README.md b/examples/render-quad/README.md new file mode 100644 index 0000000..c129ff6 --- /dev/null +++ b/examples/render-quad/README.md @@ -0,0 +1,3 @@ +# render-quad + +Renders a rainbow quad. The top-left corner follows your mouse. \ No newline at end of file diff --git a/examples/render-quad/src/main.rs b/examples/render-quad/src/main.rs new file mode 100644 index 0000000..d0993ea --- /dev/null +++ b/examples/render-quad/src/main.rs @@ -0,0 +1,87 @@ +// Copyright (C) Oscar Shrimpton 2019 + +// 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 . + +//! Renders ./example.bsp + +extern crate stockton_types; +extern crate stockton_bsp; +extern crate stockton_render; +extern crate winit; +extern crate simple_logger; +extern crate rand; + +use stockton_render::draw::{RenderingContext, UVPoint}; +use stockton_types::{Vector2, Vector3}; + +use winit::{ + event::{Event, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, + window::WindowBuilder +}; + +fn main() { + + simple_logger::init().unwrap(); + + // Create the renderer. + + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + let mut ctx = RenderingContext::new(&window).unwrap(); + + ctx.vert_buffer[0] = UVPoint(Vector2::new(-0.5, -0.5), Vector3::new(1.0, 0.0, 0.0)); + ctx.vert_buffer[1] = UVPoint(Vector2::new(0.5, -0.5), Vector3::new(0.0, 1.0, 0.0)); + ctx.vert_buffer[2] = UVPoint(Vector2::new(0.5, 0.5), Vector3::new(0.0, 0.0, 1.0)); + ctx.vert_buffer[3] = UVPoint(Vector2::new(-0.5, 0.5), Vector3::new(1.0, 0.0, 1.0)); + + ctx.index_buffer[0] = (0, 1, 2); + ctx.index_buffer[1] = (0, 2, 3); + + event_loop.run(move |event, _, flow| { + *flow = ControlFlow::Poll; + + match event { + // TODO: Handle resize + Event::WindowEvent { + event: WindowEvent::CloseRequested, + .. + } => { + *flow = ControlFlow::Exit + }, + + Event::WindowEvent { + event: WindowEvent::CursorMoved { + position, + .. + }, + .. + } => { + let win_size = window.inner_size(); + let mouse_x: f32 = ((position.x / win_size.width as f64) * 2.0 - 1.0) as f32; + let mouse_y: f32 = ((position.y / win_size.height as f64) * 2.0 - 1.0) as f32; + + ctx.vert_buffer[0] = UVPoint(Vector2::new(mouse_x, mouse_y), Vector3::new(1.0, 0.0, 0.0)); + } + + Event::MainEventsCleared => { + window.request_redraw() + }, + Event::RedrawRequested(_) => { + ctx.draw_vertices().unwrap(); + } + _ => () + } + }); +} diff --git a/examples/render-triangles/Cargo.toml b/examples/render-triangles/Cargo.toml deleted file mode 100644 index 0b47393..0000000 --- a/examples/render-triangles/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "render-triangles" -version = "0.1.0" -authors = ["Oscar "] - -[dependencies] -stockton-render = { path = "../../stockton-render", features = ["vulkan"] } -stockton-types = { path = "../../stockton-types" } -stockton-bsp = "2.0.0" -winit = "0.19.1" -log = "0.4.0" -simple_logger = "1.0" -rand = "0.7" - diff --git a/examples/render-triangles/README.md b/examples/render-triangles/README.md deleted file mode 100644 index a048662..0000000 --- a/examples/render-triangles/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# render-triangles - -Renders rainbow triangles. Space adds a new random triangle, Escape exits. \ No newline at end of file diff --git a/examples/render-triangles/src/main.rs b/examples/render-triangles/src/main.rs deleted file mode 100644 index 63c589b..0000000 --- a/examples/render-triangles/src/main.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (C) Oscar Shrimpton 2019 - -// 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 . - -//! Renders ./example.bsp - -extern crate stockton_types; -extern crate stockton_bsp; -extern crate stockton_render; -extern crate winit; -extern crate simple_logger; -extern crate rand; - -use stockton_render::draw::{RenderingContext, Tri2}; -use stockton_types::Vector2; - -use winit::{Event, WindowEvent, VirtualKeyCode, ElementState}; -use rand::prelude::*; - -fn main() { - - simple_logger::init().unwrap(); - - // Create the renderer. - let mut ctx = RenderingContext::new().unwrap(); - let mut rng = thread_rng(); - let mut running = true; - let mut add_tri = false; - - while running { - ctx.events_loop.poll_events(|event| { - match event { - // TODO: Handle resize - Event::WindowEvent { - event: WindowEvent::KeyboardInput { input, .. }, - .. - } => match input.state { - ElementState::Released => match input.virtual_keycode { - Some(VirtualKeyCode::Escape) => running = false, - Some(VirtualKeyCode::Space) => add_tri = true, - _ => () - }, - _ => () - } - _ => () - } - }); - - if add_tri { - ctx.add_map_vert(Tri2 ([ - Vector2::new( - rng.gen_range(-1.0, 1.0), - rng.gen_range(-1.0, 1.0), - ), - Vector2::new( - rng.gen_range(-1.0, 1.0), - rng.gen_range(-1.0, 1.0), - ), - Vector2::new( - rng.gen_range(-1.0, 1.0), - rng.gen_range(-1.0, 1.0), - ) - ])).unwrap(); - add_tri = false; - } - - ctx.draw_vertices().unwrap(); - } -} diff --git a/stockton-render/Cargo.toml b/stockton-render/Cargo.toml index eb717c4..d207159 100644 --- a/stockton-render/Cargo.toml +++ b/stockton-render/Cargo.toml @@ -5,7 +5,7 @@ authors = ["Oscar "] [dependencies] stockton-types = { path = "../stockton-types" } -winit = "0.19.1" +winit = "^0.21" gfx-hal = "^0.5" arrayvec = "0.4.10" nalgebra-glm = "0.4.0" @@ -18,4 +18,5 @@ vulkan = ["gfx-backend-vulkan"] [dependencies.gfx-backend-vulkan] version = "^0.5" +features = ["x11"] optional = true diff --git a/stockton-render/src/draw/buffer.rs b/stockton-render/src/draw/buffer.rs index d29f857..f420fc7 100644 --- a/stockton-render/src/draw/buffer.rs +++ b/stockton-render/src/draw/buffer.rs @@ -13,26 +13,28 @@ // You should have received a copy of the GNU General Public License along // with this program. If not, see . -use std::marker::PhantomData; -use std::ops::{Index, IndexMut, Range}; +use std::ops::{Index, IndexMut}; use std::convert::TryInto; use core::mem::{ManuallyDrop, size_of}; -use hal::memory::{Properties, Requirements, Segment}; -use hal::buffer::Usage; -use hal::adapter::{Adapter, MemoryType, PhysicalDevice}; -use hal::device::Device; -use hal::{MemoryTypeId, VertexCount, InstanceCount}; -use hal::Backend; + +use hal::prelude::*; +use hal::{ + MemoryTypeId, + buffer::Usage, + memory::{Properties, Segment}, + queue::Submission +}; + use crate::error::CreationError; -use super::RenderingContext; +use crate::types::*; // TODO: Proper sizing of buffers -const BUF_SIZE: u64 = 32; +const BUF_SIZE: u64 = 4 * 15 * 2; -fn create_buffer(device: &mut ::Device, - adapter: &Adapter, +fn create_buffer(device: &mut Device, + adapter: &Adapter, usage: Usage, - properties: Properties) -> Result<(::Buffer, ::Memory), CreationError> { + properties: Properties) -> Result<(Buffer, Memory), CreationError> { let mut buffer = unsafe { device .create_buffer(BUF_SIZE, usage) } .map_err(|e| CreationError::BufferError (e))?; @@ -59,66 +61,131 @@ fn create_buffer(device: &mut ::Device, )) } -trait ModifiableBuffer: IndexMut { - fn commit<'a>(&'a self) -> &'a ::Buffer; +pub trait ModifiableBuffer: IndexMut { + fn commit<'a>(&'a mut self, device: &Device, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool) -> &'a Buffer; } -pub struct StagedBuffer<'a> { - stagedBuffer: ManuallyDrop<::Buffer>, - stagedMemory: ManuallyDrop<::Memory>, - buffer: ManuallyDrop<::Buffer>, - memory: ManuallyDrop<::Memory>, - mappedStaged: &'a mut [f32], - stagedIsDirty: bool +pub struct StagedBuffer<'a, T: Sized> { + staged_buffer: ManuallyDrop, + staged_memory: ManuallyDrop, + buffer: ManuallyDrop, + memory: ManuallyDrop, + staged_mapped_memory: &'a mut [T], + staged_is_dirty: bool, } -impl<'a> ModifiableBuffer for StagedBuffer<'a> { - fn new(device: &mut ::Device, adapter: &Adapter, usage: Usage) -> Result { +impl<'a, T: Sized> StagedBuffer<'a, T> { + pub fn new(device: &mut Device, adapter: &Adapter, usage: Usage) -> Result { - let (stagedBuffer, stagedMemory) = create_buffer(device, adapter, Usage::TRANSFER_SRC, Properties::CPU_VISIBLE)?; + let (staged_buffer, staged_memory) = create_buffer(device, adapter, Usage::TRANSFER_SRC, Properties::CPU_VISIBLE)?; let (buffer, memory) = create_buffer(device, adapter, Usage::TRANSFER_DST | usage, Properties::DEVICE_LOCAL)?; // Map it somewhere and get a slice to that memory - let rawPtr = unsafe { - device.map_memory(&stagedMemory, Segment::ALL).unwrap() // TODO + let staged_mapped_memory = unsafe { + let ptr = device.map_memory(&staged_memory, Segment::ALL).unwrap(); + + let slice_size: usize = (BUF_SIZE / size_of::() as u64).try_into().unwrap(); // size in f32s + + std::slice::from_raw_parts_mut(ptr as *mut T, slice_size) }; - let sliceSize: usize = (BUF_SIZE / 4).try_into().unwrap(); // size in f32s - let mappedStaged: &'a mut [f32] = std::slice::from_raw_parts_mut(rawPtr as *mut f32, sliceSize); Ok(StagedBuffer { - stagedBuffer: ManuallyDrop::new(stagedBuffer), - stagedMemory: ManuallyDrop::new(stagedMemory), + staged_buffer: ManuallyDrop::new(staged_buffer), + staged_memory: ManuallyDrop::new(staged_memory), buffer: ManuallyDrop::new(buffer), memory: ManuallyDrop::new(memory), - mappedStaged: mappedStaged, - stagedIsDirty: false + staged_mapped_memory, + staged_is_dirty: false }) } + + pub(crate) fn deactivate(mut self, device: &mut Device) { + unsafe { + device.unmap_memory(&self.staged_memory); + + device.free_memory(ManuallyDrop::take(&mut self.staged_memory)); + device.destroy_buffer(ManuallyDrop::take(&mut self.staged_buffer)); + + device.free_memory(ManuallyDrop::take(&mut self.memory)); + device.destroy_buffer(ManuallyDrop::take(&mut self.buffer)); + }; + } +} + +impl <'a, T: Sized> ModifiableBuffer for StagedBuffer<'a, T> { + fn commit<'b>(&'b mut self, device: &Device, + command_queue: &mut CommandQueue, + command_pool: &mut CommandPool) -> &'b Buffer { + if self.staged_is_dirty { + // Copy from staged to buffer + + let buf = unsafe { + use hal::command::{CommandBufferFlags, BufferCopy}; + // Get a command buffer + let mut buf = command_pool.allocate_one(hal::command::Level::Primary); + + // Put in our copy command + buf.begin_primary(CommandBufferFlags::ONE_TIME_SUBMIT); + buf.copy_buffer(&self.staged_buffer, &self.buffer, &[ + BufferCopy { + src: 0, + dst: 0, + size: BUF_SIZE // TODO + } + ]); + buf.finish(); + + buf + }; + + // Submit it and wait for completion + // TODO: We could use more semaphores or something? + // TODO: Better error handling + unsafe { + let copy_finished = device.create_fence(false).unwrap(); + command_queue.submit::<_, _, Semaphore, _, _>(Submission { + command_buffers: &[&buf], + wait_semaphores: std::iter::empty::<_>(), + signal_semaphores: std::iter::empty::<_>() + }, Some(©_finished)); + + device + .wait_for_fence(©_finished, core::u64::MAX).unwrap(); + device.destroy_fence(copy_finished); + } + + self.staged_is_dirty = false; + } + + &self.buffer + } } -impl<'a> Index for StagedBuffer<'a> { - type Output = f32; +impl<'a, T: Sized> Index for StagedBuffer<'a, T> { + type Output = T; fn index(&self, index: usize) -> &Self::Output { - &self.mappedStaged[index] + &self.staged_mapped_memory[index] } } -impl<'a> IndexMut for StagedBuffer<'a> { +impl<'a, T: Sized> IndexMut for StagedBuffer<'a, T> { fn index_mut(&mut self, index: usize) -> &mut Self::Output { - self.stagedIsDirty = true; - &mut self.mappedStaged[index] + self.staged_is_dirty = true; + &mut self.staged_mapped_memory[index] } } // trait VertexLump { -// pub fn new(device: &mut ::Device, adapter: &Adapter) -> Result { +// pub fn new(device: &mut Device, adapter: &Adapter) -> Result { // } // pub(crate) struct VertexLump, X: Pod> { -// pub (crate) buffer: ManuallyDrop<::Buffer>, -// memory: ManuallyDrop<::Memory>, +// pub (crate) buffer: ManuallyDrop, +// memory: ManuallyDrop, // requirements: Requirements, // unit_size_bytes: u64, @@ -141,7 +208,7 @@ impl<'a> IndexMut for StagedBuffer<'a> { // const BATCH_SIZE: u64 = 3; // impl, X: Pod> VertexLump { -// pub fn new(device: &mut ::Device, adapter: &Adapter) -> Result, CreationError> { +// pub fn new(device: &mut Device, adapter: &Adapter) -> Result, CreationError> { // let unit_size_bytes = size_of::() as u64; // let unit_size_verts = unit_size_bytes / size_of::() as u64; @@ -286,10 +353,5 @@ impl<'a> IndexMut for StagedBuffer<'a> { // }) // } -// pub(crate) fn deactivate(&mut self, ctx: &mut RenderingContext) { -// unsafe { ctx.device.free_memory(ManuallyDrop::take(&mut self.memory)) }; -// unsafe { ctx.device.destroy_buffer(ManuallyDrop::take(&mut self.buffer)) }; -// self.active = false; -// } // } diff --git a/stockton-render/src/draw/context.rs b/stockton-render/src/draw/context.rs index 53f92cf..7a20651 100644 --- a/stockton-render/src/draw/context.rs +++ b/stockton-render/src/draw/context.rs @@ -14,239 +14,210 @@ // with this program. If not, see . //! Deals with all the Vulkan/HAL details. -use crate::error as error; -use crate::error::{CreationError, FrameError}; +//! In the end, this takes in vertices and renders them to a window. +//! You'll need something else to actually generate the vertices though. use std::mem::{ManuallyDrop, size_of}; -use std::convert::TryInto; - -use winit::{EventsLoop, WindowBuilder}; - +use winit::window::Window; use arrayvec::ArrayVec; -use hal::*; -use hal::device::Device; -use hal::format::{AsFormat, Rgba8Srgb as ColorFormat, Format, ChannelType}; -use hal::pool::CommandPool; -use hal::queue::{QueueGroup, Submission}; -use hal::window::SwapchainConfig; - -use hal::Instance as InstanceTrait; - -#[cfg(feature = "gl")] -use back::glutin as glutin; - -use stockton_types::Vector2; -use super::buffer::StagedBuffer; +use hal::{ + prelude::*, + queue::{Submission}, + window::SwapchainConfig +}; +use stockton_types::{Vector2, Vector3}; -type ModifiableBuffer<'a> = StagedBuffer<'a>; +use crate::types::*; +use crate::error; +use super::buffer::{StagedBuffer, ModifiableBuffer}; +/// Entry point name for shaders const ENTRY_NAME: &str = "main"; -const COLOR_RANGE: image::SubresourceRange = image::SubresourceRange { - aspects: format::Aspects::COLOR, - levels: 0..1, - layers: 0..1, + +/// Defines the colour range we use. +const COLOR_RANGE: hal::image::SubresourceRange = hal::image::SubresourceRange { + aspects: hal::format::Aspects::COLOR, + levels: 0..1, + layers: 0..1, }; +/// Source for vertex shader. TODO const VERTEX_SOURCE: &str = include_str!("./data/stockton.vert"); -const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag"); -const VERTEX_BUFFER_BATCH_SIZE: u64 = 10; -const VERTEX_BUFFER_INITIAL_BATCHES: u64 = 1; +/// Source for fragment shader. TODO +const FRAGMENT_SOURCE: &str = include_str!("./data/stockton.frag"); -/// Represents a triangle in 2D (screen) space. +/// Represents a point of a vertices, including U/V information. #[derive(Debug, Clone, Copy)] -pub struct Tri2 (pub [Vector2; 3]); - -/// Easy conversion to proper format. -impl From for [f32; 15] { - fn from(tri: Tri2) -> [f32; 15] { - [tri.0[0].x, tri.0[0].y, 1.0, 0.0, 0.0, - tri.0[1].x, tri.0[1].y, 0.0, 1.0, 0.0, - tri.0[2].x, tri.0[2].y, 0.0, 0.0, 1.0] - } -} - -const TRI2_SIZE_F32: usize = 15; -const TRI2_SIZE_BYTES: usize = size_of::() * TRI2_SIZE_F32; - -#[cfg(not(feature = "gl"))] -type Instance = back::Instance; - -#[cfg(feature = "gl")] -type Instance = (); +pub struct UVPoint (pub Vector2, pub Vector3); /// Contains all the hal related stuff. /// In the end, this takes some 3D points and puts it on the screen. // TODO: Settings for clear colour, buffer sizes, etc pub struct RenderingContext<'a> { - pub events_loop: winit::EventsLoop, - surface: ::Surface, + // Parents for most of these things + instance: ManuallyDrop, + device: ManuallyDrop, + + // Render destination + surface: ManuallyDrop, + swapchain: ManuallyDrop, + viewport: hal::pso::Viewport, + + imageviews: Vec, + framebuffers: Vec, + current_frame: usize, + frames_in_flight: usize, + + // Sync objects + // TODO: Collect these together? + get_image: Vec, + render_complete: Vec, + present_complete: Vec, + + // Pipeline + renderpass: ManuallyDrop, + descriptor_set_layouts: ManuallyDrop, + pipeline_layout: ManuallyDrop, + pipeline: ManuallyDrop, + + // Command pool and buffers + cmd_pool: ManuallyDrop, + cmd_buffers: Vec, + queue_group: QueueGroup, + + // Vertex and index buffers + // These are both staged + pub vert_buffer: ManuallyDrop>, + pub index_buffer: ManuallyDrop>, +} - pub (crate) instance: ManuallyDrop, - pub (crate) device: ManuallyDrop<::Device>, +impl<'a> RenderingContext<'a> { + /// Create a new RenderingContext for the given window. + pub fn new(window: &Window) -> Result { + // Create surface + let (instance, mut surface, mut adapters) = unsafe { + use hal::Instance; - swapchain: ManuallyDrop<::Swapchain>, - - viewport: pso::Viewport, + let instance = back::Instance::create("stockton", 1).map_err(|_| error::CreationError::WindowError)?; + let surface = instance.create_surface(window).map_err(|_| error::CreationError::WindowError)?; + let adapters = instance.enumerate_adapters(); - imageviews: Vec<::ImageView>, - framebuffers: Vec<::Framebuffer>, + (instance, surface, adapters) + }; - renderpass: ManuallyDrop<::RenderPass>, + // TODO: Properly figure out which adapter to use + let adapter = adapters.remove(0); + + // Device & Queue group + let (mut device, queue_group) = { + let family = adapter + .queue_families + .iter() + .find(|family| { + surface.supports_queue_family(family) && family.queue_type().supports_graphics() + }) + .unwrap(); + + let mut gpu = unsafe { + adapter + .physical_device + .open(&[(family, &[1.0])], hal::Features::empty()) + .unwrap() + }; - current_frame: usize, - // TODO: Collect these together - get_image: Vec<::Semaphore>, - render_complete: Vec<::Semaphore>, - present_complete: Vec<::Fence>, + (gpu.device, gpu.queue_groups.pop().unwrap()) + }; - frames_in_flight: usize, - cmd_pools: Vec::CommandPool>>, - cmd_buffers: Vec<::CommandBuffer>, - queue_group: QueueGroup, + // Swapchain + let (format, viewport, extent, swapchain, backbuffer) = { + use hal::{ + window::{PresentMode, CompositeAlphaMode}, + format::{Format, ChannelType}, + image::Usage, + pso::Viewport + }; - vert_buffer: ModifiableBuffer<'a>, - index_buffer: ModifiableBuffer<'a>, + // Figure out what the surface supports + let caps = surface.capabilities(&adapter.physical_device); + let formats = surface.supported_formats(&adapter.physical_device); + + // Find which settings we'll actually use based on preset preferences + 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 = { + [PresentMode::MAILBOX, PresentMode::FIFO, PresentMode::RELAXED, PresentMode::IMMEDIATE] + .iter() + .cloned() + .find(|pm| caps.present_modes.contains(*pm)) + .ok_or(error::CreationError::BadSurface)? + }; + let composite_alpha = { + [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::POSTMULTIPLIED] + .iter() + .cloned() + .find(|ca| caps.composite_alpha_modes.contains(*ca)) + .ok_or(error::CreationError::BadSurface)? + }; - descriptor_set_layouts: ::DescriptorSetLayout, - pipeline_layout: ManuallyDrop<::PipelineLayout>, - pipeline: ManuallyDrop<::GraphicsPipeline>, - pub (crate) adapter: adapter::Adapter -} + // Figure out properties for our swapchain + let extent = caps.extents.end(); // Size -impl<'a> RenderingContext<'a> { - /// Create a new RenderingContext for the given window. - pub fn new() -> Result { - let events_loop = EventsLoop::new(); - let wb = WindowBuilder::new(); - - // Create surface - #[cfg(not(feature = "gl"))] - let (window, instance, mut surface, mut adapters) = { - use hal::Instance; - let window = wb.build(&events_loop).map_err(|_| CreationError::WindowError)?; - let instance = back::Instance::create("stockton", 1); - let surface = instance.create_surface(&window); - let adapters = instance.enumerate_adapters(); - - (window, instance, surface, adapters) - }; - - #[cfg(feature = "gl")] - let (window, instance, mut surface, mut adapters) = { - use back::glutin::ContextBuilder; - - let glutin_window = ContextBuilder::new().with_vsync(true).build_windowed(wb, &events_loop).unwrap(); - let (glutin_context, glutin_window) = unsafe { - glutin_window.make_current().map_err(|_| CreationError::WindowError)? - .split() + // Number of frames to pre-render + 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 surface = back::Surface::from_context(glutin_context); - 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) = { - // TODO - let family = adapter - .queue_families - .iter() - .find(|family| { - surface.supports_queue_family(family) && family.queue_type().supports_graphics() - }) - .unwrap(); - - let mut gpu = unsafe { - adapter - .physical_device - .open(&[(family, &[1.0])], hal::Features::empty()) - .unwrap() - }; - - (gpu.queue_groups.pop().unwrap(), gpu.device) - }; - - // Swapchain stuff - let (format, viewport, extent, swapchain, backbuffer) = { - use hal::window::{PresentMode, CompositeAlphaMode}; - - 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 = { - [PresentMode::Mailbox, PresentMode::Fifo, PresentMode::Relaxed, PresentMode::Immediate] - .iter() - .cloned() - .find(|pm| present_modes.contains(pm)) - .ok_or(CreationError::BadSurface)? - }; - let composite_alpha = { - [CompositeAlphaMode::OPAQUE, CompositeAlphaMode::INHERIT, CompositeAlphaMode::PREMULTIPLIED, CompositeAlphaMode::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; // Don't support 3D + let image_usage = if caps.usage.contains(Usage::COLOR_ATTACHMENT) { + Usage::COLOR_ATTACHMENT + } else { + Err(error::CreationError::BadSurface)? }; - // Swap config - let swap_config = SwapchainConfig { - present_mode, - composite_alpha, - format, - extent: *extent, - image_count, - image_layers, - image_usage, + // Swap config + let swap_config = SwapchainConfig { + present_mode, + composite_alpha_mode: composite_alpha, + format, + extent: *extent, + image_count, + image_layers, + image_usage, }; - // 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) - }; + // Viewport + let extent = extent.to_extent(); + let viewport = 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| error::CreationError::SwapchainError (e))? + }; + + (format, viewport, extent, swapchain, backbuffer) + }; // Renderpass let renderpass = { - use hal::pass::*; - use hal::pso::PipelineStage; - use hal::image::{Access, Layout}; + use hal::{ + pass::*, + pso::PipelineStage, + image::{Access, Layout}, + memory::Dependencies + }; let attachment = Attachment { format: Some(format), @@ -265,127 +236,135 @@ impl<'a> RenderingContext<'a> { }; let dependency = SubpassDependency { - passes: None..0, + flags: Dependencies::empty(), + passes: None..Some(0), stages: PipelineStage::COLOR_ATTACHMENT_OUTPUT..PipelineStage::COLOR_ATTACHMENT_OUTPUT, accesses: Access::empty() - ..(Access::COLOR_ATTACHMENT_READ | Access::COLOR_ATTACHMENT_WRITE) + ..(Access::COLOR_ATTACHMENT_READ | Access::COLOR_ATTACHMENT_WRITE) }; unsafe { device.create_render_pass(&[attachment], &[subpass], &[dependency]) } - .map_err(|_| CreationError::OutOfMemoryError)? + .map_err(|_| error::CreationError::OutOfMemoryError)? }; // Subpass - let subpass = pass::Subpass { + let subpass = hal::pass::Subpass { index: 0, main_pass: &renderpass }; - // Vertex and index buffers - let (vert_buffer, index_buffer) = { - use hal::buffer::Usage; - ( - ModifiableBuffer::new(&mut device, &adapter, Usage::VERTEX | Usage::TRANSFER_DST), - ModifiableBuffer::new(&mut device, &adapter, Usage::TRANSFER_SRC) - ) - }; - - // 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]).allocate_one(hal::command::Level::Primary)); - 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 { - instance: ManuallyDrop::new(instance), - 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), - - vert_buffer, - index_buffer, - - adapter - }) + // Vertex and index buffers + let (vert_buffer, index_buffer) = { + use hal::buffer::Usage; + + let vert = StagedBuffer::new(&mut device, &adapter, Usage::VERTEX | Usage::TRANSFER_DST)?; + let index = StagedBuffer::new(&mut device, &adapter, Usage::TRANSFER_SRC)?; + + (vert, index) + }; + + // 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) = { + use hal::pool::CommandPoolCreateFlags; + use hal::command::Level; + + let mut cmd_pool = ManuallyDrop::new(unsafe { + device.create_command_pool(queue_group.family, CommandPoolCreateFlags::empty()) + }.map_err(|_| error::CreationError::OutOfMemoryError)?); + + 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 { + unsafe { + cmd_buffers.push(cmd_pool.allocate_one(Level::Primary)); // TODO: We can do this all at once outside the loop + } + + get_image.push(device.create_semaphore().map_err(|_| error::CreationError::SyncObjectError)?); + render_complete.push(device.create_semaphore().map_err(|_| error::CreationError::SyncObjectError)?); + present_complete.push(device.create_fence(true).map_err(|_| error::CreationError::SyncObjectError)?); + + unsafe { + use hal::image::ViewKind; + use hal::format::Swizzle; + + imageviews.push(device.create_image_view( + &backbuffer[i], + ViewKind::D2, + format, + Swizzle::NO, + COLOR_RANGE.clone(), + ).map_err(|e| error::CreationError::ImageViewError (e))?); + framebuffers.push(device.create_framebuffer( + &renderpass, + Some(&imageviews[i]), + extent + ).map_err(|_| error::CreationError::OutOfMemoryError)?); + } + } + + (cmd_pool, 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 { + instance: ManuallyDrop::new(instance), + surface: ManuallyDrop::new(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_pool, + cmd_buffers, + + descriptor_set_layouts: ManuallyDrop::new(descriptor_set_layouts), + pipeline_layout: ManuallyDrop::new(pipeline_layout), + pipeline: ManuallyDrop::new(pipeline), + + vert_buffer: ManuallyDrop::new(vert_buffer), + index_buffer: ManuallyDrop::new(index_buffer), + }) } #[allow(clippy::type_complexity)] - pub fn create_pipeline(device: &mut ::Device, extent: image::Extent, subpass: &pass::Subpass) -> Result< - ( - ::DescriptorSetLayout, - ::PipelineLayout, - ::GraphicsPipeline, - ), error::CreationError> { - use hal::pso::*; - - // Shader modules - let (vs_module, fs_module) = { + pub fn create_pipeline(device: &mut Device, extent: hal::image::Extent, subpass: &hal::pass::Subpass) -> Result< + ( + DescriptorSetLayout, + PipelineLayout, + GraphicsPipeline, + ), error::CreationError> { + use hal::pso::*; + use hal::format::Format; + + // 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) + .compile_into_spirv(VERTEX_SOURCE, shaderc::ShaderKind::Vertex, "vertex.vert", ENTRY_NAME, 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) + .compile_into_spirv(FRAGMENT_SOURCE, shaderc::ShaderKind::Fragment, "fragment.frag", ENTRY_NAME, None) .map_err(|e| error::CreationError::ShaderCError (e))?; // Make into shader module @@ -397,32 +376,32 @@ impl<'a> RenderingContext<'a> { .create_shader_module(fragment_compile_artifact.as_binary()) .map_err(|e| error::CreationError::ShaderModuleFailed (e))?) } - }; - - // 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 + }; + + // 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, @@ -445,17 +424,18 @@ impl<'a> RenderingContext<'a> { } }]; - // 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 + // Rasterizer + let rasterizer = Rasterizer { + polygon_mode: PolygonMode::Fill, + cull_face: Face::NONE, + front_face: FrontFace::Clockwise, + 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, @@ -463,19 +443,19 @@ impl<'a> RenderingContext<'a> { }; // 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), &[]) + 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 + // Colour blending let blender = { let blend_state = BlendState { color: BlendOp::Add { @@ -508,184 +488,211 @@ impl<'a> RenderingContext<'a> { // 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)) + // Pipeline description + let pipeline_desc = GraphicsPipelineDesc { + shaders, + rasterizer, + vertex_buffers, + blender, + depth_stencil, + multisampling: None, + baked_states, + layout: &layout, + subpass: *subpass, + flags: PipelineCreationFlags::empty(), + parent: 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)) } - /// 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 frame that's just cleared to the color specified. + pub fn draw_clear(&mut self, color: [f32; 4]) -> Result<(), error::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| error::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(|_| error::FrameError::SyncObjectError)?; + self.device + .reset_fence(present_complete) + .map_err(|_| error::FrameError::SyncObjectError)?; + }; + + // Record commands + unsafe { + use hal::command::{ClearValue, ClearColor, SubpassContents, CommandBufferFlags}; + + let buffer = &mut self.cmd_buffers[image_index]; + let clear_values = [ClearValue { + color: ClearColor { + float32: color + } + }]; + + buffer.begin_primary(CommandBufferFlags::EMPTY); + buffer.begin_render_pass( + &self.renderpass, + &self.framebuffers[image_index], + self.viewport.rect, + clear_values.iter(), + SubpassContents::Inline + ); + buffer.end_render_pass(); + buffer.finish(); + }; + + // Make submission object + let command_buffers = &self.cmd_buffers[image_index..=image_index]; + let wait_semaphores: ArrayVec<[_; 1]> = [(get_image, hal::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(|_| error::FrameError::PresentError)? + }; + + Ok(()) } pub fn draw_vertices(&mut self) -> 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")?; - }; - - // 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( + 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")?; + }; + + // Record commands + unsafe { + use hal::buffer::{IndexBufferView, SubRange}; + use hal::command::{SubpassContents, CommandBufferFlags, ClearValue, ClearColor}; + + let buffer = &mut self.cmd_buffers[image_index]; + let clear_values = [ClearValue { + color: ClearColor { + float32: [0.0, 0.0, 0.0, 1.0] + } + }]; + + // Commit from staging buffers + let (vbufs, ibuf) = { + let vbufref: &::Buffer = self.vert_buffer.commit( + &self.device, + &mut self.queue_group.queues[0], + &mut self.cmd_pool + ); + + let vbufs: ArrayVec<[_; 1]> = [(vbufref, SubRange::WHOLE)].into(); + let ibuf = self.index_buffer.commit( + &self.device, + &mut self.queue_group.queues[0], + &mut self.cmd_pool + ); + + (vbufs, ibuf) + }; + + buffer.begin_primary(CommandBufferFlags::EMPTY); + { + buffer.begin_render_pass( &self.renderpass, &self.framebuffers[image_index], self.viewport.rect, clear_values.iter(), + SubpassContents::Inline ); - encoder.bind_graphics_pipeline(&self.pipeline); - - // Here we must force the Deref impl of ManuallyDrop to play nice. - let buffer_ref: &::Buffer = &self.map_verts.buffer; - let buffers: ArrayVec<[_; 1]> = [(buffer_ref, 0)].into(); + buffer.bind_graphics_pipeline(&self.pipeline); + buffer.end_render_pass(); + buffer.bind_vertex_buffers(0, vbufs); - encoder.bind_vertex_buffers(0, buffers); + buffer.bind_index_buffer(IndexBufferView { + buffer: ibuf, + range: SubRange::WHOLE, + index_type: hal::IndexType::U16 + }); - trace!("Requesting draw of {:?} instances ({:?} verts)", self.map_verts.active_instances, self.map_verts.active_verts); - encoder.draw(self.map_verts.active_verts.clone(), self.map_verts.active_instances.clone()); + buffer.draw_indexed(0..6, 0, 0..1); } - 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(()) - } + buffer.finish(); + }; + + // Make submission object + let command_buffers = &self.cmd_buffers[image_index..=image_index]; + let wait_semaphores: ArrayVec<[_; 1]> = [(get_image, hal::pso::PipelineStage::COLOR_ATTACHMENT_OUTPUT)].into(); + let signal_semaphores: ArrayVec<[_; 1]> = [render_complete].into(); + + let present_wait_semaphores: ArrayVec<[_; 1]> = [render_complete].into(); - pub fn add_map_vert(&mut self, tri: Tri2) -> Result<(), ()> { - // get around the borrow checker + let submission = Submission { + command_buffers, + wait_semaphores, + signal_semaphores, + }; + + // Submit it + let command_queue = &mut self.queue_group.queues[0]; unsafe { - let ctx: *mut Self = &mut *self; - self.map_verts.add(tri, ctx.as_mut().unwrap()) - } + command_queue.submit(submission, Some(present_complete)); + self.swapchain + .present(command_queue, image_index as u32, present_wait_semaphores) + .map_err(|_| "FrameError::PresentError")? + }; + + Ok(()) } } @@ -695,36 +702,45 @@ impl<'a> core::ops::Drop for RenderingContext<'a> { self.device.wait_idle().unwrap(); 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); - } - - // self.map_verts.deactivate(self); - - use core::ptr::read; - for cmd_pool in self.cmd_pools.drain(..) { - self.device.destroy_command_pool( - ManuallyDrop::into_inner(cmd_pool), - ); - } - 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); + 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; + ManuallyDrop::into_inner(read(&self.vert_buffer)).deactivate(&mut self.device); + ManuallyDrop::into_inner(read(&self.index_buffer)).deactivate(&mut self.device); + + self.device.destroy_command_pool( + ManuallyDrop::into_inner(read(&self.cmd_pool)), + ); + + self.device + .destroy_render_pass(ManuallyDrop::into_inner(read(&self.renderpass))); + self.device + .destroy_swapchain(ManuallyDrop::into_inner(read(&self.swapchain))); + + self.device + .destroy_descriptor_set_layout(ManuallyDrop::into_inner(read(&self.descriptor_set_layouts))); + + self.device + .destroy_pipeline_layout(ManuallyDrop::into_inner(read(&self.pipeline_layout))); + + self.instance + .destroy_surface(ManuallyDrop::into_inner(read(&self.surface))); + + ManuallyDrop::drop(&mut self.device); } } } \ No newline at end of file diff --git a/stockton-render/src/draw/mod.rs b/stockton-render/src/draw/mod.rs index c506f7a..1b6db2a 100644 --- a/stockton-render/src/draw/mod.rs +++ b/stockton-render/src/draw/mod.rs @@ -19,4 +19,4 @@ mod context; mod buffer; pub use self::context::RenderingContext; -pub use self::context::Tri2; +pub use self::context::UVPoint; diff --git a/stockton-render/src/error.rs b/stockton-render/src/error.rs index 485d1e7..b029975 100644 --- a/stockton-render/src/error.rs +++ b/stockton-render/src/error.rs @@ -21,7 +21,6 @@ /// - Sanity - Things that probably aren't true, likely indicating a deeper issue. /// These aren't guaranteed sanity issues, but they are weird issues. /// - Runtime - Things caused by runtime conditions, usually resource constraints. -/// You can use the associated methods to get the group of one, which may be helpful for error reporting, etc. #[derive(Debug)] pub enum CreationError { WindowError, diff --git a/stockton-render/src/lib.rs b/stockton-render/src/lib.rs index e51a582..0222933 100644 --- a/stockton-render/src/lib.rs +++ b/stockton-render/src/lib.rs @@ -13,33 +13,11 @@ // You should have received a copy of the GNU General Public License along // with this program. If not, see . -//! Renders a world to a window. -//! -//! You'll need to pick a backend using features. You should only pick one. -//! On Linux & Windows, you should use vulkan. -//! On Mac, you should use `metal`. -//! If you're targetting machines without Vulkan, OpenGL or dx11/dx12 is preferred. -//! `empty` is used for testing -#![feature(manually_drop_take)] - extern crate core; -#[cfg(feature = "dx11")] -extern crate gfx_backend_dx11 as back; - -#[cfg(feature = "dx12")] -extern crate gfx_backend_dx12 as back; - -#[cfg(feature = "gl")] -extern crate gfx_backend_gl as back; - -#[cfg(feature = "metal")] -extern crate gfx_backend_metal as back; - #[cfg(feature = "vulkan")] extern crate gfx_backend_vulkan as back; -#[macro_use] extern crate log; extern crate gfx_hal as hal; extern crate stockton_types; @@ -48,18 +26,20 @@ extern crate winit; extern crate arrayvec; -mod error; pub mod draw; +mod error; +mod types; -use error::{CreationError, FrameError}; -use draw::{RenderingContext, Tri2}; +use std::sync::{Arc, RwLock}; -use stockton_types::{World, Vector2}; +use stockton_types::World; -use std::sync::{Arc, RwLock}; +use error::{CreationError, FrameError}; +use draw::RenderingContext; +/// Renders a world to a window when you tell it to. pub struct Renderer<'a> { - world: Arc>>, + _world: Arc>>, pub context: RenderingContext<'a> } @@ -67,11 +47,11 @@ pub struct Renderer<'a> { impl<'a> Renderer<'a> { /// Create a new Renderer. /// This initialises all the vulkan context, etc needed. - pub fn new(world: Arc>>) -> Result { - let context = RenderingContext::new()?; + pub fn new(world: Arc>>, window: &winit::window::Window) -> Result { + let context = RenderingContext::new(window)?; Ok(Renderer { - world, context + _world: world, context }) } diff --git a/stockton-render/src/types.rs b/stockton-render/src/types.rs new file mode 100644 index 0000000..297eb69 --- /dev/null +++ b/stockton-render/src/types.rs @@ -0,0 +1,36 @@ +// 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 . + +//! Convenience module to reference types that are stored in the backend's enum + +pub type Device = ::Device; +pub type Buffer = ::Buffer; +pub type Memory = ::Memory; +pub type Swapchain = ::Swapchain; +pub type Surface = ::Surface; +pub type Semaphore = ::Semaphore; +pub type Fence = ::Fence; +pub type CommandPool = ::CommandPool; +pub type CommandBuffer = ::CommandBuffer; +pub type CommandQueue = ::CommandQueue; +pub type DescriptorSetLayout = ::DescriptorSetLayout; +pub type PipelineLayout = ::PipelineLayout; +pub type GraphicsPipeline = ::GraphicsPipeline; +pub type ImageView = ::ImageView; +pub type Framebuffer = ::Framebuffer; +pub type RenderPass = ::RenderPass; + +pub type Adapter = hal::adapter::Adapter; +pub type QueueGroup = hal::queue::QueueGroup; \ No newline at end of file diff --git a/stockton-types/src/entity_store.rs b/stockton-types/src/entity_store.rs index 375ae74..ae2db82 100644 --- a/stockton-types/src/entity_store.rs +++ b/stockton-types/src/entity_store.rs @@ -54,7 +54,7 @@ impl EntityStore { /// # Returns /// The name & index of the added entity if successful. /// If an entity already exists with the given name, NameConflict is returned. - pub fn add(&mut self, entity: Box, name: String) -> Result { + pub fn add(&mut self, entity: Box, name: String) -> Result { if self.name_to_index.contains_key(&name) { return Err(NameConflict) } @@ -67,7 +67,7 @@ impl EntityStore { /// Remove the entity with the given index, returning it. /// /// Takes O(2n - i) time. - pub fn remove_by_index(&mut self, index: usize) -> Option> { + pub fn remove_by_index(&mut self, index: usize) -> Option> { if index >= self.entities.len() { return None; } @@ -78,7 +78,7 @@ impl EntityStore { /// Removes the entity with the given name, returning it. /// /// Takes O(2n - i) time. - pub fn remove_by_name(&mut self, name: &str) -> Option> { + pub fn remove_by_name(&mut self, name: &str) -> Option> { let mut index: usize = self.entities.len(); self.name_to_index.retain(|k,v| { @@ -99,7 +99,7 @@ impl EntityStore { /// Make a new EntityStore from a list of entities & names. /// /// Returns None in case of name conflicts in list. - pub fn from_entities(entities: Vec<(Box, String)>) -> Option { + pub fn from_entities(entities: Vec<(Box, String)>) -> Option { let mut store = EntityStore { entities: Vec::with_capacity(entities.len()), name_to_index: HashMap::with_capacity(entities.len()) @@ -118,7 +118,7 @@ impl EntityStore { /// Indexes the EntityStore for a specific index. /// If you want to target an entity for longer than one tick, store its name, not an index. impl Index for EntityStore { - type Output = Entity; + type Output = dyn Entity; fn index(&self, index: usize) -> &Self::Output { self.entities[index].as_ref() } @@ -127,7 +127,7 @@ impl Index for EntityStore { /// Indexes the EntityStore for a specific name. /// This is what you should use if you plan to target an entity for more than one tick. impl Index<&str> for EntityStore { - type Output = Entity; + type Output = dyn Entity; fn index(&self, index: &str) -> &Self::Output { self.entities[self.name_to_index[index]].as_ref() } diff --git a/stockton-types/src/world.rs b/stockton-types/src/world.rs index 1196592..e244aa7 100644 --- a/stockton-types/src/world.rs +++ b/stockton-types/src/world.rs @@ -35,9 +35,9 @@ impl<'a> World<'a> { /// /// `mapper` is called for each BSPEntity to map it to a concrete rust type. pub fn new(bsp: Pin>>, mut mapper: F) -> Option> - where F: FnMut(&BSPEntity) -> Option<(Box, String)> { + where F: FnMut(&BSPEntity) -> Option<(Box, String)> { - let mut entities: Vec<(Box, String)> = Vec::with_capacity(bsp.entities.entities.len()); + let mut entities: Vec<(Box, String)> = Vec::with_capacity(bsp.entities.entities.len()); for bsp_ent in bsp.entities.entities.iter() { if let Some(result) = mapper(&bsp_ent) { entities.push(result); -- cgit v1.2.3