aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:20 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:20 +0100
commitb31035bf03e0843fb516bee065f9a010424ba546 (patch)
treeb98e55478f47da33e6f1faa994da6b91b97b9ad2
parenta748e4c3e3615601ee2afe6d347ef439d8ff50fa (diff)
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
-rw-r--r--Cargo.toml2
-rw-r--r--README.md20
-rw-r--r--examples/render-quad/Cargo.toml (renamed from examples/render-triangles/Cargo.toml)2
-rw-r--r--examples/render-quad/README.md3
-rw-r--r--examples/render-quad/src/main.rs87
-rw-r--r--examples/render-triangles/README.md3
-rw-r--r--examples/render-triangles/src/main.rs80
-rw-r--r--stockton-render/Cargo.toml3
-rw-r--r--stockton-render/src/draw/buffer.rs158
-rw-r--r--stockton-render/src/draw/context.rs1092
-rw-r--r--stockton-render/src/draw/mod.rs2
-rw-r--r--stockton-render/src/error.rs1
-rw-r--r--stockton-render/src/lib.rs42
-rw-r--r--stockton-render/src/types.rs36
-rw-r--r--stockton-types/src/entity_store.rs12
-rw-r--r--stockton-types/src/world.rs4
16 files changed, 815 insertions, 732 deletions
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-triangles/Cargo.toml b/examples/render-quad/Cargo.toml
index 0b47393..6f75543 100644
--- a/examples/render-triangles/Cargo.toml
+++ b/examples/render-quad/Cargo.toml
@@ -7,7 +7,7 @@ authors = ["Oscar <oscar.shrimpton.personal@gmail.com>"]
stockton-render = { path = "../../stockton-render", features = ["vulkan"] }
stockton-types = { path = "../../stockton-types" }
stockton-bsp = "2.0.0"
-winit = "0.19.1"
+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 <http://www.gnu.org/licenses/>.
+
+//! 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/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 <http://www.gnu.org/licenses/>.
-
-//! 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 <oscar.shrimpton.personal@gmail.com>"]
[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 <http://www.gnu.org/licenses/>.
-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 <back::Backend as hal::Backend>::Device,
- adapter: &Adapter<back::Backend>,
+fn create_buffer(device: &mut Device,
+ adapter: &Adapter,
usage: Usage,
- properties: Properties) -> Result<(<back::Backend as hal::Backend>::Buffer, <back::Backend as hal::Backend>::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 <back::Backend as hal::Backend>::Device,
))
}
-trait ModifiableBuffer: IndexMut<usize> {
- fn commit<'a>(&'a self) -> &'a <back::Backend as hal::Backend>::Buffer;
+pub trait ModifiableBuffer: IndexMut<usize> {
+ fn commit<'a>(&'a mut self, device: &Device,
+ command_queue: &mut CommandQueue,
+ command_pool: &mut CommandPool) -> &'a Buffer;
}
-pub struct StagedBuffer<'a> {
- stagedBuffer: ManuallyDrop<<back::Backend as hal::Backend>::Buffer>,
- stagedMemory: ManuallyDrop<<back::Backend as hal::Backend>::Memory>,
- buffer: ManuallyDrop<<back::Backend as hal::Backend>::Buffer>,
- memory: ManuallyDrop<<back::Backend as hal::Backend>::Memory>,
- mappedStaged: &'a mut [f32],
- stagedIsDirty: bool
+pub struct StagedBuffer<'a, T: Sized> {
+ staged_buffer: ManuallyDrop<Buffer>,
+ staged_memory: ManuallyDrop<Memory>,
+ buffer: ManuallyDrop<Buffer>,
+ memory: ManuallyDrop<Memory>,
+ staged_mapped_memory: &'a mut [T],
+ staged_is_dirty: bool,
}
-impl<'a> ModifiableBuffer for StagedBuffer<'a> {
- fn new(device: &mut <back::Backend as hal::Backend>::Device, adapter: &Adapter<back::Backend>, usage: Usage) -> Result<Self, CreationError> {
+impl<'a, T: Sized> StagedBuffer<'a, T> {
+ pub fn new(device: &mut Device, adapter: &Adapter, usage: Usage) -> Result<Self, CreationError> {
- 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::<T>() 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(&copy_finished));
+
+ device
+ .wait_for_fence(&copy_finished, core::u64::MAX).unwrap();
+ device.destroy_fence(copy_finished);
+ }
+
+ self.staged_is_dirty = false;
+ }
+
+ &self.buffer
+ }
}
-impl<'a> Index<usize> for StagedBuffer<'a> {
- type Output = f32;
+impl<'a, T: Sized> Index<usize> 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<usize> for StagedBuffer<'a> {
+impl<'a, T: Sized> IndexMut<usize> 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 <back::Backend as hal::Backend>::Device, adapter: &Adapter<back::Backend>) -> Result<Self, CreationError> {
+// pub fn new(device: &mut Device, adapter: &Adapter<back::Backend>) -> Result<Self, CreationError> {
// }
// pub(crate) struct VertexLump<T: Into<X>, X: Pod> {
-// pub (crate) buffer: ManuallyDrop<<back::Backend as hal::Backend>::Buffer>,
-// memory: ManuallyDrop<<back::Backend as hal::Backend>::Memory>,
+// pub (crate) buffer: ManuallyDrop<Buffer>,
+// memory: ManuallyDrop<Memory>,
// requirements: Requirements,
// unit_size_bytes: u64,
@@ -141,7 +208,7 @@ impl<'a> IndexMut<usize> for StagedBuffer<'a> {
// const BATCH_SIZE: u64 = 3;
// impl<T: Into<X>, X: Pod> VertexLump<T, X> {
-// pub fn new(device: &mut <back::Backend as hal::Backend>::Device, adapter: &Adapter<back::Backend>) -> Result<VertexLump<T, X>, CreationError> {
+// pub fn new(device: &mut Device, adapter: &Adapter<back::Backend>) -> Result<VertexLump<T, X>, CreationError> {
// let unit_size_bytes = size_of::<X>() as u64;
// let unit_size_verts = unit_size_bytes / size_of::<f32>() as u64;
@@ -286,10 +353,5 @@ impl<'a> IndexMut<usize> 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 <http://www.gnu.org/licenses/>.
//! 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<Tri2> 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::<f32>() * 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: <back::Backend as hal::Backend>::Surface,
+ // Parents for most of these things
+ instance: ManuallyDrop<back::Instance>,
+ device: ManuallyDrop<Device>,
+
+ // Render destination
+ surface: ManuallyDrop<Surface>,
+ swapchain: ManuallyDrop<Swapchain>,
+ viewport: hal::pso::Viewport,
+
+ imageviews: Vec<ImageView>,
+ framebuffers: Vec<Framebuffer>,
+ current_frame: usize,
+ frames_in_flight: usize,
+
+ // Sync objects
+ // TODO: Collect these together?
+ get_image: Vec<Semaphore>,
+ render_complete: Vec<Semaphore>,
+ present_complete: Vec<Fence>,
+
+ // Pipeline
+ renderpass: ManuallyDrop<RenderPass>,
+ descriptor_set_layouts: ManuallyDrop<DescriptorSetLayout>,
+ pipeline_layout: ManuallyDrop<PipelineLayout>,
+ pipeline: ManuallyDrop<GraphicsPipeline>,
+
+ // Command pool and buffers
+ cmd_pool: ManuallyDrop<CommandPool>,
+ cmd_buffers: Vec<CommandBuffer>,
+ queue_group: QueueGroup,
+
+ // Vertex and index buffers
+ // These are both staged
+ pub vert_buffer: ManuallyDrop<StagedBuffer<'a, UVPoint>>,
+ pub index_buffer: ManuallyDrop<StagedBuffer<'a, (u16, u16, u16)>>,
+}
- pub (crate) instance: ManuallyDrop<Instance>,
- pub (crate) device: ManuallyDrop<<back::Backend as hal::Backend>::Device>,
+impl<'a> RenderingContext<'a> {
+ /// Create a new RenderingContext for the given window.
+ pub fn new(window: &Window) -> Result<Self, error::CreationError> {
+ // Create surface
+ let (instance, mut surface, mut adapters) = unsafe {
+ use hal::Instance;
- swapchain: ManuallyDrop<<back::Backend as hal::Backend>::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<<back::Backend as hal::Backend>::ImageView>,
- framebuffers: Vec<<back::Backend as hal::Backend>::Framebuffer>,
+ (instance, surface, adapters)
+ };
- renderpass: ManuallyDrop<<back::Backend as hal::Backend>::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<<back::Backend as hal::Backend>::Semaphore>,
- render_complete: Vec<<back::Backend as hal::Backend>::Semaphore>,
- present_complete: Vec<<back::Backend as hal::Backend>::Fence>,
+ (gpu.device, gpu.queue_groups.pop().unwrap())
+ };
- frames_in_flight: usize,
- cmd_pools: Vec<ManuallyDrop<<back::Backend as hal::Backend>::CommandPool>>,
- cmd_buffers: Vec<<back::Backend as hal::Backend>::CommandBuffer>,
- queue_group: QueueGroup<back::Backend>,
+ // 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: <back::Backend as hal::Backend>::DescriptorSetLayout,
- pipeline_layout: ManuallyDrop<<back::Backend as hal::Backend>::PipelineLayout>,
- pipeline: ManuallyDrop<<back::Backend as hal::Backend>::GraphicsPipeline>,
- pub (crate) adapter: adapter::Adapter<back::Backend>
-}
+ // 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<Self, CreationError> {
- 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 <back::Backend as hal::Backend>::Device, extent: image::Extent, subpass: &pass::Subpass<back::Backend>) -> Result<
- (
- <back::Backend as hal::Backend>::DescriptorSetLayout,
- <back::Backend as hal::Backend>::PipelineLayout,
- <back::Backend as hal::Backend>::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<back::Backend>) -> 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::<back::Backend> {
- entry: ENTRY_NAME,
- module: &vs_module,
- specialization: Specialization::default()
- },
- EntryPoint::<back::Backend> {
- entry: ENTRY_NAME,
- module: &fs_module,
- specialization: Specialization::default()
- }
- );
-
- // Shader set
- let shaders = GraphicsShaderSet {
- vertex: vs_entry,
- fragment: Some(fs_entry),
- hull: None,
- domain: None,
- geometry: None
- };
-
- // Vertex buffers
+ };
+
+ // Shader entry points (ShaderStage)
+ let (vs_entry, fs_entry) = (
+ EntryPoint::<back::Backend> {
+ entry: ENTRY_NAME,
+ module: &vs_module,
+ specialization: Specialization::default()
+ },
+ EntryPoint::<back::Backend> {
+ entry: ENTRY_NAME,
+ module: &fs_module,
+ specialization: Specialization::default()
+ }
+ );
+
+ // Shader set
+ let shaders = GraphicsShaderSet {
+ vertex: vs_entry,
+ fragment: Some(fs_entry),
+ hull: None,
+ domain: None,
+ geometry: None
+ };
+
+ // Vertex buffers
let vertex_buffers: Vec<VertexBufferDesc> = vec![VertexBufferDesc {
binding: 0,
stride: (size_of::<f32>() * 5) as u32,
@@ -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: &<back::Backend as hal::Backend>::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: &<back::Backend as hal::Backend>::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 <http://www.gnu.org/licenses/>.
-//! 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<RwLock<World<'a>>>,
+ _world: Arc<RwLock<World<'a>>>,
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<RwLock<World<'a>>>) -> Result<Self, CreationError> {
- let context = RenderingContext::new()?;
+ pub fn new(world: Arc<RwLock<World<'a>>>, window: &winit::window::Window) -> Result<Self, CreationError> {
+ 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 <http://www.gnu.org/licenses/>.
+
+//! Convenience module to reference types that are stored in the backend's enum
+
+pub type Device = <back::Backend as hal::Backend>::Device;
+pub type Buffer = <back::Backend as hal::Backend>::Buffer;
+pub type Memory = <back::Backend as hal::Backend>::Memory;
+pub type Swapchain = <back::Backend as hal::Backend>::Swapchain;
+pub type Surface = <back::Backend as hal::Backend>::Surface;
+pub type Semaphore = <back::Backend as hal::Backend>::Semaphore;
+pub type Fence = <back::Backend as hal::Backend>::Fence;
+pub type CommandPool = <back::Backend as hal::Backend>::CommandPool;
+pub type CommandBuffer = <back::Backend as hal::Backend>::CommandBuffer;
+pub type CommandQueue = <back::Backend as hal::Backend>::CommandQueue;
+pub type DescriptorSetLayout = <back::Backend as hal::Backend>::DescriptorSetLayout;
+pub type PipelineLayout = <back::Backend as hal::Backend>::PipelineLayout;
+pub type GraphicsPipeline = <back::Backend as hal::Backend>::GraphicsPipeline;
+pub type ImageView = <back::Backend as hal::Backend>::ImageView;
+pub type Framebuffer = <back::Backend as hal::Backend>::Framebuffer;
+pub type RenderPass = <back::Backend as hal::Backend>::RenderPass;
+
+pub type Adapter = hal::adapter::Adapter<back::Backend>;
+pub type QueueGroup = hal::queue::QueueGroup<back::Backend>; \ 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<Entity>, name: String) -> Result<usize, NameConflict> {
+ pub fn add(&mut self, entity: Box<dyn Entity>, name: String) -> Result<usize, NameConflict> {
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<Box<Entity>> {
+ pub fn remove_by_index(&mut self, index: usize) -> Option<Box<dyn Entity>> {
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<Box<Entity>> {
+ pub fn remove_by_name(&mut self, name: &str) -> Option<Box<dyn Entity>> {
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<Entity>, String)>) -> Option<EntityStore> {
+ pub fn from_entities(entities: Vec<(Box<dyn Entity>, String)>) -> Option<EntityStore> {
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<usize> 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<usize> 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<F>(bsp: Pin<Box<BSPFile<'a>>>, mut mapper: F) -> Option<World<'a>>
- where F: FnMut(&BSPEntity) -> Option<(Box<Entity>, String)> {
+ where F: FnMut(&BSPEntity) -> Option<(Box<dyn Entity>, String)> {
- let mut entities: Vec<(Box<Entity>, String)> = Vec::with_capacity(bsp.entities.entities.len());
+ let mut entities: Vec<(Box<dyn Entity>, 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);